feat(portal-shell): course-plans + elective 模块 5 页迁移(教师域 §9.1 B2)
§9.1 line 635-636 教师域:
- /shell/teacher/course-plans (列表) + /[id] (详情) — 2 页
- /shell/teacher/elective (列表) + /create (表单) + /[id]/edit (编辑表单) — 3 页
契约:全 ❌ schema 无 → MSW 兜底 + @contract-pending
新增文件:
- src/lib/api/{course-plans,elective}.ts (8 hooks 合计)
- src/lib/api/operations/{course-plans,elective}.graphql.ts (8 documents)
- src/features/teacher/{course-plans,elective}/ (clients + transformations + tests)
- src/app/shell/teacher/{course-plans,elective}/ (5 page.tsx + 2 loading + 2 error)
修改文件:
- src/mocks/graphql-data.ts (3 mock 数据 + 8 handler cases)
- src/messages/{zh-CN,en}.json (coursePlans/elective i18n 命名空间)
- src/lib/api/{index,operations/index}.ts (导出 course-plans/elective)
- src/shared/lib/route-permissions.ts (2 EXACT + 2 PREFIX 条目)
- scripts/check-page-count.ts (baseline 48 → 53)
DoD 验收(§11.3 11 项):
- typecheck 0 errors
- lint 0 errors
- vitest 645 tests passed
- lint:tokens 0 errors
- check:pages 53 PASS
- route-permissions 已声明
- 三态齐备
- @contract-pending + MSW 兜底
- i18n zh-CN + en 同步
设计决策:
- COURSE_PLAN_* 权限点不存在于 PERMISSION_BITMAP_ORDER,复用 LESSON_PLAN_READ/CREATE/UPDATE(同备课域语义对齐)
- elective 使用已存在的 ELECTIVE_READ/ELECTIVE_MANAGE
- 编辑表单用 useEffect + initialized state guard 预填数据
关联:ARCHITECTURE.md §5.3 / §5.4 / §9.1 / §10 P2 / §11.3 / §11.4
契约工单:docs/architecture/issues/contracts/core-edu_contract.md
This commit is contained in:
280
apps/portal-shell/src/lib/api/course-plans.ts
Normal file
280
apps/portal-shell/src/lib/api/course-plans.ts
Normal file
@@ -0,0 +1,280 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* Course Plans domain API(ARCHITECTURE.md §5.1 / §5.3 / §9.1 教师域课程计划模块)
|
||||
*
|
||||
* 契约状态:全 ❌(schema 无 coursePlan(id) / coursePlans 根字段,也无 Mutation 类型)
|
||||
* → 所有查询与 mutation 走 MSW 兜底(@contract-pending)
|
||||
*
|
||||
* 契约工单:docs/architecture/issues/contracts/core-edu_contract.md#course-plans
|
||||
* 后端补齐后:重跑 normalize + codegen → 关闭 skipDocumentsValidation → 切换 fetcher → 删 mock
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.3 契约纪律 / §5.4 MSW 兜底 / §9.1 / §11.4 契约工单
|
||||
*/
|
||||
import type { FetchPolicy } from "@apollo/client";
|
||||
|
||||
import { useWidgetMutation } from "../useWidgetMutation";
|
||||
import { useWidgetQuery } from "../useWidgetQuery";
|
||||
import { ApiError } from "./errors";
|
||||
import {
|
||||
CREATE_COURSE_PLAN_DOC,
|
||||
GET_COURSE_PLAN_DOC,
|
||||
GET_COURSE_PLANS_DOC,
|
||||
UPDATE_COURSE_PLAN_DOC,
|
||||
} from "./operations/course-plans.graphql";
|
||||
import type { UseQueryResult } from "./types";
|
||||
|
||||
// ===== 数据类型(@contract-pending,与 MSW mock 数据形状对齐)=====
|
||||
|
||||
/** 课程计划状态枚举 */
|
||||
export type CoursePlanStatus =
|
||||
"DRAFT" | "IN_PROGRESS" | "COMPLETED" | "ARCHIVED";
|
||||
|
||||
/** 单元状态枚举 */
|
||||
export type CoursePlanUnitStatus = "NOT_STARTED" | "IN_PROGRESS" | "COMPLETED";
|
||||
|
||||
/** 课程计划单元(详情页中的单元项) */
|
||||
export interface CoursePlanUnit {
|
||||
id: string;
|
||||
title: string;
|
||||
order: number;
|
||||
lessonCount: number;
|
||||
completedLessonCount: number;
|
||||
status: CoursePlanUnitStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 课程计划详情实体(用于详情页)。
|
||||
*
|
||||
* schema 无 CoursePlan 类型,字段形状由 MSW mock 定义。
|
||||
* 后端补齐后对齐真实 schema。
|
||||
*/
|
||||
export interface CoursePlanDetail {
|
||||
id: string;
|
||||
name: string;
|
||||
gradeId: string;
|
||||
subjectId: string;
|
||||
semester: string;
|
||||
status: CoursePlanStatus;
|
||||
description: string;
|
||||
objectives: string;
|
||||
units: CoursePlanUnit[];
|
||||
progress: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** 课程计划列表项(轻量字段集,用于列表渲染) */
|
||||
export interface CoursePlanListItem {
|
||||
id: string;
|
||||
name: string;
|
||||
gradeId: string;
|
||||
subjectId: string;
|
||||
semester: string;
|
||||
status: CoursePlanStatus;
|
||||
description: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** 列表查询响应(@contract-pending 假契约形状,MSW 返回此结构) */
|
||||
interface CoursePlansListResponse {
|
||||
coursePlans: {
|
||||
items: CoursePlanListItem[];
|
||||
total: number;
|
||||
};
|
||||
}
|
||||
|
||||
/** 单查响应(@contract-pending,MSW 返回此结构) */
|
||||
interface CoursePlanResponse {
|
||||
coursePlan: CoursePlanDetail | null;
|
||||
}
|
||||
|
||||
/** 创建课程计划输入 */
|
||||
export interface CreateCoursePlanInput {
|
||||
name: string;
|
||||
gradeId: string;
|
||||
subjectId: string;
|
||||
semester: string;
|
||||
description?: string;
|
||||
objectives?: string;
|
||||
}
|
||||
|
||||
/** 创建课程计划 mutation 响应(@contract-pending) */
|
||||
interface CreateCoursePlanResponse {
|
||||
createCoursePlan: { id: string } | null;
|
||||
}
|
||||
|
||||
/** 更新课程计划输入 */
|
||||
export interface UpdateCoursePlanInput {
|
||||
id: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
objectives?: string;
|
||||
status?: CoursePlanStatus;
|
||||
}
|
||||
|
||||
/** 更新课程计划 mutation 响应(@contract-pending) */
|
||||
interface UpdateCoursePlanResponse {
|
||||
updateCoursePlan: { id: string } | null;
|
||||
}
|
||||
|
||||
// ===== 筛选类型 =====
|
||||
|
||||
export interface CoursePlansListFilter {
|
||||
gradeId?: string;
|
||||
subjectId?: string;
|
||||
status?: string;
|
||||
q?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
// ===== 查询选项 =====
|
||||
|
||||
export interface CoursePlanQueryOptions {
|
||||
enabled?: boolean;
|
||||
pollInterval?: number;
|
||||
fetchPolicy?: FetchPolicy;
|
||||
}
|
||||
|
||||
// ===== Hooks =====
|
||||
|
||||
/**
|
||||
* 查询课程计划列表(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 coursePlans 根字段,由 MSW handlers 返回 mock 数据。
|
||||
* 后端补齐列表查询后切换到真实 fetcher,页面无需改动。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 列表页 / §11.4 契约工单
|
||||
*/
|
||||
export function useCoursePlans(
|
||||
filter: CoursePlansListFilter,
|
||||
options?: CoursePlanQueryOptions,
|
||||
): UseQueryResult<{ items: CoursePlanListItem[]; total: number }> {
|
||||
const result = useWidgetQuery<
|
||||
CoursePlansListResponse,
|
||||
{
|
||||
gradeId?: string;
|
||||
subjectId?: string;
|
||||
status?: string;
|
||||
q?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
>(
|
||||
GET_COURSE_PLANS_DOC,
|
||||
{
|
||||
gradeId: filter.gradeId,
|
||||
subjectId: filter.subjectId,
|
||||
status: filter.status,
|
||||
q: filter.q,
|
||||
limit: filter.limit,
|
||||
offset: filter.offset,
|
||||
},
|
||||
{
|
||||
enabled: options?.enabled ?? true,
|
||||
fetchPolicy: options?.fetchPolicy,
|
||||
pollInterval: options?.pollInterval,
|
||||
},
|
||||
);
|
||||
return {
|
||||
data: result.data?.coursePlans,
|
||||
loading: result.loading,
|
||||
error: result.error,
|
||||
refetch: result.refetch,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 id 查询课程计划详情(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 coursePlan(id) 根字段,由 MSW handlers 返回 mock 数据。
|
||||
* 用于 /shell/teacher/course-plans/[id] 详情页。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 详情页 / §11.4 契约工单
|
||||
*/
|
||||
export function useCoursePlan(
|
||||
id: string,
|
||||
options?: CoursePlanQueryOptions,
|
||||
): UseQueryResult<CoursePlanDetail | null> {
|
||||
const result = useWidgetQuery<CoursePlanResponse, { id: string }>(
|
||||
GET_COURSE_PLAN_DOC,
|
||||
{ id },
|
||||
{
|
||||
...options,
|
||||
enabled: options?.enabled ?? id.length > 0,
|
||||
},
|
||||
);
|
||||
return {
|
||||
data: result.data?.coursePlan ?? null,
|
||||
loading: result.loading,
|
||||
error: result.error,
|
||||
refetch: result.refetch,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建课程计划 mutation(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 Mutation 类型,由 MSW handlers 返回 mock 数据。
|
||||
* 后端补齐 mutation 后切换到真实 fetcher。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 / §11.4 契约工单
|
||||
*/
|
||||
export function useCreateCoursePlan(): {
|
||||
run: (input: CreateCoursePlanInput) => Promise<{ id: string }>;
|
||||
loading: boolean;
|
||||
error: unknown;
|
||||
} {
|
||||
const {
|
||||
run: rawRun,
|
||||
loading,
|
||||
error,
|
||||
} = useWidgetMutation<
|
||||
CreateCoursePlanResponse,
|
||||
{ input: CreateCoursePlanInput }
|
||||
>(CREATE_COURSE_PLAN_DOC);
|
||||
|
||||
const run = async (input: CreateCoursePlanInput): Promise<{ id: string }> => {
|
||||
const data = await rawRun({ input });
|
||||
if (!data?.createCoursePlan) {
|
||||
throw new ApiError("Failed to create course plan", "INTERNAL_ERROR");
|
||||
}
|
||||
return data.createCoursePlan;
|
||||
};
|
||||
|
||||
return { run, loading, error };
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新课程计划 mutation(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 Mutation 类型,由 MSW handlers 返回 mock 数据。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 / §11.4 契约工单
|
||||
*/
|
||||
export function useUpdateCoursePlan(): {
|
||||
run: (input: UpdateCoursePlanInput) => Promise<{ id: string }>;
|
||||
loading: boolean;
|
||||
error: unknown;
|
||||
} {
|
||||
const {
|
||||
run: rawRun,
|
||||
loading,
|
||||
error,
|
||||
} = useWidgetMutation<
|
||||
UpdateCoursePlanResponse,
|
||||
{ input: UpdateCoursePlanInput }
|
||||
>(UPDATE_COURSE_PLAN_DOC);
|
||||
|
||||
const run = async (input: UpdateCoursePlanInput): Promise<{ id: string }> => {
|
||||
const data = await rawRun({ input });
|
||||
if (!data?.updateCoursePlan) {
|
||||
throw new ApiError("Failed to update course plan", "INTERNAL_ERROR");
|
||||
}
|
||||
return data.updateCoursePlan;
|
||||
};
|
||||
|
||||
return { run, loading, error };
|
||||
}
|
||||
255
apps/portal-shell/src/lib/api/elective.ts
Normal file
255
apps/portal-shell/src/lib/api/elective.ts
Normal file
@@ -0,0 +1,255 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* Elective domain API(ARCHITECTURE.md §5.1 / §5.3 / §9.1 教师域选修课模块)
|
||||
*
|
||||
* 契约状态:全 ❌(schema 无 electives / elective(id) 根字段,也无 Mutation 类型)
|
||||
* → 所有查询与 mutation 走 MSW 兜底(@contract-pending)
|
||||
*
|
||||
* 契约工单:docs/architecture/issues/contracts/core-edu_contract.md#elective
|
||||
* 后端补齐后:重跑 normalize + codegen → 关闭 skipDocumentsValidation → 切换 fetcher → 删 mock
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.3 契约纪律 / §5.4 MSW 兜底 / §9.1 / §11.4 契约工单
|
||||
*/
|
||||
import type { FetchPolicy } from "@apollo/client";
|
||||
|
||||
import { useWidgetMutation } from "../useWidgetMutation";
|
||||
import { useWidgetQuery } from "../useWidgetQuery";
|
||||
import { ApiError } from "./errors";
|
||||
import {
|
||||
CREATE_ELECTIVE_DOC,
|
||||
GET_ELECTIVE_DOC,
|
||||
GET_ELECTIVES_DOC,
|
||||
UPDATE_ELECTIVE_DOC,
|
||||
} from "./operations/elective.graphql";
|
||||
import type { UseQueryResult } from "./types";
|
||||
|
||||
// ===== 数据类型(@contract-pending,与 MSW mock 数据形状对齐)=====
|
||||
|
||||
/** 选修课状态枚举 */
|
||||
export type ElectiveStatus = "DRAFT" | "OPEN" | "CLOSED" | "ARCHIVED";
|
||||
|
||||
/**
|
||||
* 选修课实体(列表项与单查同构,@contract-pending)。
|
||||
*
|
||||
* schema 无 Elective 类型,字段形状由 MSW mock 定义。
|
||||
* 后端补齐后对齐真实 schema。
|
||||
*/
|
||||
export interface Elective {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
capacity: number;
|
||||
enrolledCount: number;
|
||||
semester: string;
|
||||
gradeLevel: string;
|
||||
subject: string;
|
||||
teacherId: string;
|
||||
teacherName: string;
|
||||
status: ElectiveStatus;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** 列表项(与 Elective 同构) */
|
||||
export type ElectiveListItem = Elective;
|
||||
|
||||
/** 列表查询响应(@contract-pending 假契约形状,MSW 返回此结构) */
|
||||
interface ElectivesListResponse {
|
||||
electives: {
|
||||
items: ElectiveListItem[];
|
||||
total: number;
|
||||
};
|
||||
}
|
||||
|
||||
/** 单查响应(@contract-pending,MSW 返回此结构) */
|
||||
interface ElectiveResponse {
|
||||
elective: Elective | null;
|
||||
}
|
||||
|
||||
/** 创建选修课输入 */
|
||||
export interface CreateElectiveInput {
|
||||
name: string;
|
||||
description?: string;
|
||||
capacity: number;
|
||||
semester: string;
|
||||
gradeLevel: string;
|
||||
subject: string;
|
||||
teacherId?: string;
|
||||
}
|
||||
|
||||
/** 创建选修课 mutation 响应(@contract-pending) */
|
||||
interface CreateElectiveResponse {
|
||||
createElective: { id: string } | null;
|
||||
}
|
||||
|
||||
/** 更新选修课输入 */
|
||||
export interface UpdateElectiveInput {
|
||||
id: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
capacity?: number;
|
||||
semester?: string;
|
||||
gradeLevel?: string;
|
||||
subject?: string;
|
||||
teacherId?: string;
|
||||
status?: ElectiveStatus;
|
||||
}
|
||||
|
||||
/** 更新选修课 mutation 响应(@contract-pending) */
|
||||
interface UpdateElectiveResponse {
|
||||
updateElective: { id: string } | null;
|
||||
}
|
||||
|
||||
// ===== 筛选类型 =====
|
||||
|
||||
export interface ElectivesListFilter {
|
||||
status?: string;
|
||||
q?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
// ===== 查询选项 =====
|
||||
|
||||
export interface ElectiveQueryOptions {
|
||||
enabled?: boolean;
|
||||
pollInterval?: number;
|
||||
fetchPolicy?: FetchPolicy;
|
||||
}
|
||||
|
||||
// ===== Hooks =====
|
||||
|
||||
/**
|
||||
* 查询选修课列表(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 electives 根字段,由 MSW handlers 返回 mock 数据。
|
||||
* 后端补齐列表查询后切换到真实 fetcher,页面无需改动。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 列表页 / §11.4 契约工单
|
||||
*/
|
||||
export function useElectives(
|
||||
filter: ElectivesListFilter,
|
||||
options?: ElectiveQueryOptions,
|
||||
): UseQueryResult<{ items: ElectiveListItem[]; total: number }> {
|
||||
const result = useWidgetQuery<
|
||||
ElectivesListResponse,
|
||||
{
|
||||
status?: string;
|
||||
q?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
>(
|
||||
GET_ELECTIVES_DOC,
|
||||
{
|
||||
status: filter.status,
|
||||
q: filter.q,
|
||||
limit: filter.limit,
|
||||
offset: filter.offset,
|
||||
},
|
||||
{
|
||||
enabled: options?.enabled ?? true,
|
||||
fetchPolicy: options?.fetchPolicy,
|
||||
pollInterval: options?.pollInterval,
|
||||
},
|
||||
);
|
||||
return {
|
||||
data: result.data?.electives,
|
||||
loading: result.loading,
|
||||
error: result.error,
|
||||
refetch: result.refetch,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 id 查询选修课详情(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 elective(id) 根字段,由 MSW handlers 返回 mock 数据。
|
||||
* 用于 /shell/teacher/elective/[id]/edit 编辑表单页预填。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 表单页 / §11.4 契约工单
|
||||
*/
|
||||
export function useElective(
|
||||
id: string,
|
||||
options?: ElectiveQueryOptions,
|
||||
): UseQueryResult<Elective | null> {
|
||||
const result = useWidgetQuery<ElectiveResponse, { id: string }>(
|
||||
GET_ELECTIVE_DOC,
|
||||
{ id },
|
||||
{
|
||||
...options,
|
||||
enabled: options?.enabled ?? id.length > 0,
|
||||
},
|
||||
);
|
||||
return {
|
||||
data: result.data?.elective ?? null,
|
||||
loading: result.loading,
|
||||
error: result.error,
|
||||
refetch: result.refetch,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建选修课 mutation(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 Mutation 类型,由 MSW handlers 返回 mock 数据。
|
||||
* 用于 /shell/teacher/elective/create 表单页。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 表单页 / §11.4 契约工单
|
||||
*/
|
||||
export function useCreateElective(): {
|
||||
run: (input: CreateElectiveInput) => Promise<{ id: string }>;
|
||||
loading: boolean;
|
||||
error: unknown;
|
||||
} {
|
||||
const {
|
||||
run: rawRun,
|
||||
loading,
|
||||
error,
|
||||
} = useWidgetMutation<CreateElectiveResponse, { input: CreateElectiveInput }>(
|
||||
CREATE_ELECTIVE_DOC,
|
||||
);
|
||||
|
||||
const run = async (input: CreateElectiveInput): Promise<{ id: string }> => {
|
||||
const data = await rawRun({ input });
|
||||
if (!data?.createElective) {
|
||||
throw new ApiError("Failed to create elective", "INTERNAL_ERROR");
|
||||
}
|
||||
return data.createElective;
|
||||
};
|
||||
|
||||
return { run, loading, error };
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新选修课 mutation(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 Mutation 类型,由 MSW handlers 返回 mock 数据。
|
||||
* 用于 /shell/teacher/elective/[id]/edit 编辑表单页保存。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 表单页 / §11.4 契约工单
|
||||
*/
|
||||
export function useUpdateElective(): {
|
||||
run: (input: UpdateElectiveInput) => Promise<{ id: string }>;
|
||||
loading: boolean;
|
||||
error: unknown;
|
||||
} {
|
||||
const {
|
||||
run: rawRun,
|
||||
loading,
|
||||
error,
|
||||
} = useWidgetMutation<UpdateElectiveResponse, { input: UpdateElectiveInput }>(
|
||||
UPDATE_ELECTIVE_DOC,
|
||||
);
|
||||
|
||||
const run = async (input: UpdateElectiveInput): Promise<{ id: string }> => {
|
||||
const data = await rawRun({ input });
|
||||
if (!data?.updateElective) {
|
||||
throw new ApiError("Failed to update elective", "INTERNAL_ERROR");
|
||||
}
|
||||
return data.updateElective;
|
||||
};
|
||||
|
||||
return { run, loading, error };
|
||||
}
|
||||
@@ -24,5 +24,7 @@ export * from "./attendance";
|
||||
export * from "./classes";
|
||||
export * from "./students";
|
||||
export * from "./student";
|
||||
export * from "./course-plans";
|
||||
export * from "./elective";
|
||||
export * from "./parent";
|
||||
export * from "./admin";
|
||||
|
||||
100
apps/portal-shell/src/lib/api/operations/course-plans.graphql.ts
Normal file
100
apps/portal-shell/src/lib/api/operations/course-plans.graphql.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
// Course Plans domain GraphQL documents (ARCHITECTURE.md §5.3 契约纪律 / §9.1)
|
||||
//
|
||||
// 拆分原则:
|
||||
// - 全部操作:❌ schema 无 coursePlans / coursePlan(id) 根字段,也无 Mutation 类型
|
||||
// → 走 MSW 兜底(@contract-pending),等待后端补齐契约
|
||||
//
|
||||
// 契约工单:docs/architecture/issues/contracts/core-edu_contract.md#course-plans
|
||||
// 关联:ARCHITECTURE.md §5.3 / §5.4 / §9.1 / §11.4
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
// ── 假契约查询(@contract-pending)─────────────────────────────
|
||||
// 列表查询:schema 无 coursePlans(...) 根字段
|
||||
// 页面通过 MSW 兜底获取列表数据,后端补齐后切换 fetcher 指向真实查询
|
||||
// 契约工单:core-edu_contract.md#course-plans-list
|
||||
export const GET_COURSE_PLANS_DOC = gql`
|
||||
query GetCoursePlans(
|
||||
$gradeId: ID
|
||||
$subjectId: ID
|
||||
$status: String
|
||||
$q: String
|
||||
$limit: Int
|
||||
$offset: Int
|
||||
) {
|
||||
coursePlans(
|
||||
gradeId: $gradeId
|
||||
subjectId: $subjectId
|
||||
status: $status
|
||||
q: $q
|
||||
limit: $limit
|
||||
offset: $offset
|
||||
) {
|
||||
items {
|
||||
id
|
||||
name
|
||||
gradeId
|
||||
subjectId
|
||||
semester
|
||||
status
|
||||
description
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 单查(@contract-pending)────────────────────────────────────
|
||||
// schema 无 coursePlan(id) 根字段 → MSW 兜底
|
||||
// 用于 /shell/teacher/course-plans/[id] 详情页
|
||||
// 契约工单:core-edu_contract.md#course-plan-detail
|
||||
export const GET_COURSE_PLAN_DOC = gql`
|
||||
query GetCoursePlan($id: ID!) {
|
||||
coursePlan(id: $id) {
|
||||
id
|
||||
name
|
||||
gradeId
|
||||
subjectId
|
||||
semester
|
||||
status
|
||||
description
|
||||
objectives
|
||||
units {
|
||||
id
|
||||
title
|
||||
order
|
||||
lessonCount
|
||||
completedLessonCount
|
||||
status
|
||||
}
|
||||
progress
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 假契约变更(@contract-pending)─────────────────────────────
|
||||
// 创建课程计划:schema 无 Mutation 类型
|
||||
// 页面通过 MSW 兜底提交,后端补齐 mutation 后切换 fetcher
|
||||
// 契约工单:core-edu_contract.md#create-course-plan
|
||||
export const CREATE_COURSE_PLAN_DOC = gql`
|
||||
mutation CreateCoursePlan($input: CreateCoursePlanInput!) {
|
||||
createCoursePlan(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 更新课程计划 mutation(@contract-pending)──────────────────
|
||||
// schema 无 Mutation 类型 → MSW 兜底
|
||||
// 用于编辑/状态变更
|
||||
// 契约工单:core-edu_contract.md#update-course-plan
|
||||
export const UPDATE_COURSE_PLAN_DOC = gql`
|
||||
mutation UpdateCoursePlan($input: UpdateCoursePlanInput!) {
|
||||
updateCoursePlan(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
84
apps/portal-shell/src/lib/api/operations/elective.graphql.ts
Normal file
84
apps/portal-shell/src/lib/api/operations/elective.graphql.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
// Elective domain GraphQL documents (ARCHITECTURE.md §5.3 契约纪律 / §9.1)
|
||||
//
|
||||
// 拆分原则:
|
||||
// - 全部操作:❌ schema 无 electives / elective(id) 根字段,也无 Mutation 类型
|
||||
// → 走 MSW 兜底(@contract-pending),等待后端补齐契约
|
||||
//
|
||||
// 契约工单:docs/architecture/issues/contracts/core-edu_contract.md#elective
|
||||
// 关联:ARCHITECTURE.md §5.3 / §5.4 / §9.1 / §11.4
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
// ── 假契约查询(@contract-pending)─────────────────────────────
|
||||
// 列表查询:schema 无 electives(...) 根字段
|
||||
// 页面通过 MSW 兜底获取列表数据,后端补齐后切换 fetcher 指向真实查询
|
||||
// 契约工单:core-edu_contract.md#electives-list
|
||||
export const GET_ELECTIVES_DOC = gql`
|
||||
query GetElectives($status: String, $q: String, $limit: Int, $offset: Int) {
|
||||
electives(status: $status, q: $q, limit: $limit, offset: $offset) {
|
||||
items {
|
||||
id
|
||||
name
|
||||
description
|
||||
capacity
|
||||
enrolledCount
|
||||
semester
|
||||
gradeLevel
|
||||
subject
|
||||
teacherId
|
||||
teacherName
|
||||
status
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 单查(@contract-pending)────────────────────────────────────
|
||||
// schema 无 elective(id) 根字段 → MSW 兜底
|
||||
// 用于 /shell/teacher/elective/[id]/edit 编辑表单页预填
|
||||
// 契约工单:core-edu_contract.md#elective-by-id
|
||||
export const GET_ELECTIVE_DOC = gql`
|
||||
query GetElective($id: ID!) {
|
||||
elective(id: $id) {
|
||||
id
|
||||
name
|
||||
description
|
||||
capacity
|
||||
enrolledCount
|
||||
semester
|
||||
gradeLevel
|
||||
subject
|
||||
teacherId
|
||||
teacherName
|
||||
status
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 假契约变更(@contract-pending)─────────────────────────────
|
||||
// 创建选修课:schema 无 Mutation 类型
|
||||
// 页面通过 MSW 兜底提交,后端补齐 mutation 后切换 fetcher
|
||||
// 契约工单:core-edu_contract.md#create-elective
|
||||
export const CREATE_ELECTIVE_DOC = gql`
|
||||
mutation CreateElective($input: CreateElectiveInput!) {
|
||||
createElective(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 更新选修课 mutation(@contract-pending)────────────────────
|
||||
// schema 无 Mutation 类型 → MSW 兜底
|
||||
// 用于 /shell/teacher/elective/[id]/edit 编辑表单页保存
|
||||
// 契约工单:core-edu_contract.md#update-elective
|
||||
export const UPDATE_ELECTIVE_DOC = gql`
|
||||
mutation UpdateElective($input: UpdateElectiveInput!) {
|
||||
updateElective(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -14,5 +14,7 @@ export * from "./attendance.graphql";
|
||||
export * from "./classes.graphql";
|
||||
export * from "./students.graphql";
|
||||
export * from "./student.graphql";
|
||||
export * from "./course-plans.graphql";
|
||||
export * from "./elective.graphql";
|
||||
export * from "./parent.graphql";
|
||||
export * from "./admin.graphql";
|
||||
|
||||
Reference in New Issue
Block a user