AcadeDoc/apps/client/src/App.tsx
Corentin 60d64822e4 fix(acadenice): include parent-child edges in graph + space-scope view — R4.6
Graph view was empty for users who built page hierarchies (sub-pages) but had
not placed any wikilinks. The graph service now queries pages.parent_page_id
as a second edge source (type: parent_child) and merges it with acadenice_backlink
edges, so hierarchy-only workspaces display meaningful graphs immediately.

- dto: added parent_child to LinkType enum; added slug field to GraphNode
- service: loadParentChildEdges (permission-filtered SQL), parallel merge with loadEdges
- controller: always appends parent_child to the effective type list
- client: graph-client.ts typed for parent_child and slug; graph-canvas renders
  dashed grey lines for parent_child vs solid brand lines for wikilinks; legend
  with aria-label; title/slug/untitled fallback chain for node labels
- space sidebar: Graph menu item -> /s/:spaceSlug/graph
- new route: /s/:spaceSlug/graph -> SpaceGraph page (injects spaceId filter)
- i18n: en-US + fr-FR keys for legend and space graph
- tests: 42 server + 59 client, all green; 10 new R4.6 tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:14:28 +02:00

183 lines
9.5 KiB
TypeScript

import { Navigate, Route, Routes } from "react-router-dom";
import SetupWorkspace from "@/pages/auth/setup-workspace.tsx";
import LoginPage from "@/pages/auth/login";
import Home from "@/pages/dashboard/home";
import Page from "@/pages/page/page";
import AccountSettings from "@/pages/settings/account/account-settings";
import WorkspaceMembers from "@/pages/settings/workspace/workspace-members";
import WorkspaceSettings from "@/pages/settings/workspace/workspace-settings";
import WorkspaceBranding from "@/pages/settings/workspace/workspace-branding";
import Groups from "@/pages/settings/group/groups";
import GroupInfo from "./pages/settings/group/group-info";
import Spaces from "@/pages/settings/space/spaces.tsx";
import { Error404 } from "@/components/ui/error-404.tsx";
import AccountPreferences from "@/pages/settings/account/account-preferences.tsx";
import SpaceHome from "@/pages/space/space-home.tsx";
import PageRedirect from "@/pages/page/page-redirect.tsx";
import Layout from "@/components/layouts/global/layout.tsx";
import InviteSignup from "@/pages/auth/invite-signup.tsx";
import ForgotPassword from "@/pages/auth/forgot-password.tsx";
import PasswordReset from "./pages/auth/password-reset";
import Billing from "@/ee/billing/pages/billing.tsx";
import CloudLogin from "@/ee/pages/cloud-login.tsx";
import CreateWorkspace from "@/ee/pages/create-workspace.tsx";
import { isCloud } from "@/lib/config.ts";
import { useTranslation } from "react-i18next";
import Security from "@/ee/security/pages/security.tsx";
import License from "@/ee/licence/pages/license.tsx";
import { useRedirectToCloudSelect } from "@/ee/hooks/use-redirect-to-cloud-select.tsx";
import SharedPage from "@/pages/share/shared-page.tsx";
import PdfRenderPage from "@/ee/pdf-export/pdf-render-page.tsx";
import Shares from "@/pages/settings/shares/shares.tsx";
import ShareLayout from "@/features/share/components/share-layout.tsx";
import ShareRedirect from "@/pages/share/share-redirect.tsx";
import { useTrackOrigin } from "@/hooks/use-track-origin";
import SpacesPage from "@/pages/spaces/spaces.tsx";
import { MfaChallengePage } from "@/ee/mfa/pages/mfa-challenge-page";
import { MfaSetupRequiredPage } from "@/ee/mfa/pages/mfa-setup-required-page";
import SpaceTrash from "@/pages/space/space-trash.tsx";
import UserApiKeys from "@/ee/api-key/pages/user-api-keys";
import WorkspaceApiKeys from "@/ee/api-key/pages/workspace-api-keys";
import AiSettings from "@/ee/ai/pages/ai-settings.tsx";
import AuditLogs from "@/ee/audit/pages/audit-logs.tsx";
import VerifiedPages from "@/ee/page-verification/pages/verified-pages.tsx";
import TemplateList from "@/ee/template/pages/template-list";
import TemplateEditor from "@/ee/template/pages/template-editor";
import FavoritesPage from "@/pages/favorites/favorites-page";
import AiChat from "@/ee/ai-chat/pages/ai-chat.tsx";
import VerifyEmail from "@/ee/pages/verify-email.tsx";
import RolesListPage from "@/features/acadenice/rbac/pages/roles-list.page";
import RoleDetailPage from "@/features/acadenice/rbac/pages/role-detail.page";
import UserRolesPanelPage from "@/features/acadenice/rbac/pages/user-roles-panel";
// Acadenice R3.3 — custom slash commands admin page
import SlashCommandsPage from "@/features/acadenice/slash-commands-admin/pages/slash-commands-page";
// Acadenice R3.5.2 — knowledge graph view
import GraphPage from "@/features/acadenice/graph/pages/graph-page";
// Acadenice R3.6 — page templates
import TemplatesAdminPage from "@/features/acadenice/templates-admin/pages/templates-page";
// Acadenice R3.7 — mention notifications
import AcadeniceNotificationsPage from "@/features/acadenice/notifications/pages/notifications-page";
import NotificationPreferencesPage from "@/features/acadenice/notifications/pages/notification-preferences-page";
// Acadenice R4.3 — Web Clipper token management
import ClipperTokensPage from "@/features/acadenice/clipper/pages/clipper-tokens-page";
// Acadenice R4.5 — EE replacement: audit log, API keys, OIDC security status
import AcadeniceAuditLogPage from "@/features/acadenice/audit-log/pages/audit-log-page";
import AcadeniceApiKeysPage from "@/features/acadenice/api-keys/pages/api-keys-page";
import AcadeniceSecurityPage from "@/features/acadenice/oidc-status/pages/security-page";
// Acadenice R4.6 — space-scoped graph view
import SpaceGraph from "@/pages/space/space-graph";
export default function App() {
const { t } = useTranslation();
useRedirectToCloudSelect();
useTrackOrigin();
return (
<>
<Routes>
<Route index element={<Navigate to="/home" />} />
<Route path={"/login"} element={<LoginPage />} />
<Route path={"/invites/:invitationId"} element={<InviteSignup />} />
<Route path={"/forgot-password"} element={<ForgotPassword />} />
<Route path={"/password-reset"} element={<PasswordReset />} />
<Route path={"/login/mfa"} element={<MfaChallengePage />} />
<Route path={"/login/mfa/setup"} element={<MfaSetupRequiredPage />} />
{!isCloud() && (
<Route path={"/setup/register"} element={<SetupWorkspace />} />
)}
{isCloud() && (
<>
<Route path={"/create"} element={<CreateWorkspace />} />
<Route path={"/select"} element={<CloudLogin />} />
<Route path={"/verify-email"} element={<VerifyEmail />} />
</>
)}
<Route element={<ShareLayout />}>
<Route
path={"/share/:shareId/p/:pageSlug"}
element={<SharedPage />}
/>
<Route path={"/share/p/:pageSlug"} element={<SharedPage />} />
</Route>
<Route path={"/pdf-render/:pageId"} element={<PdfRenderPage />} />
<Route path={"/share/:shareId"} element={<ShareRedirect />} />
<Route path={"/p/:pageSlug"} element={<PageRedirect />} />
<Route element={<Layout />}>
<Route path={"/home"} element={<Home />} />
{/* Acadenice R3.5.2 — knowledge graph */}
<Route path={"/graph"} element={<GraphPage />} />
{/* Acadenice R3.7 — notifications full page */}
<Route path={"/notifications"} element={<AcadeniceNotificationsPage />} />
<Route path={"/ai"} element={<AiChat />} />
<Route path={"/ai/chat/:chatId"} element={<AiChat />} />
<Route path={"/spaces"} element={<SpacesPage />} />
<Route path={"/favorites"} element={<FavoritesPage />} />
<Route path={"/templates"} element={<TemplateList />} />
<Route
path={"/templates/:templateId"}
element={<TemplateEditor />}
/>
<Route path={"/s/:spaceSlug"} element={<SpaceHome />} />
<Route path={"/s/:spaceSlug/trash"} element={<SpaceTrash />} />
{/* Acadenice R4.6 — space-scoped graph view */}
<Route path={"/s/:spaceSlug/graph"} element={<SpaceGraph />} />
<Route
path={"/s/:spaceSlug/p/:pageSlug"}
element={<Page />}
/>
<Route path={"/settings"}>
<Route path={"account/profile"} element={<AccountSettings />} />
<Route
path={"account/preferences"}
element={<AccountPreferences />}
/>
<Route path={"account/api-keys"} element={<UserApiKeys />} />
<Route path={"workspace"} element={<WorkspaceSettings />} />
<Route path={"members"} element={<WorkspaceMembers />} />
<Route path={"api-keys"} element={<WorkspaceApiKeys />} />
<Route path={"groups"} element={<Groups />} />
<Route path={"groups/:groupId"} element={<GroupInfo />} />
<Route path={"spaces"} element={<Spaces />} />
<Route path={"sharing"} element={<Shares />} />
<Route path={"security"} element={<Security />} />
<Route path={"ai"} element={<AiSettings />} />
<Route path={"ai/mcp"} element={<AiSettings />} />
<Route path={"audit"} element={<AuditLogs />} />
<Route path={"verifications"} element={<VerifiedPages />} />
{/* Acadenice R2.2 — RBAC dynamique */}
<Route path={"roles"} element={<RolesListPage />} />
<Route path={"roles/:id"} element={<RoleDetailPage />} />
<Route
path={"users/:userId/roles"}
element={<UserRolesPanelPage />}
/>
{/* Acadenice R3.3 — custom slash commands admin */}
<Route path={"slash-commands"} element={<SlashCommandsPage />} />
{/* Acadenice R3.6 — page templates */}
<Route path={"templates"} element={<TemplatesAdminPage />} />
{/* Acadenice R3.7 — notification preferences */}
<Route path={"notifications"} element={<NotificationPreferencesPage />} />
{/* Acadenice R4.3 — Web Clipper token management */}
<Route path={"clipper-tokens"} element={<ClipperTokensPage />} />
{/* Acadenice R4.4 — Workspace branding */}
<Route path={"branding"} element={<WorkspaceBranding />} />
{/* Acadenice R4.5 — open source EE replacements */}
<Route path={"acadenice/audit-log"} element={<AcadeniceAuditLogPage />} />
<Route path={"acadenice/api-keys"} element={<AcadeniceApiKeysPage />} />
<Route path={"acadenice/security"} element={<AcadeniceSecurityPage />} />
{!isCloud() && <Route path={"license"} element={<License />} />}
{isCloud() && <Route path={"billing"} element={<Billing />} />}
</Route>
</Route>
<Route path="*" element={<Error404 />} />
</Routes>
</>
);
}