From bd30825c4e6164d01e705255807b541c327c42f9 Mon Sep 17 00:00:00 2001 From: Douglas DUTEIL Date: Mon, 7 Oct 2024 12:52:08 +0200 Subject: [PATCH] refactor(moderation): move actions ui components --- bun.lockb | Bin 466714 -> 467426 bytes hyyypertool.code-workspace | 15 +- packages/~/app/core/package.json | 21 +-- packages/~/app/core/src/types/index.ts | 3 + packages/~/app/ui/src/testing/index.ts | 45 +++-- packages/~/app/urls/package.json | 3 +- packages/~/app/urls/src/hx_urls.ts | 6 +- packages/~/moderations/api/package.json | 1 + .../api/src/:id/$procedures/rejected.ts | 8 +- .../src/:id/$procedures/validate.e2e.test.ts | 13 +- .../api/src/:id/$procedures/validate.ts | 13 +- .../~/moderations/api/src/:id/Actions.tsx | 50 ----- .../api/src/:id/Desicison_Context.tsx | 20 -- .../moderations/api/src/:id/Member_Valid.tsx | 174 ------------------ .../src/:id/Organizations_Of_User_Table.tsx | 40 ---- packages/~/moderations/api/src/:id/context.ts | 7 - packages/~/moderations/api/src/:id/page.tsx | 58 ++++-- .../already_signed.test.tsx.snap | 38 ---- .../src/:id/responses/already_signed.test.tsx | 111 ----------- .../~/moderations/lib/src/context/rejected.ts | 11 +- .../lib/src/entities/Moderation.ts | 7 + .../lib/src/schema/rejected.form.ts | 12 ++ .../lib/src/schema/validate.form.ts | 12 ++ .../lib/src/usecase/GetModerationHeader.ts | 1 + .../lib/src/usecase/IsUserExternalMember.ts | 35 ++++ .../usecase/send_rejected_message_to_user.ts | 6 +- .../~/moderations/ui/src/Actions/Actions.tsx | 66 +++++++ .../ui/src/Actions/AddAsMemberExternal.tsx | 34 ++++ .../ui/src/Actions/AddAsMemberInternal.tsx | 30 +++ .../moderations/ui/src/Actions/AddDomain.tsx | 23 +++ .../src/:id => ui/src/Actions}/Desicison.tsx | 4 +- .../src/Actions/MemberInvalid.tsx} | 83 +-------- .../ui/src/Actions/MemberValid.tsx | 75 ++++++++ .../src/Actions/ResponseMessageSelector.tsx | 30 +++ .../ui/src/Actions/SendNotification.tsx | 37 ++++ .../~/moderations/ui/src/Actions/context.ts | 42 +++++ .../~/moderations/ui/src/Actions/index.ts | 3 + .../already_signed.test.tsx.snap | 21 +++ .../src/Actions}/responses/accountant.tsx | 11 +- .../Actions/responses/already_signed.test.tsx | 124 +++++++++++++ .../src/Actions}/responses/already_signed.tsx | 20 +- .../Actions}/responses/chorus_pro_error.tsx | 11 +- .../src/Actions}/responses/contractors.tsx | 11 +- .../Actions}/responses/first_last_name.tsx | 11 +- .../ui/src/Actions/responses/index.ts | 31 ++++ .../src/Actions}/responses/invalid_job.tsx | 11 +- .../Actions}/responses/invalid_name_job.tsx | 0 .../responses/link_with_eduction_gouv_fr.tsx | 13 +- .../responses/link_with_organization.tsx | 11 +- .../src/Actions}/responses/mobilic.tsx | 11 +- .../Actions}/responses/use_official_email.tsx | 11 +- .../src/Actions}/responses/use_pro_email.tsx | 11 +- .../DomainsByOrganization.tsx} | 31 ++-- .../ui/src/DomainsByOrganization/index.ts | 3 + .../moderations/ui/src/Header/Header.test.tsx | 5 +- .../~/moderations/ui/src/Header/Header.tsx | 46 +++-- .../ui/src/MessageInfo/MessageInfo.tsx | 5 +- .../~/moderations/ui/src/MessageInfo/index.ts | 2 +- .../OrganizationsByUser.tsx | 57 ++++++ .../ui/src/OrganizationsByUser/index.ts | 3 + .../UsersByOrganization.tsx} | 17 +- .../ui/src/UsersByOrganization/index.ts | 3 + .../src/count_organization_members.test.ts | 72 ++++++++ .../src/count_organization_members.ts | 22 +++ .../organizations/ui/src/info/About.test.tsx | 4 +- .../users/api/src/:id/organizations/Table.tsx | 9 +- .../api/src/:id/organizations/context.tsx | 12 +- .../users/api/src/:id/organizations/index.tsx | 6 +- .../src/usecase/CountUserMemberships.test.ts | 51 +++++ .../lib/src/usecase/CountUserMemberships.ts | 28 +++ .../~/users/lib/src/usecase/GetUserInfo.ts | 31 ++++ .../src/usecase/SuggestSameUserEmails.test.ts | 74 ++++++++ .../lib/src/usecase/SuggestSameUserEmails.ts | 57 ++++++ packages/~/users/ui/package.json | 32 ++++ .../ui/src/About/About.tsx} | 66 +------ packages/~/users/ui/src/About/index.ts | 3 + .../ui/src/Investigation/Investigation.tsx | 55 ++++++ .../~/users/ui/src/Investigation/index.ts | 3 + packages/~/users/ui/tsconfig.json | 19 ++ tailwind.config.ts | 6 +- tsconfig.json | 2 +- 81 files changed, 1373 insertions(+), 796 deletions(-) create mode 100644 packages/~/app/core/src/types/index.ts delete mode 100644 packages/~/moderations/api/src/:id/Actions.tsx delete mode 100644 packages/~/moderations/api/src/:id/Desicison_Context.tsx delete mode 100644 packages/~/moderations/api/src/:id/Member_Valid.tsx delete mode 100644 packages/~/moderations/api/src/:id/Organizations_Of_User_Table.tsx delete mode 100644 packages/~/moderations/api/src/:id/responses/__snapshots__/already_signed.test.tsx.snap delete mode 100644 packages/~/moderations/api/src/:id/responses/already_signed.test.tsx create mode 100644 packages/~/moderations/lib/src/entities/Moderation.ts create mode 100644 packages/~/moderations/lib/src/schema/rejected.form.ts create mode 100644 packages/~/moderations/lib/src/schema/validate.form.ts create mode 100644 packages/~/moderations/lib/src/usecase/IsUserExternalMember.ts create mode 100644 packages/~/moderations/ui/src/Actions/Actions.tsx create mode 100644 packages/~/moderations/ui/src/Actions/AddAsMemberExternal.tsx create mode 100644 packages/~/moderations/ui/src/Actions/AddAsMemberInternal.tsx create mode 100644 packages/~/moderations/ui/src/Actions/AddDomain.tsx rename packages/~/moderations/{api/src/:id => ui/src/Actions}/Desicison.tsx (88%) rename packages/~/moderations/{api/src/:id/Member_Invalid.tsx => ui/src/Actions/MemberInvalid.tsx} (62%) create mode 100644 packages/~/moderations/ui/src/Actions/MemberValid.tsx create mode 100644 packages/~/moderations/ui/src/Actions/ResponseMessageSelector.tsx create mode 100644 packages/~/moderations/ui/src/Actions/SendNotification.tsx create mode 100644 packages/~/moderations/ui/src/Actions/context.ts create mode 100644 packages/~/moderations/ui/src/Actions/index.ts create mode 100644 packages/~/moderations/ui/src/Actions/responses/__snapshots__/already_signed.test.tsx.snap rename packages/~/moderations/{api/src/:id => ui/src/Actions}/responses/accountant.tsx (76%) create mode 100644 packages/~/moderations/ui/src/Actions/responses/already_signed.test.tsx rename packages/~/moderations/{api/src/:id => ui/src/Actions}/responses/already_signed.tsx (55%) rename packages/~/moderations/{api/src/:id => ui/src/Actions}/responses/chorus_pro_error.tsx (82%) rename packages/~/moderations/{api/src/:id => ui/src/Actions}/responses/contractors.tsx (79%) rename packages/~/moderations/{api/src/:id => ui/src/Actions}/responses/first_last_name.tsx (82%) create mode 100644 packages/~/moderations/ui/src/Actions/responses/index.ts rename packages/~/moderations/{api/src/:id => ui/src/Actions}/responses/invalid_job.tsx (79%) rename packages/~/moderations/{api/src/:id => ui/src/Actions}/responses/invalid_name_job.tsx (100%) rename packages/~/moderations/{api/src/:id => ui/src/Actions}/responses/link_with_eduction_gouv_fr.tsx (83%) rename packages/~/moderations/{api/src/:id => ui/src/Actions}/responses/link_with_organization.tsx (77%) rename packages/~/moderations/{api/src/:id => ui/src/Actions}/responses/mobilic.tsx (76%) rename packages/~/moderations/{api/src/:id => ui/src/Actions}/responses/use_official_email.tsx (77%) rename packages/~/moderations/{api/src/:id => ui/src/Actions}/responses/use_pro_email.tsx (76%) rename packages/~/moderations/{api/src/:id/Domain_Organization.tsx => ui/src/DomainsByOrganization/DomainsByOrganization.tsx} (65%) create mode 100644 packages/~/moderations/ui/src/DomainsByOrganization/index.ts create mode 100644 packages/~/moderations/ui/src/OrganizationsByUser/OrganizationsByUser.tsx create mode 100644 packages/~/moderations/ui/src/OrganizationsByUser/index.ts rename packages/~/moderations/{api/src/:id/Members_Of_Organization_Table.tsx => ui/src/UsersByOrganization/UsersByOrganization.tsx} (79%) create mode 100644 packages/~/moderations/ui/src/UsersByOrganization/index.ts create mode 100644 packages/~/organizations/repository/src/count_organization_members.test.ts create mode 100644 packages/~/organizations/repository/src/count_organization_members.ts create mode 100644 packages/~/users/lib/src/usecase/CountUserMemberships.test.ts create mode 100644 packages/~/users/lib/src/usecase/CountUserMemberships.ts create mode 100644 packages/~/users/lib/src/usecase/GetUserInfo.ts create mode 100644 packages/~/users/lib/src/usecase/SuggestSameUserEmails.test.ts create mode 100644 packages/~/users/lib/src/usecase/SuggestSameUserEmails.ts create mode 100644 packages/~/users/ui/package.json rename packages/~/{moderations/api/src/:id/About_User.tsx => users/ui/src/About/About.tsx} (51%) create mode 100644 packages/~/users/ui/src/About/index.ts create mode 100644 packages/~/users/ui/src/Investigation/Investigation.tsx create mode 100644 packages/~/users/ui/src/Investigation/index.ts create mode 100644 packages/~/users/ui/tsconfig.json diff --git a/bun.lockb b/bun.lockb index 322ddc86e90bbaab6a0d0693dd83bad6e80cd292..9a3a77d1f433b3be961ef563513b712974b9d6c2 100755 GIT binary patch delta 74650 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 z z2&5y=kN$`?OukRyya!p2+>6|Y=vDEzA$o^?HgXO!3&}xdBY8+ZQh?ly=x}oal8W>~ zdL!vbA4G>2z3*)RG7!-UL+lz?a*Q0m%=*2&J zsnBvn_ZoF?aSNh1Yw11wOX2k{qD%C~B%MhvLoP>_APbQTk&BSqiJ?! zL5>b?^Wdm~=&YibDE^EbLUbn46YvL-kCBg%bSzH$!5fjA5S@F^LUdM{fJ{VAN6tXb zMD!~4>ySaX2P1uuhtTyl^4pLSon00p7b5!k>IFzXG7XuIj6^0Nosn)xcccf>6Vc{( z1)?`@-iqj#8)qQnkh75S$l1sQWD=5rWFp;=?nrHIwk`P#eF8CSBmMKV||d8NGqfT@+HwfNAyOww-D{{cOVXB z(7y9w+z%o9Nb6?sMx+?`GH?#k59xvQM2^#>-xkLlx~K}6g#1P;sf-DTegyFe@+q

awVK~l3 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 cdBO98i-~!u0m{qC, - ): Promise => { - const reader = stream.getReader(); - let str = ""; - for (;;) { - const { done, value } = await reader.read(); - if (done) { - break; +export const render_html = PrettyRenderer({ parser: "html" }); +export const render_md = PrettyRenderer({ parser: "mdx" }); + +// + +export function PrettyRenderer(options: Options) { + return async function render_formated(element: Child) { + const textDecoder = new TextDecoder(); + const getStringFromStream = async ( + stream: ReadableStream, + ): Promise => { + const reader = stream.getReader(); + let str = ""; + for (;;) { + const { done, value } = await reader.read(); + if (done) { + break; + } + str += textDecoder.decode(value); } - str += textDecoder.decode(value); - } - return str; + return str; + }; + return format( + await getStringFromStream(await renderToReadableStream(element)), + options, + ); }; - return format( - await getStringFromStream(await renderToReadableStream(element)), - { parser: "html" }, - ); } diff --git a/packages/~/app/urls/package.json b/packages/~/app/urls/package.json index 849c0cc3..dd690010 100644 --- a/packages/~/app/urls/package.json +++ b/packages/~/app/urls/package.json @@ -16,8 +16,7 @@ "dependencies": { "consola": "3.2.3", "hono": "4.6.3", - "static-path": "0.0.4", - "type-fest": "4.26.1" + "static-path": "0.0.4" }, "devDependencies": { "@~/config.typescript": "workspace:*" diff --git a/packages/~/app/urls/src/hx_urls.ts b/packages/~/app/urls/src/hx_urls.ts index 5638d368..b29a62d6 100644 --- a/packages/~/app/urls/src/hx_urls.ts +++ b/packages/~/app/urls/src/hx_urls.ts @@ -1,10 +1,10 @@ // +import type { SetOptional } from "@~/app.core/types"; import type { Hono, HonoRequest, Schema } from "hono"; import { hc } from "hono/client"; import type { Endpoint } from "hono/types"; import type { HasRequiredKeys, UnionToIntersection } from "hono/utils/types"; -import type { SetOptional } from "type-fest"; import type { Router } from "./pattern"; // @@ -53,11 +53,11 @@ type HxClientRequest = { ? <$Args extends SetOptional<$Input, any>>( args: $Args, options?: HxClientRequestOptions, - ) => HtmxSpecifiedAttributes + ) => Promise> : <$Args extends SetOptional<$Input, any>>( args?: $Args, options?: HxClientRequestOptions, - ) => HtmxSpecifiedAttributes + ) => Promise> : never : never : never diff --git a/packages/~/moderations/api/package.json b/packages/~/moderations/api/package.json index f0521f7b..61721d20 100644 --- a/packages/~/moderations/api/package.json +++ b/packages/~/moderations/api/package.json @@ -29,6 +29,7 @@ "@~/organizations.repository": "workspace:*", "@~/organizations.ui": "workspace:*", "@~/users.lib": "workspace:*", + "@~/users.ui": "workspace:*", "@~/zammad.lib": "workspace:*", "await-to-js": "3.0.0", "drizzle-orm": "0.33.0", diff --git a/packages/~/moderations/api/src/:id/$procedures/rejected.ts b/packages/~/moderations/api/src/:id/$procedures/rejected.ts index 00ca91a8..f219966f 100644 --- a/packages/~/moderations/api/src/:id/$procedures/rejected.ts +++ b/packages/~/moderations/api/src/:id/$procedures/rejected.ts @@ -4,11 +4,9 @@ import { zValidator } from "@hono/zod-validator"; import type { Htmx_Header } from "@~/app.core/htmx"; import { Entity_Schema } from "@~/app.core/schema"; import { set_crisp_config } from "@~/crisp.middleware"; -import { - RejectedMessage_Schema, - type RejectedModeration_Context, -} from "@~/moderations.lib/context/rejected"; +import { type RejectedModeration_Context } from "@~/moderations.lib/context/rejected"; import { MODERATION_EVENTS } from "@~/moderations.lib/event"; +import { reject_form_schema } from "@~/moderations.lib/schema/rejected.form"; import { mark_moderation_as } from "@~/moderations.lib/usecase/mark_moderation_as"; import { send_rejected_message_to_user } from "@~/moderations.lib/usecase/send_rejected_message_to_user"; import { get_moderation } from "@~/moderations.repository/get_moderation"; @@ -21,7 +19,7 @@ export default new Hono().patch( "/", set_crisp_config(), zValidator("param", Entity_Schema), - zValidator("form", RejectedMessage_Schema), + zValidator("form", reject_form_schema), async function PATH({ text, req, diff --git a/packages/~/moderations/api/src/:id/$procedures/validate.e2e.test.ts b/packages/~/moderations/api/src/:id/$procedures/validate.e2e.test.ts index fb1b0ef3..1af90a13 100644 --- a/packages/~/moderations/api/src/:id/$procedures/validate.e2e.test.ts +++ b/packages/~/moderations/api/src/:id/$procedures/validate.e2e.test.ts @@ -5,6 +5,7 @@ import { set_moncomptepro_pg } from "@~/app.middleware/set_moncomptepro_pg"; import { set_nonce } from "@~/app.middleware/set_nonce"; import { set_userinfo } from "@~/app.middleware/set_userinfo"; import { anais_tailhade } from "@~/app.middleware/set_userinfo#fixture"; +import { validate_form_schema } from "@~/moderations.lib/schema/validate.form"; import { create_adora_pony_moderation, create_adora_pony_user, @@ -26,7 +27,7 @@ import { test, } from "bun:test"; import { Hono } from "hono"; -import app, { FORM_SCHEMA } from "./validate"; +import app from "./validate"; // @@ -45,7 +46,10 @@ test("GET /moderation/:id/$procedures/validate { add_domain: true, add_member: A const body = new FormData(); body.append("add_domain", "true"); - body.append("add_member", FORM_SCHEMA.shape.add_member.Enum.AS_INTERNAL); + body.append( + "add_member", + validate_form_schema.shape.add_member.enum.AS_INTERNAL, + ); const response = await new Hono() .use(set_config({})) .use(set_moncomptepro_pg(pg)) @@ -107,7 +111,10 @@ test("GET /moderation/:id/$procedures/validate { add_domain: false, add_member: const body = new FormData(); body.append("add_domain", "false"); - body.append("add_member", FORM_SCHEMA.shape.add_member.Enum.AS_EXTERNAL); + body.append( + "add_member", + validate_form_schema.shape.add_member.enum.AS_EXTERNAL, + ); const response = await new Hono() .use(set_config({})) .use(set_moncomptepro_pg(pg)) diff --git a/packages/~/moderations/api/src/:id/$procedures/validate.ts b/packages/~/moderations/api/src/:id/$procedures/validate.ts index 4657b803..51196358 100644 --- a/packages/~/moderations/api/src/:id/$procedures/validate.ts +++ b/packages/~/moderations/api/src/:id/$procedures/validate.ts @@ -4,10 +4,10 @@ import { zValidator } from "@hono/zod-validator"; import { HTTPError } from "@~/app.core/error"; import type { Htmx_Header } from "@~/app.core/htmx"; import { Entity_Schema } from "@~/app.core/schema"; -import { z_coerce_boolean } from "@~/app.core/schema/z_coerce_boolean"; import { z_email_domain } from "@~/app.core/schema/z_email_domain"; import type { App_Context } from "@~/app.middleware/context"; import { MODERATION_EVENTS } from "@~/moderations.lib/event"; +import { validate_form_schema } from "@~/moderations.lib/schema/validate.form"; import { mark_moderation_as } from "@~/moderations.lib/usecase/mark_moderation_as"; import { MemberJoinOrganization } from "@~/moderations.lib/usecase/member_join_organization"; import { GetModerationById } from "@~/moderations.repository"; @@ -25,22 +25,13 @@ import consola from "consola"; import { eq } from "drizzle-orm"; import { Hono } from "hono"; import { P, match } from "ts-pattern"; -import { z } from "zod"; - -// - -export const FORM_SCHEMA = z.object({ - add_domain: z.string().default("false").pipe(z_coerce_boolean), - add_member: z.enum(["AS_INTERNAL", "AS_EXTERNAL"]), - send_notitfication: z.string().default("false").pipe(z_coerce_boolean), -}); // export default new Hono().patch( "/", zValidator("param", Entity_Schema), - zValidator("form", FORM_SCHEMA), + zValidator("form", validate_form_schema), async function PATCH({ text, req, diff --git a/packages/~/moderations/api/src/:id/Actions.tsx b/packages/~/moderations/api/src/:id/Actions.tsx deleted file mode 100644 index 4adf6ddc..00000000 --- a/packages/~/moderations/api/src/:id/Actions.tsx +++ /dev/null @@ -1,50 +0,0 @@ -// - -import { button } from "@~/app.ui/button"; -import { hx_urls } from "@~/app.urls"; -import { MessageInfo } from "@~/moderations.ui/MessageInfo"; -import { usePageRequestContext } from "./context"; -import { Desicison } from "./Desicison"; -import { Member_Invalid } from "./Member_Invalid"; -import { Member_Valid } from "./Member_Valid"; - -// - -export async function Moderation_Actions() { - const { - var: { moderation }, - } = usePageRequestContext(); - - const hx_moderation_reprocess_props = await hx_urls.moderations[ - ":id" - ].$procedures.reprocess.$patch({ - param: { id: moderation.id.toString() }, - }); - - return ( -

-

- Actions de modération beta{" "} -

- - - -
- {moderation.moderated_at ? ( - - ) : ( - <> - - - - - )} -
- ); -} diff --git a/packages/~/moderations/api/src/:id/Desicison_Context.tsx b/packages/~/moderations/api/src/:id/Desicison_Context.tsx deleted file mode 100644 index d4e9683d..00000000 --- a/packages/~/moderations/api/src/:id/Desicison_Context.tsx +++ /dev/null @@ -1,20 +0,0 @@ -// - -import { hyper_ref } from "@~/app.core/html"; -import { createContext } from "hono/jsx"; - -// - -export const Desicison_Context = createContext({ - $accept: hyper_ref(), - $add_as_external_member: hyper_ref(), - $add_as_internal_member: hyper_ref(), - $add_domain: hyper_ref(), - $decision_form: hyper_ref(), - $destination: hyper_ref(), - $message: hyper_ref(), - $object: hyper_ref(), - $reject: hyper_ref(), - $select: hyper_ref(), - $send_notification: hyper_ref(), -}); diff --git a/packages/~/moderations/api/src/:id/Member_Valid.tsx b/packages/~/moderations/api/src/:id/Member_Valid.tsx deleted file mode 100644 index c3e0f277..00000000 --- a/packages/~/moderations/api/src/:id/Member_Valid.tsx +++ /dev/null @@ -1,174 +0,0 @@ -// - -import { Htmx_Events, hx_include } from "@~/app.core/htmx"; -import { button } from "@~/app.ui/button"; -import { fieldset } from "@~/app.ui/form"; -import { hx_urls } from "@~/app.urls"; -import { Moderation_Type_Schema } from "@~/moderations.lib/Moderation_Type"; -import { useContext } from "hono/jsx"; -import { FORM_SCHEMA } from "./$procedures/validate"; -import { Desicison_Context } from "./Desicison_Context"; -import { usePageRequestContext } from "./context"; - -// - -export async function Member_Valid() { - const { $accept, $add_domain, $decision_form } = - useContext(Desicison_Context); - const { - var: { moderation }, - } = usePageRequestContext(); - const { base, element } = fieldset(); - - return ( - - ); -} - -function SendNotification() { - const { $send_notification } = useContext(Desicison_Context); - const { - var: { moderation }, - } = usePageRequestContext(); - const { - user: { email }, - } = moderation; - - return ( -
- - -
- ); -} - -function AddDomain() { - const { $add_domain } = useContext(Desicison_Context); - const { - var: { domain }, - } = usePageRequestContext(); - - return ( -
- - -
- ); -} - -function AddAsMemberInternal() { - const { $add_as_internal_member } = useContext(Desicison_Context); - const { - var: { - moderation: { - user: { given_name }, - }, - organization_member, - }, - } = usePageRequestContext(); - const is_already_internal_member = organization_member - ? organization_member.is_external === false - : true; - return ( -
- - -
- ); -} - -function AddAsMemberExternal() { - const { $add_as_external_member } = useContext(Desicison_Context); - const { - var: { - moderation: { - user: { given_name }, - }, - organization_member, - }, - } = usePageRequestContext(); - const is_already_external_member = organization_member?.is_external === true; - return ( -
- - -
- ); -} diff --git a/packages/~/moderations/api/src/:id/Organizations_Of_User_Table.tsx b/packages/~/moderations/api/src/:id/Organizations_Of_User_Table.tsx deleted file mode 100644 index 83ece150..00000000 --- a/packages/~/moderations/api/src/:id/Organizations_Of_User_Table.tsx +++ /dev/null @@ -1,40 +0,0 @@ -// - -import { hx_trigger_from_body } from "@~/app.core/htmx"; -import { Loader } from "@~/app.ui/loader/Loader"; -import { hx_urls } from "@~/app.urls"; -import { ORGANISATION_EVENTS } from "@~/organizations.lib/event"; -import { usePageRequestContext } from "./context"; - -// - -export async function Organizations_Of_User_Table() { - const { - var: { moderation }, - } = usePageRequestContext(); - - return ( -
-

- Organisations de {moderation.user.given_name}{" "} - {moderation.user.family_name} -

- -
-
- -
-
-
- ); -} diff --git a/packages/~/moderations/api/src/:id/context.ts b/packages/~/moderations/api/src/:id/context.ts index 4c45d461..c998ce4e 100644 --- a/packages/~/moderations/api/src/:id/context.ts +++ b/packages/~/moderations/api/src/:id/context.ts @@ -13,13 +13,6 @@ import { useRequestContext } from "hono/jsx-renderer"; // -export const RESPONSE_MESSAGE_SELECT_ID = "response-message"; -export const RESPONSE_TEXTAREA_ID = "response"; -export const EMAIL_SUBJECT_INPUT_ID = "mail-subject"; -export const EMAIL_TO_INPUT_ID = "mail-to"; - -// - export interface ModerationContext extends Env { Variables: { moderation: get_moderation_dto; diff --git a/packages/~/moderations/api/src/:id/page.tsx b/packages/~/moderations/api/src/:id/page.tsx index f6c1152d..e68b3e6d 100644 --- a/packages/~/moderations/api/src/:id/page.tsx +++ b/packages/~/moderations/api/src/:id/page.tsx @@ -4,24 +4,33 @@ import { hx_trigger_from_body } from "@~/app.core/htmx"; import { button } from "@~/app.ui/button"; import { hx_urls, urls } from "@~/app.urls"; import { MODERATION_EVENTS } from "@~/moderations.lib/event"; +import { IsUserExternalMember } from "@~/moderations.lib/usecase/IsUserExternalMember"; +import { Actions } from "@~/moderations.ui/Actions"; +import { DomainsByOrganization } from "@~/moderations.ui/DomainsByOrganization"; import { Header } from "@~/moderations.ui/Header"; +import { OrganizationsByUser } from "@~/moderations.ui/OrganizationsByUser"; +import { UsersByOrganization } from "@~/moderations.ui/UsersByOrganization"; import { About as About_Organization } from "@~/organizations.ui/info/About"; import { Investigation as Investigation_Organization } from "@~/organizations.ui/info/Investigation"; +import { CountUserMemberships } from "@~/users.lib/usecase/CountUserMemberships"; +import { SuggestSameUserEmails } from "@~/users.lib/usecase/SuggestSameUserEmails"; +import { About as About_User } from "@~/users.ui/About"; +import { Investigation as Investigation_User } from "@~/users.ui/Investigation"; import { getContext } from "hono/context-storage"; -import { About_User, Investigation_User } from "./About_User"; -import { Moderation_Actions } from "./Actions"; -import { Domain_Organization } from "./Domain_Organization"; -import { Members_Of_Organization_Table } from "./Members_Of_Organization_Table"; -import { Moderation_Exchanges } from "./Moderation_Exchanges"; -import { Organizations_Of_User_Table } from "./Organizations_Of_User_Table"; import { type ModerationContext, usePageRequestContext } from "./context"; +import { Moderation_Exchanges } from "./Moderation_Exchanges"; // export default async function Moderation_Page() { const { moderation } = getContext().var; const { - var: { organization_fiche }, + var: { + moncomptepro_pg, + organization_fiche, + query_domain_count, + query_organization_members_count, + }, } = usePageRequestContext(); return ( @@ -59,7 +68,7 @@ export default async function Moderation_Page() {
- +

- +

- - +
- +
- +
- +
diff --git a/packages/~/moderations/api/src/:id/responses/__snapshots__/already_signed.test.tsx.snap b/packages/~/moderations/api/src/:id/responses/__snapshots__/already_signed.test.tsx.snap deleted file mode 100644 index 44fcb1ba..00000000 --- a/packages/~/moderations/api/src/:id/responses/__snapshots__/already_signed.test.tsx.snap +++ /dev/null @@ -1,38 +0,0 @@ -// Bun Snapshot v1, https://goo.gl/fbAQLP - -exports[`returns all members 1`] = ` -"Bonjour, - -Votre demande pour rejoindre l'organisation « 🦄 » a été prise en compte sur https://app.moncomptepro.beta.gouv.fr. - -Vous possédez déjà un compte MonComptePro : - -- adora.pony@unicorn.xyz -- pink.diamond@unicorn.xyz -- red.diamond@unicorn.xyz - -Merci de bien vouloir vous connecter avec le compte déjà existant. - -Je reste à votre disposition pour tout complément d'information. - -Excellente journée, -L’équipe MonComptePro." -`; - -exports[`returns Diamond members 1`] = ` -"Bonjour, - -Votre demande pour rejoindre l'organisation « 🦄 » a été prise en compte sur https://app.moncomptepro.beta.gouv.fr. - -Vous possédez déjà un compte MonComptePro : - -- pink.diamond@unicorn.xyz -- red.diamond@unicorn.xyz - -Merci de bien vouloir vous connecter avec le compte déjà existant. - -Je reste à votre disposition pour tout complément d'information. - -Excellente journée, -L’équipe MonComptePro." -`; diff --git a/packages/~/moderations/api/src/:id/responses/already_signed.test.tsx b/packages/~/moderations/api/src/:id/responses/already_signed.test.tsx deleted file mode 100644 index 95cefa80..00000000 --- a/packages/~/moderations/api/src/:id/responses/already_signed.test.tsx +++ /dev/null @@ -1,111 +0,0 @@ -// - -import { set_moncomptepro_pg } from "@~/app.middleware/set_moncomptepro_pg"; -import { - create_adora_pony_user, - create_pink_diamond_user, - create_red_diamond_user, - create_unicorn_organization, -} from "@~/moncomptepro.database/seed/unicorn"; -import { - add_user_to_organization, - empty_database, - migrate, - pg, -} from "@~/moncomptepro.database/testing"; -import { beforeAll, beforeEach, expect, test } from "bun:test"; -import { Hono } from "hono"; -import { jsxRenderer } from "hono/jsx-renderer"; -import { - type ContextType, - type get_moderation_dto, - type get_organization_member_dto, -} from "../context"; -import already_signed from "./already_signed"; - -// - -beforeAll(migrate); -beforeEach(empty_database); - -test("returns all members", async () => { - const unicorn_organization_id = await given_unicorn_organization(); - - const app = new Hono() - .use("*", jsxRenderer()) - .use("*", set_moncomptepro_pg(pg)) - .get( - "/already_signed", - ({ set }, next) => { - set("domain", "unicorn.xyz"); - set("moderation", { - organization: { cached_libelle: "🦄", id: unicorn_organization_id }, - user: { family_name: "🧟" }, - } as get_moderation_dto); - set("organization_member", {} as get_organization_member_dto); - return next(); - }, - ({ render }) => { - return render(); - }, - ); - - const res = await app.fetch( - new Request("http://localhost:3000/already_signed"), - ); - expect(res.status).toBe(200); - expect(await res.text()).toMatchSnapshot(); -}); - -test("returns Diamond members", async () => { - const unicorn_organization_id = await given_unicorn_organization(); - - const app = new Hono() - .use("*", jsxRenderer()) - .use("*", set_moncomptepro_pg(pg)) - .get( - "/already_signed", - ({ set }, next) => { - set("domain", "unicorn.xyz"); - set("moderation", { - organization: { cached_libelle: "🦄", id: unicorn_organization_id }, - user: { family_name: "Diamond" }, - } as get_moderation_dto); - set("organization_member", {} as get_organization_member_dto); - return next(); - }, - ({ render }) => { - return render(); - }, - ); - - const res = await app.fetch( - new Request("http://localhost:3000/already_signed"), - ); - expect(res.status).toBe(200); - expect(await res.text()).toMatchSnapshot(); -}); - -async function given_unicorn_organization() { - const unicorn_organization_id = await create_unicorn_organization(pg); - const adora_pony_user_id = await create_adora_pony_user(pg); - await add_user_to_organization({ - organization_id: unicorn_organization_id, - user_id: adora_pony_user_id, - }); - const pink_diamond_user_id = await create_pink_diamond_user(pg); - await add_user_to_organization({ - organization_id: unicorn_organization_id, - user_id: pink_diamond_user_id, - }); - const red_diamond_user_id = await create_red_diamond_user(pg); - await add_user_to_organization({ - organization_id: unicorn_organization_id, - user_id: red_diamond_user_id, - }); - return unicorn_organization_id; -} - -async function AlreadySigned() { - return <>{await already_signed()}; -} diff --git a/packages/~/moderations/lib/src/context/rejected.ts b/packages/~/moderations/lib/src/context/rejected.ts index f9424cc8..990010ef 100644 --- a/packages/~/moderations/lib/src/context/rejected.ts +++ b/packages/~/moderations/lib/src/context/rejected.ts @@ -4,16 +4,7 @@ import type { AgentConnect_UserInfo } from "@~/app.middleware/session"; import type { Config } from "@~/crisp.lib/types"; import type { get_moderation_dto } from "@~/moderations.repository/get_moderation"; import type { MonComptePro_PgDatabase } from "@~/moncomptepro.database"; -import { z } from "zod"; - -// - -export const RejectedMessage_Schema = z.object({ - message: z.string().trim(), - subject: z.string().trim(), -}); - -export type RejectedMessage = z.TypeOf; +import type { RejectedMessage } from "../schema/rejected.form"; // diff --git a/packages/~/moderations/lib/src/entities/Moderation.ts b/packages/~/moderations/lib/src/entities/Moderation.ts new file mode 100644 index 00000000..4002c977 --- /dev/null +++ b/packages/~/moderations/lib/src/entities/Moderation.ts @@ -0,0 +1,7 @@ +// + +import type { schema } from "@~/moncomptepro.database"; + +// + +export type Moderation = typeof schema.moderations.$inferSelect; diff --git a/packages/~/moderations/lib/src/schema/rejected.form.ts b/packages/~/moderations/lib/src/schema/rejected.form.ts new file mode 100644 index 00000000..ebece083 --- /dev/null +++ b/packages/~/moderations/lib/src/schema/rejected.form.ts @@ -0,0 +1,12 @@ +// + +import { z } from "zod"; + +// + +export const reject_form_schema = z.object({ + message: z.string().trim(), + subject: z.string().trim(), +}); + +export type RejectedMessage = z.infer; diff --git a/packages/~/moderations/lib/src/schema/validate.form.ts b/packages/~/moderations/lib/src/schema/validate.form.ts new file mode 100644 index 00000000..bf381c88 --- /dev/null +++ b/packages/~/moderations/lib/src/schema/validate.form.ts @@ -0,0 +1,12 @@ +// + +import { z_coerce_boolean } from "@~/app.core/schema/z_coerce_boolean"; +import { z } from "zod"; + +// + +export const validate_form_schema = z.object({ + add_domain: z.string().default("false").pipe(z_coerce_boolean), + add_member: z.enum(["AS_INTERNAL", "AS_EXTERNAL"]), + send_notitfication: z.string().default("false").pipe(z_coerce_boolean), +}); diff --git a/packages/~/moderations/lib/src/usecase/GetModerationHeader.ts b/packages/~/moderations/lib/src/usecase/GetModerationHeader.ts index d9731097..f0740980 100644 --- a/packages/~/moderations/lib/src/usecase/GetModerationHeader.ts +++ b/packages/~/moderations/lib/src/usecase/GetModerationHeader.ts @@ -8,6 +8,7 @@ export function GetModerationHeader({ pg }: MonCompteProDatabaseCradle) { const moderation = await pg.query.moderations.findFirst({ columns: { comment: true, + created_at: true, id: true, moderated_at: true, moderated_by: true, diff --git a/packages/~/moderations/lib/src/usecase/IsUserExternalMember.ts b/packages/~/moderations/lib/src/usecase/IsUserExternalMember.ts new file mode 100644 index 00000000..c081ad1c --- /dev/null +++ b/packages/~/moderations/lib/src/usecase/IsUserExternalMember.ts @@ -0,0 +1,35 @@ +// + +import { + schema, + type MonCompteProDatabaseCradle, +} from "@~/moncomptepro.database"; +import { and, eq } from "drizzle-orm"; + +// + +type UserOrganizationIdPair = { user_id: number; organization_id: number }; + +export function IsUserExternalMember({ pg }: MonCompteProDatabaseCradle) { + return async function is_user_external_member({ + organization_id, + user_id, + }: UserOrganizationIdPair) { + const user_organization = await pg.query.users_organizations.findFirst({ + columns: { is_external: true }, + where: and( + eq(schema.users_organizations.user_id, user_id), + eq(schema.users_organizations.organization_id, organization_id), + ), + }); + + return user_organization?.is_external ?? true; + }; +} + +export type IsUserExternalMemberHandler = ReturnType< + typeof IsUserExternalMember +>; +export type IsUserExternalMemberOutput = Awaited< + ReturnType +>; diff --git a/packages/~/moderations/lib/src/usecase/send_rejected_message_to_user.ts b/packages/~/moderations/lib/src/usecase/send_rejected_message_to_user.ts index f8f606d9..5ae64a62 100644 --- a/packages/~/moderations/lib/src/usecase/send_rejected_message_to_user.ts +++ b/packages/~/moderations/lib/src/usecase/send_rejected_message_to_user.ts @@ -2,10 +2,8 @@ import { NotFoundError } from "@~/app.core/error"; import { z_username } from "@~/app.core/schema/z_username"; import { to as await_to } from "await-to-js"; import consola from "consola"; -import type { - RejectedMessage, - RejectedModeration_Context, -} from "../context/rejected"; +import type { RejectedModeration_Context } from "../context/rejected"; +import type { RejectedMessage } from "../schema/rejected.form"; import { create_and_send_email_to_user } from "./create_and_send_email_to_user"; import { respond_to_ticket } from "./respond_to_ticket"; diff --git a/packages/~/moderations/ui/src/Actions/Actions.tsx b/packages/~/moderations/ui/src/Actions/Actions.tsx new file mode 100644 index 00000000..6fc104b6 --- /dev/null +++ b/packages/~/moderations/ui/src/Actions/Actions.tsx @@ -0,0 +1,66 @@ +// + +import { hyper_ref } from "@~/app.core/html"; +import { z_email_domain } from "@~/app.core/schema/z_email_domain"; +import { button } from "@~/app.ui/button"; +import { hx_urls } from "@~/app.urls"; +import { MessageInfo } from "@~/moderations.ui/MessageInfo"; +import { context, type Values } from "./context"; +import { Desicison } from "./Desicison"; +import { MemberInvalid } from "./MemberInvalid"; +import { MemberValid } from "./MemberValid"; + +// + +type ActionProps = { + value: Omit; +}; + +export async function Actions({ value }: ActionProps) { + const { moderation } = value; + + const hx_moderation_reprocess_props = await hx_urls.moderations[ + ":id" + ].$procedures.reprocess.$patch({ + param: { id: moderation.id.toString() }, + }); + + const domain = z_email_domain.parse(moderation.user.email, { + path: ["user.email"], + }); + + return ( + +
+

Actions de modération

+ + + +
+ {moderation.moderated_at ? ( + + ) : ( + <> + + + + + )} +
+
+ ); +} diff --git a/packages/~/moderations/ui/src/Actions/AddAsMemberExternal.tsx b/packages/~/moderations/ui/src/Actions/AddAsMemberExternal.tsx new file mode 100644 index 00000000..c1a944ad --- /dev/null +++ b/packages/~/moderations/ui/src/Actions/AddAsMemberExternal.tsx @@ -0,0 +1,34 @@ +// + +import { validate_form_schema } from "@~/moderations.lib/schema/validate.form"; +import { useContext } from "hono/jsx"; +import { context, valid_context } from "./context"; + +// + +export function AddAsMemberExternal() { + const { $add_as_external_member, is_already_external_member } = + useContext(valid_context); + const { + moderation: { + user: { given_name }, + }, + } = useContext(context); + + return ( +
+ + +
+ ); +} diff --git a/packages/~/moderations/ui/src/Actions/AddAsMemberInternal.tsx b/packages/~/moderations/ui/src/Actions/AddAsMemberInternal.tsx new file mode 100644 index 00000000..9d634edb --- /dev/null +++ b/packages/~/moderations/ui/src/Actions/AddAsMemberInternal.tsx @@ -0,0 +1,30 @@ +import { validate_form_schema } from "@~/moderations.lib/schema/validate.form"; +import { useContext } from "hono/jsx"; +import { context, valid_context } from "./context"; + +export function AddAsMemberInternal() { + const { $add_as_internal_member, is_already_internal_member } = + useContext(valid_context); + const { + moderation: { + user: { given_name }, + }, + } = useContext(context); + + return ( +
+ + +
+ ); +} diff --git a/packages/~/moderations/ui/src/Actions/AddDomain.tsx b/packages/~/moderations/ui/src/Actions/AddDomain.tsx new file mode 100644 index 00000000..76fb0824 --- /dev/null +++ b/packages/~/moderations/ui/src/Actions/AddDomain.tsx @@ -0,0 +1,23 @@ +import { validate_form_schema } from "@~/moderations.lib/schema/validate.form"; +import { useContext } from "hono/jsx"; +import { context, valid_context } from "./context"; + +export function AddDomain() { + const { $add_domain } = useContext(valid_context); + const { domain } = useContext(context); + + return ( +
+ + +
+ ); +} diff --git a/packages/~/moderations/api/src/:id/Desicison.tsx b/packages/~/moderations/ui/src/Actions/Desicison.tsx similarity index 88% rename from packages/~/moderations/api/src/:id/Desicison.tsx rename to packages/~/moderations/ui/src/Actions/Desicison.tsx index bd9be191..5ca77230 100644 --- a/packages/~/moderations/api/src/:id/Desicison.tsx +++ b/packages/~/moderations/ui/src/Actions/Desicison.tsx @@ -2,11 +2,11 @@ import { fieldset } from "@~/app.ui/form"; import { useContext } from "hono/jsx"; -import { Desicison_Context } from "./Desicison_Context"; +import { context } from "./context"; // export function Desicison() { - const { $accept, $decision_form, $reject } = useContext(Desicison_Context); + const { $accept, $decision_form, $reject } = useContext(context); const { base, element, legend } = fieldset(); return ( diff --git a/packages/~/moderations/api/src/:id/Member_Invalid.tsx b/packages/~/moderations/ui/src/Actions/MemberInvalid.tsx similarity index 62% rename from packages/~/moderations/api/src/:id/Member_Invalid.tsx rename to packages/~/moderations/ui/src/Actions/MemberInvalid.tsx index ef2ef67b..13c0fd3c 100644 --- a/packages/~/moderations/api/src/:id/Member_Invalid.tsx +++ b/packages/~/moderations/ui/src/Actions/MemberInvalid.tsx @@ -4,64 +4,24 @@ import { Htmx_Events, hx_disabled_form_elements } from "@~/app.core/htmx"; import { button } from "@~/app.ui/button"; import { copy_value_to_clipboard } from "@~/app.ui/button/scripts"; import { fieldset } from "@~/app.ui/form"; -import { hx_urls, urls } from "@~/app.urls"; -import type { InferRequestType } from "hono"; +import { hx_urls } from "@~/app.urls"; +import { reject_form_schema } from "@~/moderations.lib/schema/rejected.form"; import { useContext } from "hono/jsx"; -import { usePageRequestContext } from "./context"; -import { Desicison_Context } from "./Desicison_Context"; -import * as accountant from "./responses/accountant"; -import * as already_signed from "./responses/already_signed"; -import * as chorus_pro_error from "./responses/chorus_pro_error"; -import * as contractors from "./responses/contractors"; -import * as first_last_name from "./responses/first_last_name"; -import * as invalid_job from "./responses/invalid_job"; -import * as invalid_name_job from "./responses/invalid_name_job"; -import * as link_with_eduction_gouv_fr from "./responses/link_with_eduction_gouv_fr"; -import * as link_with_organization from "./responses/link_with_organization"; -import * as mobilic from "./responses/mobilic"; -import * as use_official_email from "./responses/use_official_email"; -import * as use_pro_email from "./responses/use_pro_email"; +import { ResponseMessageSelector } from "./ResponseMessageSelector"; +import { context, reject_context } from "./context"; // -const reponse_templates = [ - first_last_name, - link_with_organization, - use_pro_email, - use_official_email, - already_signed, - link_with_eduction_gouv_fr, - mobilic, - contractors, - accountant, - invalid_name_job, - chorus_pro_error, - invalid_job, -]; - -// - -export async function Member_Invalid() { - const { - var: { moderation }, - } = usePageRequestContext(); - - const { - $destination, - $reject, - $message, - $object, - $decision_form: $form, - } = useContext(Desicison_Context); +export async function MemberInvalid() { + const { moderation, $reject, $decision_form } = useContext(context); + const { $destination, $message, $object } = useContext(reject_context); const { base, element } = fieldset(); - const $patch = urls.moderations[":id"].$procedures.rejected.$patch; - type FormNames = keyof InferRequestType["form"]; return (

@@ -134,7 +94,7 @@ export async function Member_Invalid() { class="fr-input" type="text" id={$object} - name={"subject" as FormNames} + name={reject_form_schema.keyof().Enum.subject} value={`[MonComptePro] Demande pour rejoindre « ${moderation.organization.cached_libelle} »`} /> @@ -182,26 +142,3 @@ export async function Member_Invalid() { ); } - -function ResponseMessageSelector() { - const { $message, $select } = useContext(Desicison_Context); - return ( - - ); -} diff --git a/packages/~/moderations/ui/src/Actions/MemberValid.tsx b/packages/~/moderations/ui/src/Actions/MemberValid.tsx new file mode 100644 index 00000000..fa436963 --- /dev/null +++ b/packages/~/moderations/ui/src/Actions/MemberValid.tsx @@ -0,0 +1,75 @@ +// + +import { Htmx_Events, hx_include } from "@~/app.core/htmx"; +import { button } from "@~/app.ui/button"; +import { fieldset } from "@~/app.ui/form"; +import { hx_urls } from "@~/app.urls"; +import { useContext } from "hono/jsx"; +import { AddAsMemberExternal } from "./AddAsMemberExternal"; +import { AddAsMemberInternal } from "./AddAsMemberInternal"; +import { AddDomain } from "./AddDomain"; +import { context, valid_context } from "./context"; +import { SendNotification } from "./SendNotification"; + +// + +export async function MemberValid() { + const { moderation, $decision_form, $accept, query_is_user_external_member } = + useContext(context); + const context_value = useContext(valid_context); + const { $add_domain } = context_value; + const { base, element } = fieldset(); + const hx_path_validate_moderation = await hx_urls.moderations[ + ":id" + ].$procedures.validate.$patch({ + param: { id: moderation.id.toString() }, + }); + + const is_already_external_member = await query_is_user_external_member({ + organization_id: moderation.organization.id, + user_id: moderation.user.id, + }); + + return ( + + + + ); +} diff --git a/packages/~/moderations/ui/src/Actions/ResponseMessageSelector.tsx b/packages/~/moderations/ui/src/Actions/ResponseMessageSelector.tsx new file mode 100644 index 00000000..91f58df6 --- /dev/null +++ b/packages/~/moderations/ui/src/Actions/ResponseMessageSelector.tsx @@ -0,0 +1,30 @@ +// + +import { useContext } from "hono/jsx"; +import { reject_context } from "./context"; +import { reponse_templates } from "./responses"; + +// + +export function ResponseMessageSelector() { + const { $message, $select } = useContext(reject_context); + return ( + + ); +} diff --git a/packages/~/moderations/ui/src/Actions/SendNotification.tsx b/packages/~/moderations/ui/src/Actions/SendNotification.tsx new file mode 100644 index 00000000..e5a8721e --- /dev/null +++ b/packages/~/moderations/ui/src/Actions/SendNotification.tsx @@ -0,0 +1,37 @@ +// + +import { Moderation_Type_Schema } from "@~/moderations.lib/Moderation_Type"; +import { validate_form_schema } from "@~/moderations.lib/schema/validate.form"; +import { useContext } from "hono/jsx"; +import { context, valid_context } from "./context"; + +// + +export function SendNotification() { + const { + moderation: { + type, + user: { email }, + }, + } = useContext(context); + const { $send_notification } = useContext(valid_context); + + return ( +
+ + +
+ ); +} diff --git a/packages/~/moderations/ui/src/Actions/context.ts b/packages/~/moderations/ui/src/Actions/context.ts new file mode 100644 index 00000000..6bc6b3df --- /dev/null +++ b/packages/~/moderations/ui/src/Actions/context.ts @@ -0,0 +1,42 @@ +// + +import { hyper_ref } from "@~/app.core/html"; +import type { SimplifyDeep } from "@~/app.core/types"; +import type { Moderation } from "@~/moderations.lib/entities/Moderation"; +import type { IsUserExternalMemberHandler } from "@~/moderations.lib/usecase/IsUserExternalMember"; +import type { Organization } from "@~/organizations.lib/entities/Organization"; +import type { User } from "@~/users.lib/entities/User"; +import type { SuggestSameUserEmailsHandler } from "@~/users.lib/usecase/SuggestSameUserEmails"; +import { createContext } from "hono/jsx"; + +export interface Values { + domain: string; + moderation: SimplifyDeep< + Pick & { + organization: Pick; + user: Pick; + } + >; + $reject: string; + $accept: string; + $decision_form: string; + query_suggest_same_user_emails: SuggestSameUserEmailsHandler; + query_is_user_external_member: IsUserExternalMemberHandler; +} +export const context = createContext(null as any); + +// +export const reject_context = createContext({ + $destination: hyper_ref(), + $message: hyper_ref(), + $object: hyper_ref(), + $select: hyper_ref(), +}); +export const valid_context = createContext({ + $add_as_external_member: hyper_ref(), + $add_as_internal_member: hyper_ref(), + $add_domain: hyper_ref(), + $send_notification: hyper_ref(), + is_already_internal_member: false, + is_already_external_member: false, +}); diff --git a/packages/~/moderations/ui/src/Actions/index.ts b/packages/~/moderations/ui/src/Actions/index.ts new file mode 100644 index 00000000..a2287350 --- /dev/null +++ b/packages/~/moderations/ui/src/Actions/index.ts @@ -0,0 +1,3 @@ +// + +export * from "./Actions"; diff --git a/packages/~/moderations/ui/src/Actions/responses/__snapshots__/already_signed.test.tsx.snap b/packages/~/moderations/ui/src/Actions/responses/__snapshots__/already_signed.test.tsx.snap new file mode 100644 index 00000000..bfbb9126 --- /dev/null +++ b/packages/~/moderations/ui/src/Actions/responses/__snapshots__/already_signed.test.tsx.snap @@ -0,0 +1,21 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`returns all members 1`] = ` +"Bonjour, + +Votre demande pour rejoindre l'organisation « 🦄 » a été prise en compte sur https://app.moncomptepro.beta.gouv.fr. + +Vous possédez déjà un compte MonComptePro : + +- 🦄@unicorn.xyz +- 🐷@unicorn.xyz +- 🧧@unicorn.xyz + +Merci de bien vouloir vous connecter avec le compte déjà existant. + +Je reste à votre disposition pour tout complément d'information. + +Excellente journée, +L’équipe MonComptePro. +" +`; diff --git a/packages/~/moderations/api/src/:id/responses/accountant.tsx b/packages/~/moderations/ui/src/Actions/responses/accountant.tsx similarity index 76% rename from packages/~/moderations/api/src/:id/responses/accountant.tsx rename to packages/~/moderations/ui/src/Actions/responses/accountant.tsx index 77dbd327..172550e5 100644 --- a/packages/~/moderations/api/src/:id/responses/accountant.tsx +++ b/packages/~/moderations/ui/src/Actions/responses/accountant.tsx @@ -1,18 +1,17 @@ // +import { useContext } from "hono/jsx"; import { dedent } from "ts-dedent"; -import { usePageRequestContext } from "../context"; +import { context } from "../context"; export const label = "refus prestataires"; export default function template() { const { - var: { - moderation: { - organization: { cached_libelle: organization_name }, - }, + moderation: { + organization: { cached_libelle: organization_name }, }, - } = usePageRequestContext(); + } = useContext(context); return dedent` Bonjour, diff --git a/packages/~/moderations/ui/src/Actions/responses/already_signed.test.tsx b/packages/~/moderations/ui/src/Actions/responses/already_signed.test.tsx new file mode 100644 index 00000000..8dc4538d --- /dev/null +++ b/packages/~/moderations/ui/src/Actions/responses/already_signed.test.tsx @@ -0,0 +1,124 @@ +// + +import { render_md } from "@~/app.ui/testing"; +import { expect, test } from "bun:test"; +import { context } from "../context"; +import already_signed from "./already_signed"; + +// + +test("returns all members", async () => { + expect( + await render_md( + [ + "🦄@unicorn.xyz", + "🐷@unicorn.xyz", + "🧧@unicorn.xyz", + ], + query_is_user_external_member: async () => false, + }} + > + + , + ), + ).toMatchSnapshot(); +}); + +function AlreadySigned() { + return <>{already_signed()}; +} + +// import { set_moncomptepro_pg } from "@~/app.middleware/set_moncomptepro_pg"; +// import { +// create_adora_pony_user, +// create_pink_diamond_user, +// create_red_diamond_user, +// create_unicorn_organization, +// } from "@~/moncomptepro.database/seed/unicorn"; +// import { +// add_user_to_organization, +// empty_database, +// migrate, +// pg, +// } from "@~/moncomptepro.database/testing"; +// import { beforeAll, beforeEach, expect, test } from "bun:test"; +// import { Hono } from "hono"; +// import { jsxRenderer } from "hono/jsx-renderer"; +// // import { +// // type ContextType, +// // type get_moderation_dto, +// // type get_organization_member_dto, +// // } from "../context"; +// import already_signed from "./already_signed"; + +// // + +// // beforeAll(migrate); +// // beforeEach(empty_database); + + +// // test("returns Diamond members", async () => { +// // const unicorn_organization_id = await given_unicorn_organization(); + +// // const app = new Hono() +// // .use("*", jsxRenderer()) +// // .use("*", set_moncomptepro_pg(pg)) +// // .get( +// // "/already_signed", +// // ({ set }, next) => { +// // set("domain", "unicorn.xyz"); +// // set("moderation", { +// // organization: { cached_libelle: "🦄", id: unicorn_organization_id }, +// // user: { family_name: "Diamond" }, +// // } as get_moderation_dto); +// // set("organization_member", {} as get_organization_member_dto); +// // return next(); +// // }, +// // ({ render }) => { +// // return render(); +// // }, +// // ); + +// // const res = await app.fetch( +// // new Request("http://localhost:3000/already_signed"), +// // ); +// // expect(res.status).toBe(200); +// // expect(await res.text()).toMatchSnapshot(); +// // }); + +// // async function given_unicorn_organization() { +// // const unicorn_organization_id = await create_unicorn_organization(pg); +// // const adora_pony_user_id = await create_adora_pony_user(pg); +// // await add_user_to_organization({ +// // organization_id: unicorn_organization_id, +// // user_id: adora_pony_user_id, +// // }); +// // const pink_diamond_user_id = await create_pink_diamond_user(pg); +// // await add_user_to_organization({ +// // organization_id: unicorn_organization_id, +// // user_id: pink_diamond_user_id, +// // }); +// // const red_diamond_user_id = await create_red_diamond_user(pg); +// // await add_user_to_organization({ +// // organization_id: unicorn_organization_id, +// // user_id: red_diamond_user_id, +// // }); +// // return unicorn_organization_id; +// // } + +// // async function AlreadySigned() { +// // return <>{await already_signed()}; +// // } diff --git a/packages/~/moderations/api/src/:id/responses/already_signed.tsx b/packages/~/moderations/ui/src/Actions/responses/already_signed.tsx similarity index 55% rename from packages/~/moderations/api/src/:id/responses/already_signed.tsx rename to packages/~/moderations/ui/src/Actions/responses/already_signed.tsx index 6c3ced2d..cecaa2ef 100644 --- a/packages/~/moderations/api/src/:id/responses/already_signed.tsx +++ b/packages/~/moderations/ui/src/Actions/responses/already_signed.tsx @@ -1,25 +1,17 @@ // -import type { MonComptePro_Pg_Context } from "@~/app.middleware/moncomptepro_pg"; -import { get_emails_by_organization_id } from "@~/users.repository/get_emails_by_organization_id"; -import { useRequestContext } from "hono/jsx-renderer"; +import { useContext } from "hono/jsx"; import { dedent } from "ts-dedent"; -import { usePageRequestContext } from "../context"; +import { context } from "../context"; export const label = "Vous possédez déjà un compte MonComptePro"; export default async function template() { - const { - var: { moderation }, - } = usePageRequestContext(); + const { moderation, query_suggest_same_user_emails } = useContext(context); - const { - var: { moncomptepro_pg }, - } = useRequestContext(); - - const members_email = await get_emails_by_organization_id(moncomptepro_pg, { - organization_id: moderation.organization.id, + const members_email = await query_suggest_same_user_emails({ family_name: moderation.user.family_name ?? "", + organization_id: moderation.organization.id, }); return dedent` @@ -29,7 +21,7 @@ export default async function template() { Vous possédez déjà un compte MonComptePro : - - ${members_email.map(({ email }) => email).join("\n- ")} + - ${members_email.join("\n- ")} Merci de bien vouloir vous connecter avec le compte déjà existant. diff --git a/packages/~/moderations/api/src/:id/responses/chorus_pro_error.tsx b/packages/~/moderations/ui/src/Actions/responses/chorus_pro_error.tsx similarity index 82% rename from packages/~/moderations/api/src/:id/responses/chorus_pro_error.tsx rename to packages/~/moderations/ui/src/Actions/responses/chorus_pro_error.tsx index 337695a7..a2a64fcc 100644 --- a/packages/~/moderations/api/src/:id/responses/chorus_pro_error.tsx +++ b/packages/~/moderations/ui/src/Actions/responses/chorus_pro_error.tsx @@ -1,18 +1,17 @@ // +import { useContext } from "hono/jsx"; import { dedent } from "ts-dedent"; -import { usePageRequestContext } from "../context"; +import { context } from "../context"; export const label = "Erreur Chorus Pro"; export default function template() { const { - var: { - moderation: { - organization: { cached_libelle: organization_name }, - }, + moderation: { + organization: { cached_libelle: organization_name }, }, - } = usePageRequestContext(); + } = useContext(context); return dedent` Bonjour, diff --git a/packages/~/moderations/api/src/:id/responses/contractors.tsx b/packages/~/moderations/ui/src/Actions/responses/contractors.tsx similarity index 79% rename from packages/~/moderations/api/src/:id/responses/contractors.tsx rename to packages/~/moderations/ui/src/Actions/responses/contractors.tsx index 9b26528c..e4fc98fd 100644 --- a/packages/~/moderations/api/src/:id/responses/contractors.tsx +++ b/packages/~/moderations/ui/src/Actions/responses/contractors.tsx @@ -1,18 +1,17 @@ // +import { useContext } from "hono/jsx"; import { dedent } from "ts-dedent"; -import { usePageRequestContext } from "../context"; +import { context } from "../context"; export const label = "refus comptable"; export default function template() { const { - var: { - moderation: { - organization: { cached_libelle: organization_name }, - }, + moderation: { + organization: { cached_libelle: organization_name }, }, - } = usePageRequestContext(); + } = useContext(context); return dedent` Bonjour, diff --git a/packages/~/moderations/api/src/:id/responses/first_last_name.tsx b/packages/~/moderations/ui/src/Actions/responses/first_last_name.tsx similarity index 82% rename from packages/~/moderations/api/src/:id/responses/first_last_name.tsx rename to packages/~/moderations/ui/src/Actions/responses/first_last_name.tsx index 71a6a073..dd77400f 100644 --- a/packages/~/moderations/api/src/:id/responses/first_last_name.tsx +++ b/packages/~/moderations/ui/src/Actions/responses/first_last_name.tsx @@ -1,18 +1,17 @@ // +import { useContext } from "hono/jsx"; import { dedent } from "ts-dedent"; -import { usePageRequestContext } from "../context"; +import { context } from "../context"; export const label = "nom et prénom et fonction"; export default function template() { const { - var: { - moderation: { - organization: { cached_libelle: organization_name, siret }, - }, + moderation: { + organization: { cached_libelle: organization_name, siret }, }, - } = usePageRequestContext(); + } = useContext(context); return dedent` Bonjour, diff --git a/packages/~/moderations/ui/src/Actions/responses/index.ts b/packages/~/moderations/ui/src/Actions/responses/index.ts new file mode 100644 index 00000000..99f444bc --- /dev/null +++ b/packages/~/moderations/ui/src/Actions/responses/index.ts @@ -0,0 +1,31 @@ +// + +import * as accountant from "./accountant"; +import * as already_signed from "./already_signed"; +import * as chorus_pro_error from "./chorus_pro_error"; +import * as contractors from "./contractors"; +import * as first_last_name from "./first_last_name"; +import * as invalid_job from "./invalid_job"; +import * as invalid_name_job from "./invalid_name_job"; +import * as link_with_eduction_gouv_fr from "./link_with_eduction_gouv_fr"; +import * as link_with_organization from "./link_with_organization"; +import * as mobilic from "./mobilic"; +import * as use_official_email from "./use_official_email"; +import * as use_pro_email from "./use_pro_email"; + +// + +export const reponse_templates = [ + first_last_name, + link_with_organization, + use_pro_email, + use_official_email, + already_signed, + link_with_eduction_gouv_fr, + mobilic, + contractors, + accountant, + invalid_name_job, + chorus_pro_error, + invalid_job, +]; diff --git a/packages/~/moderations/api/src/:id/responses/invalid_job.tsx b/packages/~/moderations/ui/src/Actions/responses/invalid_job.tsx similarity index 79% rename from packages/~/moderations/api/src/:id/responses/invalid_job.tsx rename to packages/~/moderations/ui/src/Actions/responses/invalid_job.tsx index 079e55e0..7479947d 100644 --- a/packages/~/moderations/api/src/:id/responses/invalid_job.tsx +++ b/packages/~/moderations/ui/src/Actions/responses/invalid_job.tsx @@ -1,18 +1,17 @@ // +import { useContext } from "hono/jsx"; import { dedent } from "ts-dedent"; -import { usePageRequestContext } from "../context"; +import { context } from "../context"; export const label = "modification fonction"; export default function template() { const { - var: { - moderation: { - organization: { cached_libelle: organization_name }, - }, + moderation: { + organization: { cached_libelle: organization_name }, }, - } = usePageRequestContext(); + } = useContext(context); return dedent` Bonjour, diff --git a/packages/~/moderations/api/src/:id/responses/invalid_name_job.tsx b/packages/~/moderations/ui/src/Actions/responses/invalid_name_job.tsx similarity index 100% rename from packages/~/moderations/api/src/:id/responses/invalid_name_job.tsx rename to packages/~/moderations/ui/src/Actions/responses/invalid_name_job.tsx diff --git a/packages/~/moderations/api/src/:id/responses/link_with_eduction_gouv_fr.tsx b/packages/~/moderations/ui/src/Actions/responses/link_with_eduction_gouv_fr.tsx similarity index 83% rename from packages/~/moderations/api/src/:id/responses/link_with_eduction_gouv_fr.tsx rename to packages/~/moderations/ui/src/Actions/responses/link_with_eduction_gouv_fr.tsx index a1caab0a..46f6660c 100644 --- a/packages/~/moderations/api/src/:id/responses/link_with_eduction_gouv_fr.tsx +++ b/packages/~/moderations/ui/src/Actions/responses/link_with_eduction_gouv_fr.tsx @@ -1,19 +1,18 @@ // +import { useContext } from "hono/jsx"; import { dedent } from "ts-dedent"; -import { usePageRequestContext } from "../context"; +import { context } from "../context"; export const label = "Pas de légitimité - Ministère de l'Éducation"; export default function template() { const { - var: { - domain, - moderation: { - organization: { cached_libelle: organization_name }, - }, + domain, + moderation: { + organization: { cached_libelle: organization_name }, }, - } = usePageRequestContext(); + } = useContext(context); return dedent` Bonjour, diff --git a/packages/~/moderations/api/src/:id/responses/link_with_organization.tsx b/packages/~/moderations/ui/src/Actions/responses/link_with_organization.tsx similarity index 77% rename from packages/~/moderations/api/src/:id/responses/link_with_organization.tsx rename to packages/~/moderations/ui/src/Actions/responses/link_with_organization.tsx index 00f3c910..ba5accf2 100644 --- a/packages/~/moderations/api/src/:id/responses/link_with_organization.tsx +++ b/packages/~/moderations/ui/src/Actions/responses/link_with_organization.tsx @@ -1,18 +1,17 @@ // +import { useContext } from "hono/jsx"; import { dedent } from "ts-dedent"; -import { usePageRequestContext } from "../context"; +import { context } from "../context"; export const label = "Quel lien avec l'organisation ?"; export default function template() { const { - var: { - moderation: { - organization: { cached_libelle: organization_name }, - }, + moderation: { + organization: { cached_libelle: organization_name }, }, - } = usePageRequestContext(); + } = useContext(context); return dedent` Bonjour, diff --git a/packages/~/moderations/api/src/:id/responses/mobilic.tsx b/packages/~/moderations/ui/src/Actions/responses/mobilic.tsx similarity index 76% rename from packages/~/moderations/api/src/:id/responses/mobilic.tsx rename to packages/~/moderations/ui/src/Actions/responses/mobilic.tsx index 2f3fd844..eb55daff 100644 --- a/packages/~/moderations/api/src/:id/responses/mobilic.tsx +++ b/packages/~/moderations/ui/src/Actions/responses/mobilic.tsx @@ -1,18 +1,17 @@ // +import { useContext } from "hono/jsx"; import { dedent } from "ts-dedent"; -import { usePageRequestContext } from "../context"; +import { context } from "../context"; export const label = "livreurs / mobilic"; export default function template() { const { - var: { - moderation: { - organization: { cached_libelle: organization_name }, - }, + moderation: { + organization: { cached_libelle: organization_name }, }, - } = usePageRequestContext(); + } = useContext(context); return dedent` Bonjour, diff --git a/packages/~/moderations/api/src/:id/responses/use_official_email.tsx b/packages/~/moderations/ui/src/Actions/responses/use_official_email.tsx similarity index 77% rename from packages/~/moderations/api/src/:id/responses/use_official_email.tsx rename to packages/~/moderations/ui/src/Actions/responses/use_official_email.tsx index 8b79b82b..6171c302 100644 --- a/packages/~/moderations/api/src/:id/responses/use_official_email.tsx +++ b/packages/~/moderations/ui/src/Actions/responses/use_official_email.tsx @@ -1,18 +1,17 @@ // +import { useContext } from "hono/jsx"; import { dedent } from "ts-dedent"; -import { usePageRequestContext } from "../context"; +import { context } from "../context"; export const label = "Merci d'utiliser votre adresse officielle de contact"; export default function template() { const { - var: { - moderation: { - organization: { cached_libelle: organization_name }, - }, + moderation: { + organization: { cached_libelle: organization_name }, }, - } = usePageRequestContext(); + } = useContext(context); return dedent` Bonjour, diff --git a/packages/~/moderations/api/src/:id/responses/use_pro_email.tsx b/packages/~/moderations/ui/src/Actions/responses/use_pro_email.tsx similarity index 76% rename from packages/~/moderations/api/src/:id/responses/use_pro_email.tsx rename to packages/~/moderations/ui/src/Actions/responses/use_pro_email.tsx index 717cda1a..8cd31a8f 100644 --- a/packages/~/moderations/api/src/:id/responses/use_pro_email.tsx +++ b/packages/~/moderations/ui/src/Actions/responses/use_pro_email.tsx @@ -1,18 +1,17 @@ // +import { useContext } from "hono/jsx"; import { dedent } from "ts-dedent"; -import { usePageRequestContext } from "../context"; +import { context } from "../context"; export const label = "Merci d'utiliser votre adresse email professionnelle"; export default function template() { const { - var: { - moderation: { - organization: { cached_libelle: organization_name }, - }, + moderation: { + organization: { cached_libelle: organization_name }, }, - } = usePageRequestContext(); + } = useContext(context); return dedent` Bonjour, diff --git a/packages/~/moderations/api/src/:id/Domain_Organization.tsx b/packages/~/moderations/ui/src/DomainsByOrganization/DomainsByOrganization.tsx similarity index 65% rename from packages/~/moderations/api/src/:id/Domain_Organization.tsx rename to packages/~/moderations/ui/src/DomainsByOrganization/DomainsByOrganization.tsx index 39d86e73..984ab369 100644 --- a/packages/~/moderations/api/src/:id/Domain_Organization.tsx +++ b/packages/~/moderations/ui/src/DomainsByOrganization/DomainsByOrganization.tsx @@ -5,20 +5,28 @@ import { hx_trigger_from_body } from "@~/app.core/htmx"; import { Loader } from "@~/app.ui/loader/Loader"; import { formattedPlural } from "@~/app.ui/plurial"; import { hx_urls } from "@~/app.urls"; +import type { Organization } from "@~/organizations.lib/entities/Organization"; import { ORGANISATION_EVENTS } from "@~/organizations.lib/event"; -import { usePageRequestContext } from "./context"; // -export async function Domain_Organization() { +type Props = { + organization: Pick; + query_domain_count: Promise; +}; +export async function DomainsByOrganization(props: Props) { const $describedby = hyper_ref(); - const { - var: { - moderation: { organization }, - query_domain_count, - }, - } = usePageRequestContext(); + const { organization, query_domain_count } = props; const count = await query_domain_count; + const query_domains_by_organization_id = await hx_urls.organizations[ + ":id" + ].domains.$get({ + param: { + id: organization.id.toString(), + }, + query: { describedby: $describedby }, + }); + return (
@@ -33,12 +41,7 @@ export async function Domain_Organization() {
{ expect( - await renderHTML( + await render_html( (null as any); +const context = createContext({} as any); // + export async function Header() { const { moderation } = useContext(context); + const hx_get_duplicate_warning = await hx_urls.moderations[ + ":id" + ].duplicate_warning.$get({ + param: { + id: moderation.id.toString(), + }, + query: { + organization_id: moderation.organization.id.toString(), + user_id: moderation.user.id.toString(), + }, + }); return (
+
+ Créé le +

@@ -50,18 +65,7 @@ export async function Header() {
-
+
Demande multiples ?

@@ -97,6 +101,12 @@ async function ModerationCallout() { if (!moderation.moderated_at) return raw``; const { base, text, title } = callout({ intent: "success" }); + const hx_patch_moderation_reprocess = await hx_urls.moderations[ + ":id" + ].$procedures.reprocess.$patch({ + param: { id: moderation.id.toString() }, + }); + return (

Modération traitée

@@ -110,17 +120,13 @@ async function ModerationCallout() { {" "} par {moderation.moderated_by} - ) : ( - raw`` - )} + ) : null} .

); } - -export function Investigation_User(props: JSX.IntrinsicElements["section"]) { - const { - var: { - moderation: { user, organization }, - }, - } = usePageRequestContext(); - - const domain = z_email_domain.parse(user.email, { path: ["user.email"] }); - - return ( -
-

🕵️ Enquête sur ce profile

- -
    -
  • - - Résultats Google pour cet email - -
  • -
  • - - Résultats Google pour ce nom de domaine - -
  • -
  • - - Résultats Google pour le nom de l'organisation et le nom de domaine - -
  • -
-
- ); -} diff --git a/packages/~/users/ui/src/About/index.ts b/packages/~/users/ui/src/About/index.ts new file mode 100644 index 00000000..f8ec6918 --- /dev/null +++ b/packages/~/users/ui/src/About/index.ts @@ -0,0 +1,3 @@ +// + +export * from "./About"; diff --git a/packages/~/users/ui/src/Investigation/Investigation.tsx b/packages/~/users/ui/src/Investigation/Investigation.tsx new file mode 100644 index 00000000..aeeddf86 --- /dev/null +++ b/packages/~/users/ui/src/Investigation/Investigation.tsx @@ -0,0 +1,55 @@ +// + +import { z_email_domain } from "@~/app.core/schema/z_email_domain"; +import { button } from "@~/app.ui/button"; +import { GoogleSearchButton } from "@~/app.ui/button/components/search"; +import type { Organization } from "@~/organizations.lib/entities/Organization"; +import type { User } from "@~/users.lib/entities/User"; + +// + +type InvestigationProps = { + user: Pick; + organization: Pick; +}; + +// + +export function Investigation(props: InvestigationProps) { + const { user, organization } = props; + + const domain = z_email_domain.parse(user.email, { path: ["user.email"] }); + + return ( +
+

🕵️ Enquête sur ce profile

+ +
    +
  • + + Résultats Google pour cet email + +
  • +
  • + + Résultats Google pour ce nom de domaine + +
  • +
  • + + Résultats Google pour le nom de l'organisation et le nom de domaine + +
  • +
+
+ ); +} diff --git a/packages/~/users/ui/src/Investigation/index.ts b/packages/~/users/ui/src/Investigation/index.ts new file mode 100644 index 00000000..9be65b42 --- /dev/null +++ b/packages/~/users/ui/src/Investigation/index.ts @@ -0,0 +1,3 @@ +// + +export * from "./Investigation"; diff --git a/packages/~/users/ui/tsconfig.json b/packages/~/users/ui/tsconfig.json new file mode 100644 index 00000000..888b4ebf --- /dev/null +++ b/packages/~/users/ui/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "jsxImportSource": "hono/jsx", + "outDir": "./node_modules/.cache/tsc" + }, + "extends": "@~/config.typescript/api/tsconfig.json", + "include": ["src"], + "references": [ + { + "path": "../../app/ui" + }, + { + "path": "../../app/urls" + }, + { + "path": "../lib" + } + ] +} diff --git a/tailwind.config.ts b/tailwind.config.ts index 5e148198..04f3e7c3 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -7,12 +7,14 @@ import { Config } from "tailwindcss"; const config: Pick = { content: [ - "./src/**/*.{ts,tsx}", "./packages/~/**/*.{ts,tsx}", "./packages/~/app/ui/src/**/*.{ts,tsx}", + "./packages/~/infra/crisp/ui/src/**/*.{ts,tsx}", "./packages/~/moderations/api/src/**/*.{ts,tsx}", + "./packages/~/moderations/ui/src/**/*.{ts,tsx}", "./packages/~/organizations/api/src/**/*.{ts,tsx}", - "./packages/~/infra/crisp/ui/src/**/*.{ts,tsx}", + "./packages/~/organizations/ui/src/**/*.{ts,tsx}", + "./packages/~/users/ui/src/**/*.{ts,tsx}", ], presets: [base_config], }; diff --git a/tsconfig.json b/tsconfig.json index 07d18a2c..5a56cccf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ "build.ts", "packages/~/*/*/*/src/**/*.tsx", "packages/~/*/*/src/**/*.tsx" - ], +, "packages/~/moderations/ui/src/Actions/context.ts" ], "references": [ { "path": "packages/~/app/api/tsconfig.json" }, { "path": "packages/~/app/core/tsconfig.json" }