feat(scripts): GET /api/v1/scripts/count + DashboardRoute wiring
After #34 the workspace store only holds the root-level scripts plus whatever subfolders the user has expanded. DashboardRoute's "全部脚本"/"工作副本" counts derived from scripts.length therefore underreport the workspace total until the user navigates to /scripts and expands every folder. Fix: separate count endpoint + dedicated store field, mounted independently. Backend — backend/src/backend/scripts.py - New endpoint GET /api/v1/scripts/count. - Route declared BEFORE /api/v1/scripts/{script_id}/... so FastAPI's declaration-order matching does not interpret "count" as a script_id. - Returns { data: { total: number }, meta: {} }; SQL is a single COUNT(*) on scripts filtered by workspace_id + status='active'. Frontend — services/api.ts + context/AuthContext.tsx - countScripts(workspaceId) client; WorkspaceBoundApi gains the field; AuthContext binding forwards workspaceId. Frontend — state/scriptWorkspaceStore.ts - scriptCount: number | null, scriptCountLoading: boolean. - loadScriptCount() action: idempotent (no-op while in-flight), silent on failure (dashboard tolerates a stale count). - Initial state and reset() clear both fields. Frontend — features/platform/DashboardRoute.tsx - Subscribes to scriptCount; calls loadScriptCount() on mount. - Falls back to scripts.length until the count resolves so the dashboard never blanks. Tests — backend/tests/test_count_scripts.py (new) - 3 unit tests: scalar result handling, NULL coercion, route callable. Verified: pytest 55 passed (52 + 3 new); pnpm typecheck clean.
This commit is contained in:
@@ -296,6 +296,22 @@ export async function listScripts(
|
||||
return apiRequest<ScriptItem[]>(`/api/v1/scripts${query}`, {}, workspaceId);
|
||||
}
|
||||
|
||||
export async function countScripts(
|
||||
workspaceId: string,
|
||||
): Promise<number> {
|
||||
// Backend route /api/v1/scripts/count must be declared BEFORE the
|
||||
// /scripts/{script_id} route on the server side. Returns
|
||||
// { data: { total: number } } — the dashboard's single source of
|
||||
// truth for "total active scripts in workspace", independent of the
|
||||
// lazy-loaded scripts[] in the workspace store.
|
||||
const envelope = await apiRequest<{ total: number }>(
|
||||
"/api/v1/scripts/count",
|
||||
{},
|
||||
workspaceId,
|
||||
);
|
||||
return envelope.total;
|
||||
}
|
||||
|
||||
function initialContent(scriptType: ScriptType): string {
|
||||
if (scriptType === "python") {
|
||||
return [
|
||||
@@ -1466,6 +1482,7 @@ export type WorkspaceBoundApi = {
|
||||
listScripts: (
|
||||
parentPath?: Parameters<typeof listScripts>[1],
|
||||
) => Promise<ScriptItem[]>;
|
||||
countScripts: () => Promise<number>;
|
||||
listResources: (
|
||||
opts?: Parameters<typeof listResources>[1],
|
||||
) => Promise<ResourceItem[]>;
|
||||
|
||||
Reference in New Issue
Block a user