mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-13 13:18:56 +02:00
- Add explicit type="button" to 61 <button> elements missing the attribute - Replace useContext() with React 19 use() across 16 context consumers - Replace [...arr].sort() with arr.toSorted() in 12 web/desktop files (mobile excluded — Hermes lacks toSorted support) - Fix rules-of-hooks violation: useSidebar try/catch → useSidebarSafe null check - Fix nested component definition: useMemo wrapping HeaderRight → useCallback - Fix missing ARIA: add aria-expanded + aria-controls to combobox in create-squad React Doctor score: 23 → 30. No behavioral changes, no business logic modified. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
71 lines
2.2 KiB
TypeScript
71 lines
2.2 KiB
TypeScript
"use client";
|
|
|
|
import { createContext, use, type ReactNode } from "react";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import type { Workspace } from "../types";
|
|
import { workspaceListOptions } from "../workspace/queries";
|
|
import { paths, type WorkspacePaths } from "./paths";
|
|
|
|
/**
|
|
* Context for the current workspace slug (read from URL by the platform layer).
|
|
*
|
|
* apps/web populates this from Next.js `params.workspaceSlug` in
|
|
* [workspaceSlug]/layout.tsx. apps/desktop populates it from react-router's
|
|
* `useParams()` in the workspace route layout.
|
|
*
|
|
* packages/core/ cannot import next/navigation or react-router-dom directly,
|
|
* so the slug arrives via this Context — mirroring how WorkspaceIdProvider
|
|
* already works for workspace IDs.
|
|
*/
|
|
const WorkspaceSlugContext = createContext<string | null>(null);
|
|
|
|
export function WorkspaceSlugProvider({
|
|
slug,
|
|
children,
|
|
}: {
|
|
slug: string | null;
|
|
children: ReactNode;
|
|
}) {
|
|
return (
|
|
<WorkspaceSlugContext.Provider value={slug}>
|
|
{children}
|
|
</WorkspaceSlugContext.Provider>
|
|
);
|
|
}
|
|
|
|
/** Current workspace slug from URL, or null outside workspace-scoped routes. */
|
|
export function useWorkspaceSlug(): string | null {
|
|
return use(WorkspaceSlugContext);
|
|
}
|
|
|
|
/** Same as useWorkspaceSlug, but throws if called outside a workspace route. */
|
|
export function useRequiredWorkspaceSlug(): string {
|
|
const slug = useWorkspaceSlug();
|
|
if (!slug) {
|
|
throw new Error(
|
|
"useRequiredWorkspaceSlug called outside a workspace-scoped route",
|
|
);
|
|
}
|
|
return slug;
|
|
}
|
|
|
|
/**
|
|
* The currently-selected workspace, derived from URL slug + React Query list.
|
|
* Returns null if slug is missing or doesn't match any workspace in the list.
|
|
*/
|
|
export function useCurrentWorkspace(): Workspace | null {
|
|
const slug = useWorkspaceSlug();
|
|
const { data: list = [] } = useQuery(workspaceListOptions());
|
|
if (!slug) return null;
|
|
return list.find((w) => w.slug === slug) ?? null;
|
|
}
|
|
|
|
/**
|
|
* Path builder bound to the current workspace. Throws if called outside a
|
|
* workspace route — for cross-workspace links use paths.workspace(slug) directly.
|
|
*/
|
|
export function useWorkspacePaths(): WorkspacePaths {
|
|
const slug = useRequiredWorkspaceSlug();
|
|
return paths.workspace(slug);
|
|
}
|