From 6ba04effcfe336a118c9159977692436495e18c5 Mon Sep 17 00:00:00 2001 From: MangoPig Date: Fri, 19 Jun 2026 17:39:39 +0100 Subject: [PATCH] Feat: Hydrate shell from app state --- .../components/shell/AppShell/AppShell.tsx | 14 +- .../DepartmentSelector/DepartmentSelector.tsx | 31 +- .../components/shell/LeftRail/LeftRail.tsx | 12 +- .../shell/MobileBottomNav/MobileBottomNav.tsx | 9 +- .../MobileWorkspaceBrowser.tsx | 23 +- .../shell/ProjectSelector/ProjectSelector.tsx | 17 +- .../shell/ServerDock/ServerDock.tsx | 21 +- .../components/shell/TopBar/ProfileMenu.tsx | 13 +- .../WorkspaceSidebar/WorkspaceSidebar.tsx | 12 +- .../shell/data/app-shell.context.tsx | 317 +++++++++++ .../src/components/shell/data/shell.data.ts | 4 +- .../WorkspaceHome/WorkspaceHome.module.scss | 403 +++++++++++++- .../WorkspaceHome/WorkspaceHome.tsx | 494 ++++++++++++++++-- Frontend/src/global.d.ts | 8 + Frontend/src/lib/api.ts | 11 + 15 files changed, 1258 insertions(+), 131 deletions(-) create mode 100644 Frontend/src/components/shell/data/app-shell.context.tsx create mode 100644 Frontend/src/lib/api.ts diff --git a/Frontend/src/components/shell/AppShell/AppShell.tsx b/Frontend/src/components/shell/AppShell/AppShell.tsx index f529a0a..9365125 100644 --- a/Frontend/src/components/shell/AppShell/AppShell.tsx +++ b/Frontend/src/components/shell/AppShell/AppShell.tsx @@ -3,6 +3,7 @@ import { createSignal, onCleanup, onMount, Show, type JSX } from "solid-js"; import { getDocumentTheme, setTheme, type Theme } from "../../../theme/runtime"; import { WorkspaceHome } from "../../workspace-home/WorkspaceHome/WorkspaceHome"; +import { AppShellDataProvider, useAppShellData } from "../data/app-shell.context"; import { LeftRail } from "../LeftRail/LeftRail"; import { MobileBottomNav } from "../MobileBottomNav/MobileBottomNav"; import { MobileWorkspaceBrowser } from "../MobileWorkspaceBrowser/MobileWorkspaceBrowser"; @@ -16,13 +17,14 @@ import styles from "./AppShell.module.scss"; type MobileWorkspaceView = "notifications" | "profile" | null; const MOBILE_VIEWPORT_QUERY = "(max-width: 48rem)"; -export const AppShell = (): JSX.Element => { +const AppShellContent = (): JSX.Element => { const [themeState, setThemeState] = createSignal("light"); const [isRailCollapsed, setIsRailCollapsed] = createSignal(false); const [isSidebarCollapsed, setIsSidebarCollapsed] = createSignal(false); const [isMobileViewport, setIsMobileViewport] = createSignal(false); const [isMobileWorkspaceBrowserOpen, setIsMobileWorkspaceBrowserOpen] = createSignal(false); const [activeMobileWorkspaceView, setActiveMobileWorkspaceView] = createSignal(null); + const appShellData = useAppShellData(); onMount((): void => { setThemeState(getDocumentTheme()); @@ -79,7 +81,7 @@ export const AppShell = (): JSX.Element => { }; return ( -
+
{
); }; + +export const AppShell = (): JSX.Element => { + return ( + + + + ); +}; diff --git a/Frontend/src/components/shell/DepartmentSelector/DepartmentSelector.tsx b/Frontend/src/components/shell/DepartmentSelector/DepartmentSelector.tsx index 360a18a..43d6fd4 100644 --- a/Frontend/src/components/shell/DepartmentSelector/DepartmentSelector.tsx +++ b/Frontend/src/components/shell/DepartmentSelector/DepartmentSelector.tsx @@ -1,22 +1,29 @@ -import { For, createSignal, onCleanup, onMount, type JSX } from "solid-js"; +import { For, createEffect, createSignal, onCleanup, onMount, type JSX } from "solid-js"; import { ChevronDown } from "../../../lib/icons"; -import { activeDepartment, departmentItems, type DepartmentItem } from "../data/shell.data"; +import { useAppShellData } from "../data/app-shell.context"; +import { type DepartmentItem } from "../data/shell.data"; import styles from "./DepartmentSelector.module.scss"; -const defaultDepartment = departmentItems.find((item) => item.id === activeDepartment.id) ?? departmentItems[0]; -const defaultTeamName = departmentItems - .find((item) => item.id === activeDepartment.id) - ?.teams.find((teamName) => teamName === activeDepartment.teamName) - ?? defaultDepartment?.teams[0] - ?? ""; - export const DepartmentSelector = (): JSX.Element => { + const appShellData = useAppShellData(); const [isOpen, setIsOpen] = createSignal(false); - const [selectedDepartment, setSelectedDepartment] = createSignal(defaultDepartment); - const [selectedTeamName, setSelectedTeamName] = createSignal(defaultTeamName); + const [selectedDepartment, setSelectedDepartment] = createSignal(appShellData.departmentItems()[0] ?? appShellData.activeDepartment()); + const [selectedTeamName, setSelectedTeamName] = createSignal(appShellData.activeDepartment().teamName); let rootRef: HTMLDivElement | undefined; + createEffect(() => { + const activeDepartment = appShellData.activeDepartment(); + const matchingDepartment = appShellData.departmentItems().find((item) => item.id === activeDepartment.id) ?? appShellData.departmentItems()[0]; + + if (!matchingDepartment) { + return; + } + + setSelectedDepartment(matchingDepartment); + setSelectedTeamName(activeDepartment.teamName || matchingDepartment.teams[0] || ""); + }); + onMount(() => { const handlePointerDown = (event: PointerEvent): void => { if (!isOpen()) return; @@ -66,7 +73,7 @@ export const DepartmentSelector = (): JSX.Element => {
Departments - + {(item): JSX.Element => ( + <> +
+
+
+ +
+ +
+ {breadcrumb()} +
+ + -
- {breadcrumb()} +
+ Bootstrap +

{appShellData.activeServer().name}

+ +
+ +
+
+
+
+ + + +
+ + + + ); }; diff --git a/Frontend/src/global.d.ts b/Frontend/src/global.d.ts index a11339c..26bcbaa 100644 --- a/Frontend/src/global.d.ts +++ b/Frontend/src/global.d.ts @@ -1,3 +1,11 @@ // Path: Frontend/src/global.d.ts /// + +interface ImportMetaEnv { + readonly VITE_API_BASE_URL?: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/Frontend/src/lib/api.ts b/Frontend/src/lib/api.ts new file mode 100644 index 0000000..a52a64c --- /dev/null +++ b/Frontend/src/lib/api.ts @@ -0,0 +1,11 @@ +// Path: Frontend/src/lib/api.ts + +export const resolveAPIBase = (): string => { + const configuredBase = import.meta.env.VITE_API_BASE_URL?.trim(); + + if (!configuredBase) { + return "/v1"; + } + + return configuredBase.replace(/\/$/, ""); +};