diff --git a/src/modules/access/fetchAuthState.ts b/src/modules/access/fetchAuthState.ts index c48f35db..2ac15f74 100644 --- a/src/modules/access/fetchAuthState.ts +++ b/src/modules/access/fetchAuthState.ts @@ -1,8 +1,8 @@ import { verifySession } from "@/session"; import { createServerFn } from "@tanstack/react-start"; import { getUserLanguagesReadModel } from "../languages/read-models/getUserLanguagesReadModel"; -import { LanguageReadModel } from "../languages/read-models/getAllLanguagesReadModel"; import { SystemRoleRaw } from "@/modules/users/types"; +import { UserLanguageReadModel } from "../languages/read-models/getUserLanguagesReadModel"; export interface AuthState { user?: { id: string; name: string }; @@ -14,7 +14,7 @@ export const fetchAuthState = createServerFn().handler( async (): Promise => { const session = await verifySession(); - let languages: LanguageReadModel[] = []; + let languages: UserLanguageReadModel[] = []; if (session) { languages = await getUserLanguagesReadModel(session.user.id); } diff --git a/src/modules/reporting/index.ts b/src/modules/reporting/index.ts index 445016c8..b7c410a2 100644 --- a/src/modules/reporting/index.ts +++ b/src/modules/reporting/index.ts @@ -7,23 +7,3 @@ export { type BookProgressRow, type BookProgressContributor, } from "./read-models/getLanguageBookProgressReadModel"; -export { - getLanguageDashboardBooksReadModel, - type LanguageDashboardBookReadModel, -} from "./read-models/getLanguageDashboardBooksReadModel"; -export { - getLanguageDashboardMembersReadModel, - type LanguageDashboardMemberReadModel, -} from "./read-models/getLanguageDashboardMembersReadModel"; -export { - getLanguageDashboardContributionsReadModel, - type LanguageDashboardContributionReadModel, -} from "./read-models/getLanguageDashboardContributionsReadModel"; -export { - getLanguageDashboardActivityReadModel, - type LanguageDashboardActivityEntryReadModel, -} from "./read-models/getLanguageDashboardActivityReadModel"; -export { - getLanguageApprovalActivityReadModel, - type LanguageApprovalActivityReadModel, -} from "./read-models/getLanguageApprovalActivityReadModel"; diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 5c1d0289..5ba0d609 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -26,19 +26,19 @@ import { Route as MainReadRouteRouteImport } from "./routes/_main/read/route"; import { Route as MainTranslateCodeRouteImport } from "./routes/_main/translate/$code"; import { Route as MainReadCodeRouteImport } from "./routes/_main/read/$code"; import { Route as MainPSplatRouteImport } from "./routes/_main/p.$"; -import { Route as MainAdminMainRouteImport } from "./routes/_main/admin/_main"; +import { Route as mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteImport } from "./ui/admin/routes/_main"; import { Route as MainTranslateCodeVerseIdRouteImport } from "./routes/_main/translate/$code.$verseId"; import { Route as MainReadCodeChapterIdRouteImport } from "./routes/_main/read/$code.$chapterId"; -import { Route as MainAdminMainJobsRouteImport } from "./routes/_main/admin/_main.jobs"; -import { Route as MainAdminLanguagesCodeRouteRouteImport } from "./routes/_main/admin/languages.$code/route"; -import { Route as MainAdminLanguagesCodeIndexRouteImport } from "./routes/_main/admin/languages.$code/index"; -import { Route as MainAdminMainUsersIndexRouteImport } from "./routes/_main/admin/_main.users/index"; -import { Route as MainAdminMainLanguagesIndexRouteImport } from "./routes/_main/admin/_main.languages/index"; -import { Route as MainAdminLanguagesCodeSettingsRouteImport } from "./routes/_main/admin/languages.$code/settings"; -import { Route as MainAdminLanguagesCodeInviteRouteImport } from "./routes/_main/admin/languages.$code/invite"; -import { Route as MainAdminLanguagesCodeExportsRouteImport } from "./routes/_main/admin/languages.$code/exports"; -import { Route as MainAdminMainUsersInviteRouteImport } from "./routes/_main/admin/_main.users/invite"; -import { Route as MainAdminMainLanguagesNewRouteImport } from "./routes/_main/admin/_main.languages/new"; +import { Route as mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotjobsRouteImport } from "./ui/admin/routes/_main.jobs"; +import { Route as mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRouteImport } from "./ui/admin/routes/languages.$code/route"; +import { Route as mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeIndexRouteImport } from "./ui/admin/routes/languages.$code/index"; +import { Route as mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersIndexRouteImport } from "./ui/admin/routes/_main.users/index"; +import { Route as mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesIndexRouteImport } from "./ui/admin/routes/_main.languages/index"; +import { Route as mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeSettingsRouteImport } from "./ui/admin/routes/languages.$code/settings"; +import { Route as mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeInviteRouteImport } from "./ui/admin/routes/languages.$code/invite"; +import { Route as mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeExportsRouteImport } from "./ui/admin/routes/languages.$code/exports"; +import { Route as mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersInviteRouteImport } from "./ui/admin/routes/_main.users/invite"; +import { Route as mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesNewRouteImport } from "./ui/admin/routes/_main.languages/new"; const MinimalRouteRoute = MinimalRouteRouteImport.update({ id: "/_minimal", @@ -123,11 +123,12 @@ const MainPSplatRoute = MainPSplatRouteImport.update({ path: "/p/$", getParentRoute: () => MainRouteRoute, } as any); -const MainAdminMainRoute = MainAdminMainRouteImport.update({ - id: "/admin/_main", - path: "/admin", - getParentRoute: () => MainRouteRoute, -} as any); +const mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute = + mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteImport.update({ + id: "/admin/_main", + path: "/admin", + getParentRoute: () => MainRouteRoute, + } as any); const MainTranslateCodeVerseIdRoute = MainTranslateCodeVerseIdRouteImport.update({ id: "/$verseId", @@ -139,64 +140,84 @@ const MainReadCodeChapterIdRoute = MainReadCodeChapterIdRouteImport.update({ path: "/$chapterId", getParentRoute: () => MainReadCodeRoute, } as any); -const MainAdminMainJobsRoute = MainAdminMainJobsRouteImport.update({ - id: "/jobs", - path: "/jobs", - getParentRoute: () => MainAdminMainRoute, -} as any); -const MainAdminLanguagesCodeRouteRoute = - MainAdminLanguagesCodeRouteRouteImport.update({ - id: "/admin/languages/$code", - path: "/admin/languages/$code", - getParentRoute: () => MainRouteRoute, - } as any); -const MainAdminLanguagesCodeIndexRoute = - MainAdminLanguagesCodeIndexRouteImport.update({ - id: "/", - path: "/", - getParentRoute: () => MainAdminLanguagesCodeRouteRoute, - } as any); -const MainAdminMainUsersIndexRoute = MainAdminMainUsersIndexRouteImport.update({ - id: "/users/", - path: "/users/", - getParentRoute: () => MainAdminMainRoute, -} as any); -const MainAdminMainLanguagesIndexRoute = - MainAdminMainLanguagesIndexRouteImport.update({ - id: "/languages/", - path: "/languages/", - getParentRoute: () => MainAdminMainRoute, +const mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotjobsRoute = + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotjobsRouteImport.update({ + id: "/jobs", + path: "/jobs", + getParentRoute: () => mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute, } as any); -const MainAdminLanguagesCodeSettingsRoute = - MainAdminLanguagesCodeSettingsRouteImport.update({ - id: "/settings", - path: "/settings", - getParentRoute: () => MainAdminLanguagesCodeRouteRoute, - } as any); -const MainAdminLanguagesCodeInviteRoute = - MainAdminLanguagesCodeInviteRouteImport.update({ - id: "/invite", - path: "/invite", - getParentRoute: () => MainAdminLanguagesCodeRouteRoute, - } as any); -const MainAdminLanguagesCodeExportsRoute = - MainAdminLanguagesCodeExportsRouteImport.update({ - id: "/exports", - path: "/exports", - getParentRoute: () => MainAdminLanguagesCodeRouteRoute, +const mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRoute = + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRouteImport.update( + { + id: "/admin/languages/$code", + path: "/admin/languages/$code", + getParentRoute: () => MainRouteRoute, + } as any, + ); +const mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeIndexRoute = + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeIndexRouteImport.update( + { + id: "/", + path: "/", + getParentRoute: () => + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRoute, + } as any, + ); +const mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersIndexRoute = + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersIndexRouteImport.update({ + id: "/users/", + path: "/users/", + getParentRoute: () => mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute, } as any); -const MainAdminMainUsersInviteRoute = - MainAdminMainUsersInviteRouteImport.update({ +const mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesIndexRoute = + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesIndexRouteImport.update( + { + id: "/languages/", + path: "/languages/", + getParentRoute: () => mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute, + } as any, + ); +const mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeSettingsRoute = + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeSettingsRouteImport.update( + { + id: "/settings", + path: "/settings", + getParentRoute: () => + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRoute, + } as any, + ); +const mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeInviteRoute = + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeInviteRouteImport.update( + { + id: "/invite", + path: "/invite", + getParentRoute: () => + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRoute, + } as any, + ); +const mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeExportsRoute = + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeExportsRouteImport.update( + { + id: "/exports", + path: "/exports", + getParentRoute: () => + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRoute, + } as any, + ); +const mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersInviteRoute = + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersInviteRouteImport.update({ id: "/users/invite", path: "/users/invite", - getParentRoute: () => MainAdminMainRoute, - } as any); -const MainAdminMainLanguagesNewRoute = - MainAdminMainLanguagesNewRouteImport.update({ - id: "/languages/new", - path: "/languages/new", - getParentRoute: () => MainAdminMainRoute, + getParentRoute: () => mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute, } as any); +const mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesNewRoute = + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesNewRouteImport.update( + { + id: "/languages/new", + path: "/languages/new", + getParentRoute: () => mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute, + } as any, + ); export interface FileRoutesByFullPath { "/": typeof IndexRoute; @@ -211,22 +232,22 @@ export interface FileRoutesByFullPath { "/reset-password": typeof MinimalResetPasswordRoute; "/verify-email": typeof MinimalVerifyEmailRoute; "/email/notifications": typeof EmailNotificationsRoute; - "/admin": typeof MainAdminMainRouteWithChildren; + "/admin": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteWithChildren; "/p/$": typeof MainPSplatRoute; "/read/$code": typeof MainReadCodeRouteWithChildren; "/translate/$code": typeof MainTranslateCodeRouteWithChildren; - "/admin/languages/$code": typeof MainAdminLanguagesCodeRouteRouteWithChildren; - "/admin/jobs": typeof MainAdminMainJobsRoute; + "/admin/languages/$code": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRouteWithChildren; + "/admin/jobs": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotjobsRoute; "/read/$code/$chapterId": typeof MainReadCodeChapterIdRoute; "/translate/$code/$verseId": typeof MainTranslateCodeVerseIdRoute; - "/admin/languages/new": typeof MainAdminMainLanguagesNewRoute; - "/admin/users/invite": typeof MainAdminMainUsersInviteRoute; - "/admin/languages/$code/exports": typeof MainAdminLanguagesCodeExportsRoute; - "/admin/languages/$code/invite": typeof MainAdminLanguagesCodeInviteRoute; - "/admin/languages/$code/settings": typeof MainAdminLanguagesCodeSettingsRoute; - "/admin/languages/": typeof MainAdminMainLanguagesIndexRoute; - "/admin/users/": typeof MainAdminMainUsersIndexRoute; - "/admin/languages/$code/": typeof MainAdminLanguagesCodeIndexRoute; + "/admin/languages/new": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesNewRoute; + "/admin/users/invite": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersInviteRoute; + "/admin/languages/$code/exports": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeExportsRoute; + "/admin/languages/$code/invite": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeInviteRoute; + "/admin/languages/$code/settings": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeSettingsRoute; + "/admin/languages/": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesIndexRoute; + "/admin/users/": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersIndexRoute; + "/admin/languages/$code/": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeIndexRoute; } export interface FileRoutesByTo { "/": typeof IndexRoute; @@ -241,21 +262,21 @@ export interface FileRoutesByTo { "/reset-password": typeof MinimalResetPasswordRoute; "/verify-email": typeof MinimalVerifyEmailRoute; "/email/notifications": typeof EmailNotificationsRoute; - "/admin": typeof MainAdminMainRouteWithChildren; + "/admin": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteWithChildren; "/p/$": typeof MainPSplatRoute; "/read/$code": typeof MainReadCodeRouteWithChildren; "/translate/$code": typeof MainTranslateCodeRouteWithChildren; - "/admin/jobs": typeof MainAdminMainJobsRoute; + "/admin/jobs": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotjobsRoute; "/read/$code/$chapterId": typeof MainReadCodeChapterIdRoute; "/translate/$code/$verseId": typeof MainTranslateCodeVerseIdRoute; - "/admin/languages/new": typeof MainAdminMainLanguagesNewRoute; - "/admin/users/invite": typeof MainAdminMainUsersInviteRoute; - "/admin/languages/$code/exports": typeof MainAdminLanguagesCodeExportsRoute; - "/admin/languages/$code/invite": typeof MainAdminLanguagesCodeInviteRoute; - "/admin/languages/$code/settings": typeof MainAdminLanguagesCodeSettingsRoute; - "/admin/languages": typeof MainAdminMainLanguagesIndexRoute; - "/admin/users": typeof MainAdminMainUsersIndexRoute; - "/admin/languages/$code": typeof MainAdminLanguagesCodeIndexRoute; + "/admin/languages/new": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesNewRoute; + "/admin/users/invite": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersInviteRoute; + "/admin/languages/$code/exports": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeExportsRoute; + "/admin/languages/$code/invite": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeInviteRoute; + "/admin/languages/$code/settings": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeSettingsRoute; + "/admin/languages": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesIndexRoute; + "/admin/users": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersIndexRoute; + "/admin/languages/$code": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeIndexRoute; } export interface FileRoutesById { __root__: typeof rootRouteImport; @@ -273,22 +294,22 @@ export interface FileRoutesById { "/_minimal/reset-password": typeof MinimalResetPasswordRoute; "/_minimal/verify-email": typeof MinimalVerifyEmailRoute; "/email/notifications": typeof EmailNotificationsRoute; - "/_main/admin/_main": typeof MainAdminMainRouteWithChildren; + "/_main/admin/_main": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteWithChildren; "/_main/p/$": typeof MainPSplatRoute; "/_main/read/$code": typeof MainReadCodeRouteWithChildren; "/_main/translate/$code": typeof MainTranslateCodeRouteWithChildren; - "/_main/admin/languages/$code": typeof MainAdminLanguagesCodeRouteRouteWithChildren; - "/_main/admin/_main/jobs": typeof MainAdminMainJobsRoute; + "/_main/admin/languages/$code": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRouteWithChildren; + "/_main/admin/_main/jobs": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotjobsRoute; "/_main/read/$code/$chapterId": typeof MainReadCodeChapterIdRoute; "/_main/translate/$code/$verseId": typeof MainTranslateCodeVerseIdRoute; - "/_main/admin/_main/languages/new": typeof MainAdminMainLanguagesNewRoute; - "/_main/admin/_main/users/invite": typeof MainAdminMainUsersInviteRoute; - "/_main/admin/languages/$code/exports": typeof MainAdminLanguagesCodeExportsRoute; - "/_main/admin/languages/$code/invite": typeof MainAdminLanguagesCodeInviteRoute; - "/_main/admin/languages/$code/settings": typeof MainAdminLanguagesCodeSettingsRoute; - "/_main/admin/_main/languages/": typeof MainAdminMainLanguagesIndexRoute; - "/_main/admin/_main/users/": typeof MainAdminMainUsersIndexRoute; - "/_main/admin/languages/$code/": typeof MainAdminLanguagesCodeIndexRoute; + "/_main/admin/_main/languages/new": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesNewRoute; + "/_main/admin/_main/users/invite": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersInviteRoute; + "/_main/admin/languages/$code/exports": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeExportsRoute; + "/_main/admin/languages/$code/invite": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeInviteRoute; + "/_main/admin/languages/$code/settings": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeSettingsRoute; + "/_main/admin/_main/languages/": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesIndexRoute; + "/_main/admin/_main/users/": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersIndexRoute; + "/_main/admin/languages/$code/": typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeIndexRoute; } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath; @@ -516,7 +537,7 @@ declare module "@tanstack/react-router" { id: "/_main/admin/_main"; path: "/admin"; fullPath: "/admin"; - preLoaderRoute: typeof MainAdminMainRouteImport; + preLoaderRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteImport; parentRoute: typeof MainRouteRoute; }; "/_main/translate/$code/$verseId": { @@ -537,71 +558,71 @@ declare module "@tanstack/react-router" { id: "/_main/admin/_main/jobs"; path: "/jobs"; fullPath: "/admin/jobs"; - preLoaderRoute: typeof MainAdminMainJobsRouteImport; - parentRoute: typeof MainAdminMainRoute; + preLoaderRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotjobsRouteImport; + parentRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute; }; "/_main/admin/languages/$code": { id: "/_main/admin/languages/$code"; path: "/admin/languages/$code"; fullPath: "/admin/languages/$code"; - preLoaderRoute: typeof MainAdminLanguagesCodeRouteRouteImport; + preLoaderRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRouteImport; parentRoute: typeof MainRouteRoute; }; "/_main/admin/languages/$code/": { id: "/_main/admin/languages/$code/"; path: "/"; fullPath: "/admin/languages/$code/"; - preLoaderRoute: typeof MainAdminLanguagesCodeIndexRouteImport; - parentRoute: typeof MainAdminLanguagesCodeRouteRoute; + preLoaderRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeIndexRouteImport; + parentRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRoute; }; "/_main/admin/_main/users/": { id: "/_main/admin/_main/users/"; path: "/users"; fullPath: "/admin/users/"; - preLoaderRoute: typeof MainAdminMainUsersIndexRouteImport; - parentRoute: typeof MainAdminMainRoute; + preLoaderRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersIndexRouteImport; + parentRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute; }; "/_main/admin/_main/languages/": { id: "/_main/admin/_main/languages/"; path: "/languages"; fullPath: "/admin/languages/"; - preLoaderRoute: typeof MainAdminMainLanguagesIndexRouteImport; - parentRoute: typeof MainAdminMainRoute; + preLoaderRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesIndexRouteImport; + parentRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute; }; "/_main/admin/languages/$code/settings": { id: "/_main/admin/languages/$code/settings"; path: "/settings"; fullPath: "/admin/languages/$code/settings"; - preLoaderRoute: typeof MainAdminLanguagesCodeSettingsRouteImport; - parentRoute: typeof MainAdminLanguagesCodeRouteRoute; + preLoaderRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeSettingsRouteImport; + parentRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRoute; }; "/_main/admin/languages/$code/invite": { id: "/_main/admin/languages/$code/invite"; path: "/invite"; fullPath: "/admin/languages/$code/invite"; - preLoaderRoute: typeof MainAdminLanguagesCodeInviteRouteImport; - parentRoute: typeof MainAdminLanguagesCodeRouteRoute; + preLoaderRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeInviteRouteImport; + parentRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRoute; }; "/_main/admin/languages/$code/exports": { id: "/_main/admin/languages/$code/exports"; path: "/exports"; fullPath: "/admin/languages/$code/exports"; - preLoaderRoute: typeof MainAdminLanguagesCodeExportsRouteImport; - parentRoute: typeof MainAdminLanguagesCodeRouteRoute; + preLoaderRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeExportsRouteImport; + parentRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRoute; }; "/_main/admin/_main/users/invite": { id: "/_main/admin/_main/users/invite"; path: "/users/invite"; fullPath: "/admin/users/invite"; - preLoaderRoute: typeof MainAdminMainUsersInviteRouteImport; - parentRoute: typeof MainAdminMainRoute; + preLoaderRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersInviteRouteImport; + parentRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute; }; "/_main/admin/_main/languages/new": { id: "/_main/admin/_main/languages/new"; path: "/languages/new"; fullPath: "/admin/languages/new"; - preLoaderRoute: typeof MainAdminMainLanguagesNewRouteImport; - parentRoute: typeof MainAdminMainRoute; + preLoaderRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesNewRouteImport; + parentRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute; }; } } @@ -652,44 +673,55 @@ const MainTranslateRouteRouteChildren: MainTranslateRouteRouteChildren = { const MainTranslateRouteRouteWithChildren = MainTranslateRouteRoute._addFileChildren(MainTranslateRouteRouteChildren); -interface MainAdminMainRouteChildren { - MainAdminMainJobsRoute: typeof MainAdminMainJobsRoute; - MainAdminMainLanguagesNewRoute: typeof MainAdminMainLanguagesNewRoute; - MainAdminMainUsersInviteRoute: typeof MainAdminMainUsersInviteRoute; - MainAdminMainLanguagesIndexRoute: typeof MainAdminMainLanguagesIndexRoute; - MainAdminMainUsersIndexRoute: typeof MainAdminMainUsersIndexRoute; +interface mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteChildren { + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotjobsRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotjobsRoute; + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesNewRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesNewRoute; + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersInviteRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersInviteRoute; + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesIndexRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesIndexRoute; + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersIndexRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersIndexRoute; } -const MainAdminMainRouteChildren: MainAdminMainRouteChildren = { - MainAdminMainJobsRoute: MainAdminMainJobsRoute, - MainAdminMainLanguagesNewRoute: MainAdminMainLanguagesNewRoute, - MainAdminMainUsersInviteRoute: MainAdminMainUsersInviteRoute, - MainAdminMainLanguagesIndexRoute: MainAdminMainLanguagesIndexRoute, - MainAdminMainUsersIndexRoute: MainAdminMainUsersIndexRoute, -}; +const mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteChildren: mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteChildren = + { + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotjobsRoute: + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotjobsRoute, + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesNewRoute: + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesNewRoute, + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersInviteRoute: + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersInviteRoute, + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesIndexRoute: + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotlanguagesIndexRoute, + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersIndexRoute: + mainAdminDotDotDotDotDotDotUiAdminRoutesMainDotusersIndexRoute, + }; -const MainAdminMainRouteWithChildren = MainAdminMainRoute._addFileChildren( - MainAdminMainRouteChildren, -); +const mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteWithChildren = + mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute._addFileChildren( + mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteChildren, + ); -interface MainAdminLanguagesCodeRouteRouteChildren { - MainAdminLanguagesCodeExportsRoute: typeof MainAdminLanguagesCodeExportsRoute; - MainAdminLanguagesCodeInviteRoute: typeof MainAdminLanguagesCodeInviteRoute; - MainAdminLanguagesCodeSettingsRoute: typeof MainAdminLanguagesCodeSettingsRoute; - MainAdminLanguagesCodeIndexRoute: typeof MainAdminLanguagesCodeIndexRoute; +interface mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRouteChildren { + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeExportsRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeExportsRoute; + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeInviteRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeInviteRoute; + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeSettingsRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeSettingsRoute; + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeIndexRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeIndexRoute; } -const MainAdminLanguagesCodeRouteRouteChildren: MainAdminLanguagesCodeRouteRouteChildren = +const mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRouteChildren: mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRouteChildren = { - MainAdminLanguagesCodeExportsRoute: MainAdminLanguagesCodeExportsRoute, - MainAdminLanguagesCodeInviteRoute: MainAdminLanguagesCodeInviteRoute, - MainAdminLanguagesCodeSettingsRoute: MainAdminLanguagesCodeSettingsRoute, - MainAdminLanguagesCodeIndexRoute: MainAdminLanguagesCodeIndexRoute, + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeExportsRoute: + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeExportsRoute, + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeInviteRoute: + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeInviteRoute, + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeSettingsRoute: + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeSettingsRoute, + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeIndexRoute: + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeIndexRoute, }; -const MainAdminLanguagesCodeRouteRouteWithChildren = - MainAdminLanguagesCodeRouteRoute._addFileChildren( - MainAdminLanguagesCodeRouteRouteChildren, +const mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRouteWithChildren = + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRoute._addFileChildren( + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRouteChildren, ); interface MainRouteRouteChildren { @@ -698,9 +730,9 @@ interface MainRouteRouteChildren { MainDashboardRoute: typeof MainDashboardRoute; MainFeaturesRoute: typeof MainFeaturesRoute; MainProfileRoute: typeof MainProfileRoute; - MainAdminMainRoute: typeof MainAdminMainRouteWithChildren; + mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteWithChildren; MainPSplatRoute: typeof MainPSplatRoute; - MainAdminLanguagesCodeRouteRoute: typeof MainAdminLanguagesCodeRouteRouteWithChildren; + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRoute: typeof mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRouteWithChildren; } const MainRouteRouteChildren: MainRouteRouteChildren = { @@ -709,10 +741,11 @@ const MainRouteRouteChildren: MainRouteRouteChildren = { MainDashboardRoute: MainDashboardRoute, MainFeaturesRoute: MainFeaturesRoute, MainProfileRoute: MainProfileRoute, - MainAdminMainRoute: MainAdminMainRouteWithChildren, + mainAdminDotDotDotDotDotDotUiAdminRoutesMainRoute: + mainAdminDotDotDotDotDotDotUiAdminRoutesMainRouteWithChildren, MainPSplatRoute: MainPSplatRoute, - MainAdminLanguagesCodeRouteRoute: - MainAdminLanguagesCodeRouteRouteWithChildren, + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRoute: + mainAdminDotDotDotDotDotDotUiAdminRoutesLanguagesDotcodeRouteRouteWithChildren, }; const MainRouteRouteWithChildren = MainRouteRoute._addFileChildren( diff --git a/src/routes/_main/admin/__virtual.ts b/src/routes/_main/admin/__virtual.ts new file mode 100644 index 00000000..512fff83 --- /dev/null +++ b/src/routes/_main/admin/__virtual.ts @@ -0,0 +1,8 @@ +import { + defineVirtualSubtreeConfig, + physical, +} from "@tanstack/virtual-file-routes"; + +export default defineVirtualSubtreeConfig([ + physical("../../../ui/admin/routes"), +]); diff --git a/src/modules/translation/ui/AIGlossesImportForm.tsx b/src/ui/admin/components/AIGlossesImportForm.tsx similarity index 67% rename from src/modules/translation/ui/AIGlossesImportForm.tsx rename to src/ui/admin/components/AIGlossesImportForm.tsx index 51908308..37de04e5 100644 --- a/src/modules/translation/ui/AIGlossesImportForm.tsx +++ b/src/ui/admin/components/AIGlossesImportForm.tsx @@ -1,44 +1,10 @@ import { Icon } from "@/components/Icon"; -import { createPolicyMiddleware, Policy } from "@/modules/access"; -import { importAIGlosses } from "../actions/importAIGlosses"; -import { getAIGlossImportJobReadModel } from "../read-models/getAIGlossImportJobReadModel"; -import { getAIGlossImportLanguagesReadModel } from "../read-models/getAIGlossImportLanguagesReadModel"; +import { importAIGlosses } from "@/modules/translation/actions/importAIGlosses"; import JobStatusPoller from "@/shared/jobs/ui/JobStatusPoller"; import { JobStatus } from "@/shared/jobs/model"; import { useSuspenseQuery } from "@tanstack/react-query"; -import { createServerFn } from "@tanstack/react-start"; -import * as z from "zod"; import Button from "@/components/Button"; - -const requestSchema = z.object({ - code: z.string(), -}); - -const policy = new Policy({ - systemRoles: [Policy.SystemRole.Admin], - languageMember: true, -}); - -export const getAIGlossesImportFormData = createServerFn() - .inputValidator(requestSchema) - .middleware([ - createPolicyMiddleware({ - policy, - languageCodeField: "code", - }), - ]) - .handler(async ({ data }) => { - const [job, availableLanguages] = await Promise.all([ - getAIGlossImportJobReadModel(data.code), - getAIGlossImportLanguagesReadModel(), - ]); - - const language = availableLanguages.find( - (entry) => entry.code === data.code, - ); - - return { job, languageAvailable: !!language }; - }); +import { getAIGlossesImportFormData } from "@/ui/admin/serverFns/getAIGlossesImportFormData"; export default function AIGlossesImportForm({ code }: { code: string }) { const { data, refetch } = useSuspenseQuery({ diff --git a/src/modules/languages/ui/ActivityChart.tsx b/src/ui/admin/components/ActivityChart.tsx similarity index 98% rename from src/modules/languages/ui/ActivityChart.tsx rename to src/ui/admin/components/ActivityChart.tsx index 59b0b38c..c6bf2776 100644 --- a/src/modules/languages/ui/ActivityChart.tsx +++ b/src/ui/admin/components/ActivityChart.tsx @@ -163,9 +163,9 @@ function ActivityChartSVG({ const xScaleRef = useRef | null>(null); // Unique IDs so multiple charts on the same page don't share gradient defs - const uid = useId().replace(/:/g, ""); - const gradientId = `activity-gradient-${uid}`; - const clipId = `activity-clip-${uid}`; + const chartId = useId().replace(/:/g, ""); + const gradientId = `activity-gradient-${chartId}`; + const clipId = `activity-clip-${chartId}`; const [elementRef, size] = useElementDimensions(); diff --git a/src/modules/languages/ui/BookProgressDetailsModal.tsx b/src/ui/admin/components/BookProgressDetailsModal.tsx similarity index 97% rename from src/modules/languages/ui/BookProgressDetailsModal.tsx rename to src/ui/admin/components/BookProgressDetailsModal.tsx index 4dbd19fb..4bb096b8 100644 --- a/src/modules/languages/ui/BookProgressDetailsModal.tsx +++ b/src/ui/admin/components/BookProgressDetailsModal.tsx @@ -6,9 +6,9 @@ import ActivityChart, { ActivityChartProvider, type ActivityChartEntry, type ActivityChartRange, -} from "@/modules/languages/ui/ActivityChart"; -import ProgressBar from "@/modules/languages/ui/ProgressBar"; -import RangeToggle from "@/modules/languages/ui/RangeToggle"; +} from "@/ui/admin/components/ActivityChart"; +import ProgressBar from "@/ui/admin/components/ProgressBar"; +import RangeToggle from "@/ui/admin/components/RangeToggle"; import Button from "@/components/Button"; export interface BookProgressDetails { diff --git a/src/modules/languages/ui/ContributionBar.tsx b/src/ui/admin/components/ContributionBar.tsx similarity index 100% rename from src/modules/languages/ui/ContributionBar.tsx rename to src/ui/admin/components/ContributionBar.tsx diff --git a/src/modules/languages/ui/DashboardCard.tsx b/src/ui/admin/components/DashboardCard.tsx similarity index 100% rename from src/modules/languages/ui/DashboardCard.tsx rename to src/ui/admin/components/DashboardCard.tsx diff --git a/src/modules/export/ui/InterlinearExportPanel.tsx b/src/ui/admin/components/InterlinearExportPanel.tsx similarity index 84% rename from src/modules/export/ui/InterlinearExportPanel.tsx rename to src/ui/admin/components/InterlinearExportPanel.tsx index 0a9ee9a8..659cae1b 100644 --- a/src/modules/export/ui/InterlinearExportPanel.tsx +++ b/src/ui/admin/components/InterlinearExportPanel.tsx @@ -1,39 +1,11 @@ import { requestInterlinearExport } from "@/modules/export/actions/requestInterlinearExport"; -import { createPolicyMiddleware, Policy } from "@/modules/access"; import { JobStatus } from "@/shared/jobs/model"; import Button from "@/components/Button"; import { Icon } from "@/components/Icon"; -import exportJobQueryService from "../data-access/ExportJobQueryService"; import JobStatusPoller from "@/shared/jobs/ui/JobStatusPoller"; import { useTranslations } from "use-intl"; import { useSuspenseQuery } from "@tanstack/react-query"; -import { createServerFn } from "@tanstack/react-start"; -import * as z from "zod"; - -const requestSchema = z.object({ - languageCode: z.string().min(1), -}); - -const policy = new Policy({ - systemRoles: [Policy.SystemRole.Admin], - languageMember: true, -}); - -export const getInterlinearExportPanelData = createServerFn() - .inputValidator(requestSchema) - .middleware([ - createPolicyMiddleware({ - policy, - }), - ]) - .handler(async ({ data }) => { - const [jobs, pendingJob] = await Promise.all([ - exportJobQueryService.findRecentForLanguage(data.languageCode), - exportJobQueryService.findPendingForLanguage(data.languageCode), - ]); - - return { jobs, pendingJob }; - }); +import { getInterlinearExportPanelData } from "@/ui/admin/serverFns/getInterlinearExportPanelData"; export default function InterlinearExportPanel({ languageCode, diff --git a/src/modules/languages/ui/LanguageBookProgressDashboardCard.tsx b/src/ui/admin/components/LanguageBookProgressDashboardCard.tsx similarity index 88% rename from src/modules/languages/ui/LanguageBookProgressDashboardCard.tsx rename to src/ui/admin/components/LanguageBookProgressDashboardCard.tsx index 5e6bdea0..2816a028 100644 --- a/src/modules/languages/ui/LanguageBookProgressDashboardCard.tsx +++ b/src/ui/admin/components/LanguageBookProgressDashboardCard.tsx @@ -1,7 +1,7 @@ "use client"; import { useMemo } from "react"; -import ProgressBar from "@/modules/languages/ui/ProgressBar"; +import ProgressBar from "@/ui/admin/components/ProgressBar"; import Button from "@/components/Button"; import { Icon } from "@/components/Icon"; import { @@ -11,14 +11,12 @@ import { } from "./DashboardCard"; import BookProgressDetailsModal, { type BookProgressDetails, -} from "@/modules/languages/ui/BookProgressDetailsModal"; -import { type ActivityChartRange } from "@/modules/languages/ui/ActivityChart"; -import { - type LanguageDashboardActivityEntryReadModel, - type LanguageDashboardBookReadModel, - type LanguageDashboardContributionReadModel, - type LanguageDashboardMemberReadModel, -} from "@/modules/reporting"; +} from "@/ui/admin/components/BookProgressDetailsModal"; +import { type ActivityChartRange } from "@/ui/admin/components/ActivityChart"; +import { type LanguageDashboardActivityEntryReadModel } from "@/ui/admin/readModels/getLanguageDashboardActivityReadModel"; +import { type LanguageDashboardContributionReadModel } from "@/ui/admin/readModels/getLanguageDashboardContributionsReadModel"; +import { type LanguageDashboardMemberReadModel } from "@/ui/admin/readModels/getLanguageDashboardMembersReadModel"; +import { type LanguageDashboardBookReadModel } from "@/ui/admin/readModels/getLanguageDashboardBooksReadModel"; interface LanguageBookProgressDashboardCardProps { className?: string; diff --git a/src/modules/languages/ui/LanguageGlossApprovalDashboardCard.tsx b/src/ui/admin/components/LanguageGlossApprovalDashboardCard.tsx similarity index 98% rename from src/modules/languages/ui/LanguageGlossApprovalDashboardCard.tsx rename to src/ui/admin/components/LanguageGlossApprovalDashboardCard.tsx index bb3f7ae8..b58a049e 100644 --- a/src/modules/languages/ui/LanguageGlossApprovalDashboardCard.tsx +++ b/src/ui/admin/components/LanguageGlossApprovalDashboardCard.tsx @@ -14,7 +14,7 @@ import { import { UTCDate } from "@date-fns/utc"; import * as d3 from "d3"; import { useEffect, useId, useMemo, useRef, useState } from "react"; -import { type LanguageApprovalActivityReadModel } from "@/modules/reporting"; +import { type LanguageApprovalActivityReadModel } from "@/ui/admin/readModels/getLanguageApprovalActivityReadModel"; import { useElementDimensions } from "@/utils/measure-element"; import { type ActivityChartRange } from "./ActivityChart"; import { @@ -248,8 +248,8 @@ function ApprovalActivityChart({ > > | null>(null); - const uid = useId().replace(/:/g, ""); - const clipId = `approval-clip-${uid}`; + const chartId = useId().replace(/:/g, ""); + const clipId = `approval-clip-${chartId}`; const [elementRef, size] = useElementDimensions(); diff --git a/src/modules/languages/ui/LanguageUsersDashboardCard.tsx b/src/ui/admin/components/LanguageUsersDashboardCard.tsx similarity index 92% rename from src/modules/languages/ui/LanguageUsersDashboardCard.tsx rename to src/ui/admin/components/LanguageUsersDashboardCard.tsx index b16a686a..221caa1a 100644 --- a/src/modules/languages/ui/LanguageUsersDashboardCard.tsx +++ b/src/ui/admin/components/LanguageUsersDashboardCard.tsx @@ -1,15 +1,13 @@ -import { - LanguageDashboardActivityEntryReadModel, - LanguageDashboardContributionReadModel, - LanguageDashboardMemberReadModel, -} from "@/modules/reporting"; +import { type LanguageDashboardActivityEntryReadModel } from "@/ui/admin/readModels/getLanguageDashboardActivityReadModel"; +import { type LanguageDashboardContributionReadModel } from "@/ui/admin/readModels/getLanguageDashboardContributionsReadModel"; +import { type LanguageDashboardMemberReadModel } from "@/ui/admin/readModels/getLanguageDashboardMembersReadModel"; import ActivityChart, { ActivityChartRange } from "./ActivityChart"; import { useMemo } from "react"; import { Icon } from "@/components/Icon"; import ContributionBar from "./ContributionBar"; import ServerAction from "@/components/ServerAction"; -import { removeLanguageMember } from "../actions/removeLanguageMember"; -import { reinviteLanguageMemberAction } from "../actions/reinviteLanguageMember"; +import { removeLanguageMember } from "@/modules/languages/actions/removeLanguageMember"; +import { reinviteLanguageMemberAction } from "@/modules/languages/actions/reinviteLanguageMember"; import { DashboardCard, DashboardCardEmptyState, diff --git a/src/modules/languages/ui/ProgressBar.tsx b/src/ui/admin/components/ProgressBar.tsx similarity index 100% rename from src/modules/languages/ui/ProgressBar.tsx rename to src/ui/admin/components/ProgressBar.tsx diff --git a/src/modules/languages/ui/RangeToggle.tsx b/src/ui/admin/components/RangeToggle.tsx similarity index 91% rename from src/modules/languages/ui/RangeToggle.tsx rename to src/ui/admin/components/RangeToggle.tsx index 494d3d98..a3e8a374 100644 --- a/src/modules/languages/ui/RangeToggle.tsx +++ b/src/ui/admin/components/RangeToggle.tsx @@ -1,5 +1,5 @@ import { Radio, RadioGroup } from "@headlessui/react"; -import { type ActivityChartRange } from "@/modules/languages/ui/ActivityChart"; +import { type ActivityChartRange } from "@/ui/admin/components/ActivityChart"; export default function RangeToggle({ range, diff --git a/src/modules/languages/ui/SavingIndicator.tsx b/src/ui/admin/components/SavingIndicator.tsx similarity index 100% rename from src/modules/languages/ui/SavingIndicator.tsx rename to src/ui/admin/components/SavingIndicator.tsx diff --git a/src/modules/languages/ui/StatusBadge.tsx b/src/ui/admin/components/StatusBadge.tsx similarity index 100% rename from src/modules/languages/ui/StatusBadge.tsx rename to src/ui/admin/components/StatusBadge.tsx diff --git a/src/modules/translation/read-models/getAIGlossImportJobReadModel.ts b/src/ui/admin/readModels/getAIGlossImportJobReadModel.ts similarity index 88% rename from src/modules/translation/read-models/getAIGlossImportJobReadModel.ts rename to src/ui/admin/readModels/getAIGlossImportJobReadModel.ts index c26f8aab..0813da54 100644 --- a/src/modules/translation/read-models/getAIGlossImportJobReadModel.ts +++ b/src/ui/admin/readModels/getAIGlossImportJobReadModel.ts @@ -1,5 +1,5 @@ import { getDb } from "@/db"; -import { TRANSLATION_JOB_TYPES } from "../jobs/jobType"; +import { TRANSLATION_JOB_TYPES } from "@/modules/translation/jobs/jobType"; import { JobStatus } from "@/shared/jobs/model"; interface AIGlossImportJobReadModel { diff --git a/src/modules/translation/read-models/getAIGlossImportLanguagesReadModel.ts b/src/ui/admin/readModels/getAIGlossImportLanguagesReadModel.ts similarity index 58% rename from src/modules/translation/read-models/getAIGlossImportLanguagesReadModel.ts rename to src/ui/admin/readModels/getAIGlossImportLanguagesReadModel.ts index d3530d59..8c0f66a2 100644 --- a/src/modules/translation/read-models/getAIGlossImportLanguagesReadModel.ts +++ b/src/ui/admin/readModels/getAIGlossImportLanguagesReadModel.ts @@ -1,4 +1,4 @@ -import { aiGlossImportService } from "../data-access/aiGlossImportService"; +import { aiGlossImportService } from "@/modules/translation/data-access/aiGlossImportService"; // TODO: Cache this export function getAIGlossImportLanguagesReadModel() { diff --git a/src/modules/languages/read-models/getAllLanguagesReadModel.test.ts b/src/ui/admin/readModels/getAllLanguagesReadModel.test.ts similarity index 93% rename from src/modules/languages/read-models/getAllLanguagesReadModel.test.ts rename to src/ui/admin/readModels/getAllLanguagesReadModel.test.ts index 3c0efba8..6306cb07 100644 --- a/src/modules/languages/read-models/getAllLanguagesReadModel.test.ts +++ b/src/ui/admin/readModels/getAllLanguagesReadModel.test.ts @@ -1,6 +1,6 @@ import { initializeDatabase } from "@/tests/vitest/dbUtils"; import { expect, test } from "vitest"; -import { getAllLanguagesReadModel } from "./getAllLanguagesReadModel"; +import { getAllLanguagesReadModel } from "@/ui/admin/readModels/getAllLanguagesReadModel"; import { languageFactory } from "@/modules/languages/test-utils/languageFactory"; initializeDatabase(); diff --git a/src/modules/languages/read-models/getAllLanguagesReadModel.ts b/src/ui/admin/readModels/getAllLanguagesReadModel.ts similarity index 100% rename from src/modules/languages/read-models/getAllLanguagesReadModel.ts rename to src/ui/admin/readModels/getAllLanguagesReadModel.ts diff --git a/src/modules/reporting/read-models/getLanguageApprovalActivityReadModel.ts b/src/ui/admin/readModels/getLanguageApprovalActivityReadModel.ts similarity index 100% rename from src/modules/reporting/read-models/getLanguageApprovalActivityReadModel.ts rename to src/ui/admin/readModels/getLanguageApprovalActivityReadModel.ts diff --git a/src/modules/reporting/read-models/getLanguageDashboardActivityReadModel.ts b/src/ui/admin/readModels/getLanguageDashboardActivityReadModel.ts similarity index 100% rename from src/modules/reporting/read-models/getLanguageDashboardActivityReadModel.ts rename to src/ui/admin/readModels/getLanguageDashboardActivityReadModel.ts diff --git a/src/modules/reporting/read-models/getLanguageDashboardBooksReadModel.ts b/src/ui/admin/readModels/getLanguageDashboardBooksReadModel.ts similarity index 100% rename from src/modules/reporting/read-models/getLanguageDashboardBooksReadModel.ts rename to src/ui/admin/readModels/getLanguageDashboardBooksReadModel.ts diff --git a/src/modules/reporting/read-models/getLanguageDashboardContributionsReadModel.ts b/src/ui/admin/readModels/getLanguageDashboardContributionsReadModel.ts similarity index 100% rename from src/modules/reporting/read-models/getLanguageDashboardContributionsReadModel.ts rename to src/ui/admin/readModels/getLanguageDashboardContributionsReadModel.ts diff --git a/src/modules/reporting/read-models/getLanguageDashboardMembersReadModel.ts b/src/ui/admin/readModels/getLanguageDashboardMembersReadModel.ts similarity index 100% rename from src/modules/reporting/read-models/getLanguageDashboardMembersReadModel.ts rename to src/ui/admin/readModels/getLanguageDashboardMembersReadModel.ts diff --git a/src/modules/languages/read-models/getLanguageSettingsReadModel.test.ts b/src/ui/admin/readModels/getLanguageSettingsReadModel.test.ts similarity index 81% rename from src/modules/languages/read-models/getLanguageSettingsReadModel.test.ts rename to src/ui/admin/readModels/getLanguageSettingsReadModel.test.ts index 0f755a13..32bf0568 100644 --- a/src/modules/languages/read-models/getLanguageSettingsReadModel.test.ts +++ b/src/ui/admin/readModels/getLanguageSettingsReadModel.test.ts @@ -1,8 +1,11 @@ import { test, expect } from "vitest"; import { initializeDatabase } from "@/tests/vitest/dbUtils"; -import { getLanguageSettingsReadModel } from "./getLanguageSettingsReadModel"; -import { languageFactory } from "../test-utils/languageFactory"; -import { MachineGlossStrategy, TextDirectionRaw } from "../model"; +import { getLanguageSettingsReadModel } from "@/ui/admin/readModels/getLanguageSettingsReadModel"; +import { languageFactory } from "@/modules/languages/test-utils/languageFactory"; +import { + MachineGlossStrategy, + TextDirectionRaw, +} from "@/modules/languages/model"; initializeDatabase(); diff --git a/src/modules/languages/read-models/getLanguageSettingsReadModel.ts b/src/ui/admin/readModels/getLanguageSettingsReadModel.ts similarity index 93% rename from src/modules/languages/read-models/getLanguageSettingsReadModel.ts rename to src/ui/admin/readModels/getLanguageSettingsReadModel.ts index 18d00545..85ce34cd 100644 --- a/src/modules/languages/read-models/getLanguageSettingsReadModel.ts +++ b/src/ui/admin/readModels/getLanguageSettingsReadModel.ts @@ -1,6 +1,6 @@ import { getDb } from "@/db"; import { sql } from "kysely"; -import { MachineGlossStrategy } from "../model"; +import { MachineGlossStrategy } from "@/modules/languages/model"; export interface LanguageSettingsReadModel { englishName: string; diff --git a/src/modules/languages/read-models/searchLanguagesReadModel.test.ts b/src/ui/admin/readModels/searchLanguagesReadModel.test.ts similarity index 95% rename from src/modules/languages/read-models/searchLanguagesReadModel.test.ts rename to src/ui/admin/readModels/searchLanguagesReadModel.test.ts index 7fe0a864..8f8ea327 100644 --- a/src/modules/languages/read-models/searchLanguagesReadModel.test.ts +++ b/src/ui/admin/readModels/searchLanguagesReadModel.test.ts @@ -1,6 +1,6 @@ import { initializeDatabase } from "@/tests/vitest/dbUtils"; import { expect, test } from "vitest"; -import { searchLanguagesReadModel } from "./searchLanguagesReadModel"; +import { searchLanguagesReadModel } from "@/ui/admin/readModels/searchLanguagesReadModel"; import { languageFactory } from "@/modules/languages/test-utils/languageFactory"; initializeDatabase(); diff --git a/src/modules/languages/read-models/searchLanguagesReadModel.ts b/src/ui/admin/readModels/searchLanguagesReadModel.ts similarity index 100% rename from src/modules/languages/read-models/searchLanguagesReadModel.ts rename to src/ui/admin/readModels/searchLanguagesReadModel.ts diff --git a/src/modules/users/read-models/searchUsersReadModel.ts b/src/ui/admin/readModels/searchUsersReadModel.ts similarity index 93% rename from src/modules/users/read-models/searchUsersReadModel.ts rename to src/ui/admin/readModels/searchUsersReadModel.ts index 0b17594b..035ca692 100644 --- a/src/modules/users/read-models/searchUsersReadModel.ts +++ b/src/ui/admin/readModels/searchUsersReadModel.ts @@ -1,9 +1,9 @@ import { jsonBuildObject } from "kysely/helpers/postgres"; import { getDb } from "@/db"; -import { UserStatusRaw } from "../model/UserStatus"; +import { UserStatusRaw } from "@/modules/users/model/UserStatus"; import { sql } from "kysely"; -import { SystemRoleRaw } from "../model/SystemRole"; -import { EmailStatusRaw } from "../model/EmailStatus"; +import { SystemRoleRaw } from "@/modules/users/model/SystemRole"; +import { EmailStatusRaw } from "@/modules/users/model/EmailStatus"; export interface UserReadModel { id: string; diff --git a/src/routes/_main/admin/_main.jobs.tsx b/src/ui/admin/routes/_main.jobs.tsx similarity index 78% rename from src/routes/_main/admin/_main.jobs.tsx rename to src/ui/admin/routes/_main.jobs.tsx index 47b6557f..157142c1 100644 --- a/src/routes/_main/admin/_main.jobs.tsx +++ b/src/ui/admin/routes/_main.jobs.tsx @@ -12,45 +12,24 @@ import { import { queueJobAction } from "@/shared/jobs/queueJobAction"; import { REPORTING_JOB_TYPES } from "@/modules/reporting/jobs/jobTypes"; import { EXPORT_JOB_TYPES } from "@/modules/export/jobs/jobTypes"; -import { createPolicyMiddleware, Policy } from "@/modules/access"; -import { getDb } from "@/db"; import { JobStatus } from "@/shared/jobs/model"; import { useSuspenseQuery } from "@tanstack/react-query"; -import { createServerFn } from "@tanstack/react-start"; import { createFileRoute } from "@tanstack/react-router"; import { withDocumentTitle } from "@/documentTitle"; -import { sql } from "kysely"; +import { getActiveJobs } from "@/ui/admin/serverFns/getActiveJobs"; export const Route = createFileRoute("/_main/admin/_main/jobs")({ head: () => withDocumentTitle("Jobs | Admin"), component: AdminJobsView, }); -const githubExportPolicy = new Policy({ - systemRoles: [Policy.SystemRole.Admin], -}); - -function getStatusClassName(status: JobStatus): string { - switch (status) { - case JobStatus.Complete: - return "bg-green-200 text-gray-900"; - case JobStatus.Failed: - return "bg-red-300 text-gray-900"; - case JobStatus.InProgress: - return "bg-brown-100 text-gray-900"; - case JobStatus.Pending: - default: - return "bg-gray-200 text-gray-900"; - } -} - function AdminJobsView() { const { data: { exportJobs }, refetch, } = useSuspenseQuery({ queryKey: ["activeJobs"], - queryFn: () => getActiveJobsReadModel(), + queryFn: () => getActiveJobs(), refetchInterval: ({ state }) => { const hasInprogressJobs = state.data?.exportJobs.some( (job) => @@ -151,46 +130,16 @@ function AdminJobsView() { ); } -const getActiveJobsReadModel = createServerFn() - .middleware([ - createPolicyMiddleware({ - policy: githubExportPolicy, - }), - ]) - .handler(async () => { - const exportJobs = await getDb() - .with("export_job", (db) => - db - .selectFrom("job") - .where("type", "=", EXPORT_JOB_TYPES.EXPORT_GLOSSES) - .orderBy("created_at", "desc") - .select(["id"]) - .limit(1), - ) - .selectFrom("job") - .where((eb) => - eb.or([ - eb( - "job.parent_job_id", - "=", - eb.selectFrom("export_job").select("id"), - ), - eb("job.id", "=", eb.selectFrom("export_job").select("id")), - ]), - ) - .orderBy("created_at") - .select([ - "job.id", - "job.type", - "job.status", - "job.updated_at as updatedAt", - "job.created_at as createdAt", - (eb) => - sql>`${eb.ref("job.payload")}->'languageCodes'`.as( - "languages", - ), - ]) - .execute(); - - return { exportJobs }; - }); +function getStatusClassName(status: JobStatus): string { + switch (status) { + case JobStatus.Complete: + return "bg-green-200 text-gray-900"; + case JobStatus.Failed: + return "bg-red-300 text-gray-900"; + case JobStatus.InProgress: + return "bg-brown-100 text-gray-900"; + case JobStatus.Pending: + default: + return "bg-gray-200 text-gray-900"; + } +} diff --git a/src/routes/_main/admin/_main.languages/index.tsx b/src/ui/admin/routes/_main.languages/index.tsx similarity index 77% rename from src/routes/_main/admin/_main.languages/index.tsx rename to src/ui/admin/routes/_main.languages/index.tsx index 59f6628b..dba59cf2 100644 --- a/src/routes/_main/admin/_main.languages/index.tsx +++ b/src/ui/admin/routes/_main.languages/index.tsx @@ -11,16 +11,16 @@ import { } from "@/components/List"; import Pagination from "@/components/Pagination"; import ViewTitle from "@/components/ViewTitle"; -import { createPolicyMiddleware, Policy } from "@/modules/access"; +import { Policy } from "@/modules/access"; import { routerGuard } from "@/modules/access/routerGuard"; -import { searchLanguagesReadModel } from "@/modules/languages/read-models/searchLanguagesReadModel"; -import { createFileRoute, redirect } from "@tanstack/react-router"; -import { createServerFn } from "@tanstack/react-start"; +import { createFileRoute } from "@tanstack/react-router"; import { withDocumentTitle } from "@/documentTitle"; +import { loadAdminLanguagesPage } from "@/ui/admin/serverFns/loadAdminLanguagesPage"; -const LIMIT = 20; const policy = new Policy({ systemRoles: [Policy.SystemRole.Admin] }); +const LIMIT = 20; + const schema = z.object({ page: z.coerce.number().int().default(1), }); @@ -31,31 +31,12 @@ export const Route = createFileRoute("/_main/admin/_main/languages/")({ beforeLoad: ({ context }) => { routerGuard({ context: context.auth, policy }); }, - loader: ({ deps }) => loaderFn({ data: deps }), + loader: ({ deps }) => + loadAdminLanguagesPage({ data: { ...deps, limit: LIMIT } }), head: () => withDocumentTitle("Languages | Admin"), component: AdminLanguagesRoute, }); -const loaderFn = createServerFn() - .inputValidator(schema) - .middleware([createPolicyMiddleware({ policy })]) - .handler(async ({ data }) => { - if (data.page <= 0) { - throw redirect({ to: "/admin/languages", search: { page: 1 } }); - } - - const { page: languages, total } = await searchLanguagesReadModel({ - page: data.page - 1, - limit: LIMIT, - }); - - if (languages.length === 0 && data.page !== 1) { - throw redirect({ to: "/admin/languages", search: { page: 1 } }); - } - - return { languages, total }; - }); - function AdminLanguagesRoute() { const { languages, total } = Route.useLoaderData(); diff --git a/src/routes/_main/admin/_main.languages/new.tsx b/src/ui/admin/routes/_main.languages/new.tsx similarity index 100% rename from src/routes/_main/admin/_main.languages/new.tsx rename to src/ui/admin/routes/_main.languages/new.tsx diff --git a/src/routes/_main/admin/_main.tsx b/src/ui/admin/routes/_main.tsx similarity index 92% rename from src/routes/_main/admin/_main.tsx rename to src/ui/admin/routes/_main.tsx index c7af3f23..1e845d7d 100644 --- a/src/routes/_main/admin/_main.tsx +++ b/src/ui/admin/routes/_main.tsx @@ -1,4 +1,3 @@ -import { ReactNode } from "react"; import SidebarLink from "@/components/SidebarLink"; import { Icon } from "@/components/Icon"; import { createFileRoute, Outlet, redirect } from "@tanstack/react-router"; @@ -25,11 +24,6 @@ export const Route = createFileRoute("/_main/admin/_main")({ component: AdminLayout, }); -export interface AdminLayoutProps { - children?: ReactNode; - params: Promise<{ locale: string }>; -} - function AdminLayout() { const t = useTranslations("AdminLayout"); diff --git a/src/routes/_main/admin/_main.users/index.tsx b/src/ui/admin/routes/_main.users/index.tsx similarity index 86% rename from src/routes/_main/admin/_main.users/index.tsx rename to src/ui/admin/routes/_main.users/index.tsx index 15360361..ec38e6c5 100644 --- a/src/routes/_main/admin/_main.users/index.tsx +++ b/src/ui/admin/routes/_main.users/index.tsx @@ -17,13 +17,15 @@ import Form from "@/components/Form"; import ServerAction from "@/components/ServerAction"; import { useTranslations } from "use-intl"; import Pagination from "@/components/Pagination"; -import { searchUsersReadModel } from "@/modules/users/read-models/searchUsersReadModel"; import { reinviteUserAction } from "@/modules/users/actions/reinviteUser"; -import { createFileRoute, redirect } from "@tanstack/react-router"; -import { createServerFn } from "@tanstack/react-start"; +import { createFileRoute } from "@tanstack/react-router"; import { withDocumentTitle } from "@/documentTitle"; +import { loadAdminUsersPage } from "@/ui/admin/serverFns/loadAdminUsersPage"; +import { routerGuard } from "@/modules/access/routerGuard"; +import { Policy } from "@/modules/access"; const LIMIT = 20; +const policy = new Policy({ systemRoles: [Policy.SystemRole.Admin] }); const schema = z.object({ page: z.coerce.number().int().default(1), @@ -32,30 +34,14 @@ const schema = z.object({ export const Route = createFileRoute("/_main/admin/_main/users/")({ validateSearch: schema, loaderDeps: ({ search }) => search, - loader: ({ deps }) => loaderFn({ data: deps }), + beforeLoad: ({ context }) => { + routerGuard({ context: context.auth, policy }); + }, + loader: ({ deps }) => loadAdminUsersPage({ data: { ...deps, limit: LIMIT } }), head: () => withDocumentTitle("Users | Admin"), component: AdminUsersPage, }); -const loaderFn = createServerFn() - .inputValidator(schema) - .handler(async ({ data }) => { - if (data.page <= 0) { - throw redirect({ to: "/admin/users", search: { page: 1 } }); - } - - const { page: users, total } = await searchUsersReadModel({ - page: data.page - 1, - limit: LIMIT, - }); - - if (users.length === 0 && data.page !== 1) { - throw redirect({ to: "/admin/users", search: { page: 1 } }); - } - - return { users, total }; - }); - function AdminUsersPage() { const { users, total } = Route.useLoaderData(); const t = useTranslations("AdminUsersPage"); diff --git a/src/routes/_main/admin/_main.users/invite.tsx b/src/ui/admin/routes/_main.users/invite.tsx similarity index 100% rename from src/routes/_main/admin/_main.users/invite.tsx rename to src/ui/admin/routes/_main.users/invite.tsx diff --git a/src/routes/_main/admin/languages.$code/exports.tsx b/src/ui/admin/routes/languages.$code/exports.tsx similarity index 93% rename from src/routes/_main/admin/languages.$code/exports.tsx rename to src/ui/admin/routes/languages.$code/exports.tsx index d53a9480..cd71b634 100644 --- a/src/routes/_main/admin/languages.$code/exports.tsx +++ b/src/ui/admin/routes/languages.$code/exports.tsx @@ -1,6 +1,6 @@ import LoadingSpinner from "@/components/LoadingSpinner"; import ViewTitle from "@/components/ViewTitle"; -import InterlinearExportPanel from "@/modules/export/ui/InterlinearExportPanel"; +import InterlinearExportPanel from "@/ui/admin/components/InterlinearExportPanel"; import { createFileRoute } from "@tanstack/react-router"; import { useTranslations } from "use-intl"; import { Suspense } from "react"; diff --git a/src/routes/_main/admin/languages.$code/index.tsx b/src/ui/admin/routes/languages.$code/index.tsx similarity index 74% rename from src/routes/_main/admin/languages.$code/index.tsx rename to src/ui/admin/routes/languages.$code/index.tsx index 12a1db44..bb64eaf6 100644 --- a/src/routes/_main/admin/languages.$code/index.tsx +++ b/src/ui/admin/routes/languages.$code/index.tsx @@ -1,36 +1,28 @@ import ViewTitle from "@/components/ViewTitle"; -import { createPolicyMiddleware, Policy } from "@/modules/access"; -import { getLanguageDashboardBaseData } from "@/modules/languages/actions/getLanguageDashboardBaseData"; -import { getLanguageDashboardRangeData } from "@/modules/languages/actions/getLanguageDashboardRangeData"; -import { getLanguageByCodeReadModel } from "@/modules/languages/read-models/getLanguageByCodeReadModel"; -import LanguageBookProgressDashboardCard from "@/modules/languages/ui/LanguageBookProgressDashboardCard"; -import { createFileRoute, notFound, useNavigate } from "@tanstack/react-router"; -import { createServerFn } from "@tanstack/react-start"; +import { getLanguageDashboardBaseData } from "@/ui/admin/serverFns/getLanguageDashboardBaseData"; +import { getLanguageDashboardRangeData } from "@/ui/admin/serverFns/getLanguageDashboardRangeData"; +import LanguageBookProgressDashboardCard from "@/ui/admin/components/LanguageBookProgressDashboardCard"; +import { createFileRoute, useNavigate } from "@tanstack/react-router"; import * as z from "zod"; import { withDocumentTitle } from "@/documentTitle"; -import LanguageUsersDashboardCard from "@/modules/languages/ui/LanguageUsersDashboardCard"; -import RangeToggle from "@/modules/languages/ui/RangeToggle"; -import { ActivityChartProvider } from "@/modules/languages/ui/ActivityChart"; +import LanguageUsersDashboardCard from "@/ui/admin/components/LanguageUsersDashboardCard"; +import RangeToggle from "@/ui/admin/components/RangeToggle"; +import { ActivityChartProvider } from "@/ui/admin/components/ActivityChart"; import Button from "@/components/Button"; import { Icon } from "@/components/Icon"; -import LanguageGlossApprovalDashboardCard from "@/modules/languages/ui/LanguageGlossApprovalDashboardCard"; - -const requestSchema = z.object({ code: z.string() }); +import LanguageGlossApprovalDashboardCard from "@/ui/admin/components/LanguageGlossApprovalDashboardCard"; +import { getAdminLanguageByCode } from "@/ui/admin/serverFns/getAdminLanguageByCode"; const searchSchema = z.object({ bookDetails: z.coerce.number().int().positive().optional(), range: z.enum(["30d", "6m"]).optional(), }); -const policy = new Policy({ - systemRoles: [Policy.SystemRole.Admin], -}); - export const Route = createFileRoute("/_main/admin/languages/$code/")({ validateSearch: searchSchema, loader: async ({ params }) => { const [languageData, baseData, range30dData, range6mData] = await Promise.all([ - loaderFn({ data: params }), + getAdminLanguageByCode({ data: params }), getLanguageDashboardBaseData({ data: params }), getLanguageDashboardRangeData({ data: { code: params.code, range: "30d" }, @@ -54,23 +46,6 @@ export const Route = createFileRoute("/_main/admin/languages/$code/")({ component: LanguageDashboardRoute, }); -const loaderFn = createServerFn() - .inputValidator(requestSchema) - .middleware([ - createPolicyMiddleware({ - policy, - languageCodeField: "code", - }), - ]) - .handler(async ({ data }) => { - const language = await getLanguageByCodeReadModel(data.code); - if (!language) { - throw notFound(); - } - - return { language }; - }); - function LanguageDashboardRoute() { const { language, books, members, contributions, activityByRange } = Route.useLoaderData(); diff --git a/src/routes/_main/admin/languages.$code/invite.tsx b/src/ui/admin/routes/languages.$code/invite.tsx similarity index 100% rename from src/routes/_main/admin/languages.$code/invite.tsx rename to src/ui/admin/routes/languages.$code/invite.tsx diff --git a/src/ui/admin/routes/languages.$code/route.tsx b/src/ui/admin/routes/languages.$code/route.tsx new file mode 100644 index 00000000..959e6960 --- /dev/null +++ b/src/ui/admin/routes/languages.$code/route.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { getAdminLanguageByCode } from "@/ui/admin/serverFns/getAdminLanguageByCode"; + +export const Route = createFileRoute("/_main/admin/languages/$code")({ + ssr: "data-only", + loader: ({ params }) => { + return getAdminLanguageByCode({ data: params }); + }, +}); diff --git a/src/routes/_main/admin/languages.$code/settings.tsx b/src/ui/admin/routes/languages.$code/settings.tsx similarity index 87% rename from src/routes/_main/admin/languages.$code/settings.tsx rename to src/ui/admin/routes/languages.$code/settings.tsx index 0c56a1e5..d7273571 100644 --- a/src/routes/_main/admin/languages.$code/settings.tsx +++ b/src/ui/admin/routes/languages.$code/settings.tsx @@ -1,4 +1,3 @@ -import { BibleClient } from "@gracious.tech/fetch-client"; import { ButtonSelectorInput, ButtonSelectorOption, @@ -13,29 +12,24 @@ import LoadingSpinner from "@/components/LoadingSpinner"; import SortableMultiselectInput from "@/components/SortableMultiselectInput"; import TextInput from "@/components/TextInput"; import ViewTitle from "@/components/ViewTitle"; -import { createPolicyMiddleware, Policy } from "@/modules/access"; +import { Policy } from "@/modules/access"; import { routerGuard } from "@/modules/access/routerGuard"; import { updateLanguageSettings } from "@/modules/languages/actions/updateLanguageSettings"; import { MachineGlossStrategy } from "@/modules/languages/model"; -import AIGlossesImportForm from "@/modules/translation/ui/AIGlossesImportForm"; -import { getAllLanguagesReadModel } from "@/modules/languages/read-models/getAllLanguagesReadModel"; -import { getLanguageSettingsReadModel } from "@/modules/languages/read-models/getLanguageSettingsReadModel"; -import SavingIndicator from "@/modules/languages/ui/SavingIndicator"; +import AIGlossesImportForm from "@/ui/admin/components/AIGlossesImportForm"; +import SavingIndicator from "@/ui/admin/components/SavingIndicator"; import { fontMap } from "@/fonts"; -import { createFileRoute, notFound } from "@tanstack/react-router"; -import { createServerFn } from "@tanstack/react-start"; +import { createFileRoute } from "@tanstack/react-router"; import { Suspense } from "react"; import { useTranslations } from "use-intl"; -import * as z from "zod"; import { withDocumentTitle } from "@/documentTitle"; +import { loadLanguageSettings } from "@/ui/admin/serverFns/loadLanguageSettings"; const policy = new Policy({ systemRoles: [Policy.SystemRole.Admin], languageMember: true, }); -const loaderRequestSchema = z.object({ code: z.string() }); - export const Route = createFileRoute("/_main/admin/languages/$code/settings")({ beforeLoad: ({ context, params }) => { routerGuard({ @@ -44,34 +38,12 @@ export const Route = createFileRoute("/_main/admin/languages/$code/settings")({ languageCode: params.code, }); }, - loader: ({ params }) => loaderFn({ data: params }), + loader: ({ params }) => loadLanguageSettings({ data: params }), head: ({ loaderData }) => withDocumentTitle(`Settings | ${loaderData?.languageSettings.englishName}`), component: LanguageSettingsRoute, }); -const loaderFn = createServerFn() - .inputValidator(loaderRequestSchema) - .middleware([ - createPolicyMiddleware({ - policy, - languageCodeField: "code", - }), - ]) - .handler(async ({ data }) => { - const [languageSettings, languages, translations] = await Promise.all([ - getLanguageSettingsReadModel(data.code), - getAllLanguagesReadModel(), - fetchTranslations(data.code), - ]); - - if (!languageSettings) { - throw notFound(); - } - - return { languageSettings, languages, translations }; - }); - function LanguageSettingsRoute() { const t = useTranslations("LanguageSettingsPage"); const { languageSettings, languages, translations } = Route.useLoaderData(); @@ -346,19 +318,3 @@ function LanguageSettingsRoute() { ); } - -async function fetchTranslations( - languageCode: string, -): Promise<{ id: string; name: string }[]> { - const client = new BibleClient(); - const collection = await client.fetch_collection(); - const translations = collection.get_translations({ - sort_by_year: true, - language: languageCode, - }); - - return translations.map(({ id, name_english, name_local }) => ({ - id, - name: name_local ? name_local : name_english, - })); -} diff --git a/src/ui/admin/serverFns/getAIGlossesImportFormData.ts b/src/ui/admin/serverFns/getAIGlossesImportFormData.ts new file mode 100644 index 00000000..f1ce72ad --- /dev/null +++ b/src/ui/admin/serverFns/getAIGlossesImportFormData.ts @@ -0,0 +1,35 @@ +import * as z from "zod"; +import { createPolicyMiddleware, Policy } from "@/modules/access"; +import { getAIGlossImportJobReadModel } from "@/ui/admin/readModels/getAIGlossImportJobReadModel"; +import { getAIGlossImportLanguagesReadModel } from "@/ui/admin/readModels/getAIGlossImportLanguagesReadModel"; +import { createServerFn } from "@tanstack/react-start"; + +const requestSchema = z.object({ + code: z.string(), +}); + +const policy = new Policy({ + systemRoles: [Policy.SystemRole.Admin], + languageMember: true, +}); + +export const getAIGlossesImportFormData = createServerFn() + .inputValidator(requestSchema) + .middleware([ + createPolicyMiddleware({ + policy, + languageCodeField: "code", + }), + ]) + .handler(async ({ data }) => { + const [job, availableLanguages] = await Promise.all([ + getAIGlossImportJobReadModel(data.code), + getAIGlossImportLanguagesReadModel(), + ]); + + const language = availableLanguages.find( + (entry) => entry.code === data.code, + ); + + return { job, languageAvailable: !!language }; + }); diff --git a/src/ui/admin/serverFns/getActiveJobs.ts b/src/ui/admin/serverFns/getActiveJobs.ts new file mode 100644 index 00000000..2ca09cf6 --- /dev/null +++ b/src/ui/admin/serverFns/getActiveJobs.ts @@ -0,0 +1,53 @@ +import { sql } from "kysely"; +import { createPolicyMiddleware, Policy } from "@/modules/access"; +import { getDb } from "@/db"; +import { EXPORT_JOB_TYPES } from "@/modules/export/jobs/jobTypes"; +import { createServerFn } from "@tanstack/react-start"; + +const policy = new Policy({ + systemRoles: [Policy.SystemRole.Admin], +}); + +export const getActiveJobs = createServerFn() + .middleware([ + createPolicyMiddleware({ + policy, + }), + ]) + .handler(async () => { + const exportJobs = await getDb() + .with("export_job", (db) => + db + .selectFrom("job") + .where("type", "=", EXPORT_JOB_TYPES.EXPORT_GLOSSES) + .orderBy("created_at", "desc") + .select(["id"]) + .limit(1), + ) + .selectFrom("job") + .where((eb) => + eb.or([ + eb( + "job.parent_job_id", + "=", + eb.selectFrom("export_job").select("id"), + ), + eb("job.id", "=", eb.selectFrom("export_job").select("id")), + ]), + ) + .orderBy("created_at") + .select([ + "job.id", + "job.type", + "job.status", + "job.updated_at as updatedAt", + "job.created_at as createdAt", + (eb) => + sql>`${eb.ref("job.payload")}->'languageCodes'`.as( + "languages", + ), + ]) + .execute(); + + return { exportJobs }; + }); diff --git a/src/routes/_main/admin/languages.$code/route.tsx b/src/ui/admin/serverFns/getAdminLanguageByCode.ts similarity index 72% rename from src/routes/_main/admin/languages.$code/route.tsx rename to src/ui/admin/serverFns/getAdminLanguageByCode.ts index a7bc72b8..2d6971bd 100644 --- a/src/routes/_main/admin/languages.$code/route.tsx +++ b/src/ui/admin/serverFns/getAdminLanguageByCode.ts @@ -1,8 +1,8 @@ +import * as z from "zod"; import { createPolicyMiddleware, Policy } from "@/modules/access"; import { getLanguageByCodeReadModel } from "@/modules/languages/read-models/getLanguageByCodeReadModel"; -import { createFileRoute, notFound } from "@tanstack/react-router"; +import { notFound } from "@tanstack/react-router"; import { createServerFn } from "@tanstack/react-start"; -import * as z from "zod"; const policy = new Policy({ systemRoles: [Policy.SystemRole.Admin], @@ -11,14 +11,7 @@ const policy = new Policy({ const requestSchema = z.object({ code: z.string() }); -export const Route = createFileRoute("/_main/admin/languages/$code")({ - ssr: "data-only", - loader: ({ params }) => { - return loaderFn({ data: params }); - }, -}); - -const loaderFn = createServerFn() +export const getAdminLanguageByCode = createServerFn() .inputValidator(requestSchema) .middleware([ createPolicyMiddleware({ diff --git a/src/ui/admin/serverFns/getInterlinearExportPanelData.ts b/src/ui/admin/serverFns/getInterlinearExportPanelData.ts new file mode 100644 index 00000000..b7ff3d40 --- /dev/null +++ b/src/ui/admin/serverFns/getInterlinearExportPanelData.ts @@ -0,0 +1,30 @@ +import * as z from "zod"; +import { createPolicyMiddleware, Policy } from "@/modules/access"; +import exportJobQueryService from "@/modules/export/data-access/ExportJobQueryService"; +import { createServerFn } from "@tanstack/react-start"; + +const requestSchema = z.object({ + languageCode: z.string().min(1), +}); + +const policy = new Policy({ + systemRoles: [Policy.SystemRole.Admin], + languageMember: true, +}); + +export const getInterlinearExportPanelData = createServerFn() + .inputValidator(requestSchema) + .middleware([ + createPolicyMiddleware({ + policy, + }), + ]) + .handler(async ({ data }) => { + // TODO: move these to read models + const [jobs, pendingJob] = await Promise.all([ + exportJobQueryService.findRecentForLanguage(data.languageCode), + exportJobQueryService.findPendingForLanguage(data.languageCode), + ]); + + return { jobs, pendingJob }; + }); diff --git a/src/modules/languages/actions/getLanguageDashboardBaseData.ts b/src/ui/admin/serverFns/getLanguageDashboardBaseData.ts similarity index 87% rename from src/modules/languages/actions/getLanguageDashboardBaseData.ts rename to src/ui/admin/serverFns/getLanguageDashboardBaseData.ts index 49b217e2..c70c3813 100644 --- a/src/modules/languages/actions/getLanguageDashboardBaseData.ts +++ b/src/ui/admin/serverFns/getLanguageDashboardBaseData.ts @@ -2,12 +2,16 @@ import { createPolicyMiddleware, Policy } from "@/modules/access"; import { getLanguageByCodeReadModel } from "@/modules/languages/read-models/getLanguageByCodeReadModel"; import { getLanguageDashboardBooksReadModel, - getLanguageDashboardContributionsReadModel, - getLanguageDashboardMembersReadModel, type LanguageDashboardBookReadModel, - type LanguageDashboardContributionReadModel, +} from "@/ui/admin/readModels/getLanguageDashboardBooksReadModel"; +import { + getLanguageDashboardMembersReadModel, type LanguageDashboardMemberReadModel, -} from "@/modules/reporting"; +} from "@/ui/admin/readModels/getLanguageDashboardMembersReadModel"; +import { + getLanguageDashboardContributionsReadModel, + type LanguageDashboardContributionReadModel, +} from "@/ui/admin/readModels/getLanguageDashboardContributionsReadModel"; import { notFound } from "@tanstack/react-router"; import { createServerFn } from "@tanstack/react-start"; import * as z from "zod"; diff --git a/src/modules/languages/actions/getLanguageDashboardRangeData.ts b/src/ui/admin/serverFns/getLanguageDashboardRangeData.ts similarity index 89% rename from src/modules/languages/actions/getLanguageDashboardRangeData.ts rename to src/ui/admin/serverFns/getLanguageDashboardRangeData.ts index a97b2022..f5eb040d 100644 --- a/src/modules/languages/actions/getLanguageDashboardRangeData.ts +++ b/src/ui/admin/serverFns/getLanguageDashboardRangeData.ts @@ -1,11 +1,13 @@ import { createPolicyMiddleware, Policy } from "@/modules/access"; import { getLanguageByCodeReadModel } from "@/modules/languages/read-models/getLanguageByCodeReadModel"; import { - getLanguageApprovalActivityReadModel, getLanguageDashboardActivityReadModel, - LanguageApprovalActivityReadModel, type LanguageDashboardActivityEntryReadModel, -} from "@/modules/reporting"; +} from "@/ui/admin/readModels/getLanguageDashboardActivityReadModel"; +import { + getLanguageApprovalActivityReadModel, + type LanguageApprovalActivityReadModel, +} from "@/ui/admin/readModels/getLanguageApprovalActivityReadModel"; import { notFound } from "@tanstack/react-router"; import { createServerFn } from "@tanstack/react-start"; import * as z from "zod"; diff --git a/src/ui/admin/serverFns/loadAdminLanguagesPage.ts b/src/ui/admin/serverFns/loadAdminLanguagesPage.ts new file mode 100644 index 00000000..bfe97510 --- /dev/null +++ b/src/ui/admin/serverFns/loadAdminLanguagesPage.ts @@ -0,0 +1,32 @@ +import * as z from "zod"; +import { createPolicyMiddleware, Policy } from "@/modules/access"; +import { searchLanguagesReadModel } from "@/ui/admin/readModels/searchLanguagesReadModel"; +import { redirect } from "@tanstack/react-router"; +import { createServerFn } from "@tanstack/react-start"; + +const policy = new Policy({ systemRoles: [Policy.SystemRole.Admin] }); + +const schema = z.object({ + page: z.coerce.number().int().default(1), + limit: z.coerce.number().int().default(20), +}); + +export const loadAdminLanguagesPage = createServerFn() + .inputValidator(schema) + .middleware([createPolicyMiddleware({ policy })]) + .handler(async ({ data }) => { + if (data.page <= 0) { + throw redirect({ to: "/admin/languages", search: { page: 1 } }); + } + + const { page: languages, total } = await searchLanguagesReadModel({ + page: data.page - 1, + limit: data.limit, + }); + + if (languages.length === 0 && data.page !== 1) { + throw redirect({ to: "/admin/languages", search: { page: 1 } }); + } + + return { languages, total }; + }); diff --git a/src/ui/admin/serverFns/loadAdminUsersPage.ts b/src/ui/admin/serverFns/loadAdminUsersPage.ts new file mode 100644 index 00000000..097fb9ee --- /dev/null +++ b/src/ui/admin/serverFns/loadAdminUsersPage.ts @@ -0,0 +1,32 @@ +import * as z from "zod"; +import { searchUsersReadModel } from "@/ui/admin/readModels/searchUsersReadModel"; +import { redirect } from "@tanstack/react-router"; +import { createServerFn } from "@tanstack/react-start"; +import { createPolicyMiddleware, Policy } from "@/modules/access"; + +const policy = new Policy({ systemRoles: [Policy.SystemRole.Admin] }); + +const schema = z.object({ + page: z.coerce.number().int().default(1), + limit: z.coerce.number().int().default(20), +}); + +export const loadAdminUsersPage = createServerFn() + .inputValidator(schema) + .middleware([createPolicyMiddleware({ policy })]) + .handler(async ({ data }) => { + if (data.page <= 0) { + throw redirect({ to: "/admin/users", search: { page: 1 } }); + } + + const { page: users, total } = await searchUsersReadModel({ + page: data.page - 1, + limit: data.limit, + }); + + if (users.length === 0 && data.page !== 1) { + throw redirect({ to: "/admin/users", search: { page: 1 } }); + } + + return { users, total }; + }); diff --git a/src/ui/admin/serverFns/loadLanguageSettings.ts b/src/ui/admin/serverFns/loadLanguageSettings.ts new file mode 100644 index 00000000..ea67779c --- /dev/null +++ b/src/ui/admin/serverFns/loadLanguageSettings.ts @@ -0,0 +1,52 @@ +import * as z from "zod"; +import { createPolicyMiddleware, Policy } from "@/modules/access"; +import { getAllLanguagesReadModel } from "@/ui/admin/readModels/getAllLanguagesReadModel"; +import { getLanguageSettingsReadModel } from "@/ui/admin/readModels/getLanguageSettingsReadModel"; +import { BibleClient } from "@gracious.tech/fetch-client"; +import { notFound } from "@tanstack/react-router"; +import { createServerFn } from "@tanstack/react-start"; + +const policy = new Policy({ + systemRoles: [Policy.SystemRole.Admin], + languageMember: true, +}); + +const loaderRequestSchema = z.object({ code: z.string() }); + +export const loadLanguageSettings = createServerFn() + .inputValidator(loaderRequestSchema) + .middleware([ + createPolicyMiddleware({ + policy, + languageCodeField: "code", + }), + ]) + .handler(async ({ data }) => { + const [languageSettings, languages, translations] = await Promise.all([ + getLanguageSettingsReadModel(data.code), + getAllLanguagesReadModel(), + fetchTranslations(data.code), + ]); + + if (!languageSettings) { + throw notFound(); + } + + return { languageSettings, languages, translations }; + }); + +async function fetchTranslations( + languageCode: string, +): Promise<{ id: string; name: string }[]> { + const client = new BibleClient(); + const collection = await client.fetch_collection(); + const translations = collection.get_translations({ + sort_by_year: true, + language: languageCode, + }); + + return translations.map(({ id, name_english, name_local }) => ({ + id, + name: name_local ? name_local : name_english, + })); +}