docs(portal-shell): add implementation plan for data abstraction & GraphQL hardening

- Plan: 20 tasks across M1-M4 phases

- Spec: fix useWidgetMutation destructure (object, not array)
This commit is contained in:
SpecialX
2026-07-17 12:03:03 +08:00
parent 135efa5ee5
commit 117c89396d
2 changed files with 1645 additions and 9 deletions

View File

@@ -222,33 +222,38 @@ export function useAuditLogs(
}
// Mutationhook 形态,因为 useWidgetMutation 是 React Hook
export function useCreateInvitationCode(): [
(input: CreateInvitationCodeInput) => Promise<InvitationCode>,
UseMutationResult,
] {
const [run, result] = useWidgetMutation<
export function useCreateInvitationCode(): {
run: (input: CreateInvitationCodeInput) => Promise<InvitationCode>;
loading: boolean;
error: ApolloError | undefined;
} {
const {
run: rawRun,
loading,
error,
} = useWidgetMutation<
CreateInvitationCodeMutation,
CreateInvitationCodeMutationVariables
>(CREATE_INVITATION_CODE_DOC);
const wrappedRun = async (
const run = async (
input: CreateInvitationCodeInput,
): Promise<InvitationCode> => {
const { data } = await run({ input });
const data = await rawRun({ input });
if (!data?.createInvitationCode) {
throw new ApiError("Failed to create invitation code", "INTERNAL_ERROR");
}
return data.createInvitationCode;
};
return [wrappedRun, result];
return { run, loading, error };
}
```
**规范要点**
- 查询返回**领域模型**`ChildSummary[]`),不是 GraphQL 原始响应(`{ myChildren: [...] }`),避免 schema 形状泄漏
- Mutation 包装为 hook返回 `[wrappedRun, result]` 元组;`wrappedRun``ApiError`widget 在事件 handler 中 `try/catch`
- Mutation 包装为 hook返回 `{ run, loading, error }` 对象(与 `useWidgetMutation` 返回形态一致);`run``ApiError`widget 在事件 handler 中 `try/catch`
- 命名:查询与 Mutation 都用 `use` 前缀(`useParentChildren``useCreateInvitationCode``useApproveLeave`),保持 React Hook 规范
- 文件名 `<domain>.ts`,函数名 `use<Entity>`(查询)/ `use<Action><Entity>`Mutation