Files
Edu/apps/portal-shell/src/messages/en.json
SpecialX 2f8f3f3855 feat(portal-shell): grades 模块 5 页迁移(教师域 §9.1 B2)
§9.1 B2 grades 行:/grades、/entry、/analytics、/stats、/report-card
契约:grade(id)  真实查询;列表/分析/统计/成绩单 → MSW 兜底

新增文件:
- src/lib/api/grades.ts (6 hooks)
- src/lib/api/operations/grades.graphql.ts (6 documents)
- src/features/teacher/grades/ (7 files: transformations + tests + 5 clients)
- src/app/shell/teacher/grades/ (5 page.tsx + loading.tsx + error.tsx)

修改文件:
- src/mocks/graphql-data.ts (5 mock 常量 + 6 handler cases)
- src/messages/{zh-CN,en}.json (grades i18n)
- src/lib/api/{index,operations/index}.ts (导出 grades)
- scripts/check-page-count.ts (baseline 26 → 31)

DoD 验收(§11.3 11 项):
- typecheck 0 errors
- lint 0 errors
- vitest 357 tests passed
- lint:tokens 0 errors
- check:pages 31 PASS

关联:ARCHITECTURE.md §5.3 / §5.4 / §9.1 / §10 P2 / §11.3 / §11.4
2026-07-22 18:41:55 +08:00

694 lines
24 KiB
JSON

{
"common": {
"brand": "Edu",
"button": {
"save": "Save",
"cancel": "Cancel",
"edit": "Edit",
"export": "Export",
"search": "Search",
"delete": "Delete",
"retry": "Retry",
"back": "Back"
},
"label": {
"search": "Search",
"loading": "Loading...",
"student": "Student",
"studentNo": "Student No.",
"score": "Score",
"feedback": "Feedback",
"class": "Class",
"subject": "Subject",
"name": "Name",
"email": "Email",
"roles": "Roles"
},
"status": {
"loading": "Loading...",
"saved": "Saved"
},
"error": {
"loadFailed": "Failed to load: {message}",
"pageError": "Something went wrong"
},
"nav": {
"empty": "No viewports available",
"loadError": "Navigation failed to load: {message}",
"logout": "Log out",
"noRoles": "No roles"
},
"locale": {
"label": "Language",
"zhCN": "简体中文",
"en": "English"
},
"empty": {
"title": "No data",
"description": "Adjust filters and retry, or create your first record"
},
"navLabel": {
"dashboard": "Dashboard",
"classes": "Classes",
"exams": "Exams",
"homework": "Homework",
"grades": "Grades",
"analytics": "Analytics",
"knowledgeGraph": "Knowledge Graph",
"notifications": "Notifications",
"aiAssist": "AI Assist",
"aiLessonPlan": "AI Lesson Plan",
"aiReport": "AI Report",
"attendance": "Attendance",
"questions": "Questions",
"textbooks": "Textbooks",
"lessonPlans": "Lesson Plans",
"coursePlans": "Course Plans",
"diagnostic": "Diagnostic",
"errorBook": "Error Book",
"practice": "Practice",
"elective": "Elective",
"leave": "Leave Requests",
"scheduleChanges": "Schedule Changes",
"settings": "Settings",
"students": "Students"
}
},
"shell": {
"toggleSidebar": "Toggle sidebar",
"layout": "Layout",
"empty": "No visible plugins",
"dev": {
"templates": {
"title": "Page Templates Quartet",
"description": "P1-3 preview pages (dev only, 404 in production)",
"list": "List Page Template",
"listDescription": "PageHeader + FilterBar + DataTable + Pagination + 3 states",
"detail": "Detail Page Template",
"detailDescription": "PageHeader + info area + Tabs/sections + related list",
"form": "Form Page Template",
"formDescription": "PageHeader + form + submit/cancel + error summary",
"workbench": "Workbench Page Template",
"workbenchDescription": "Three-column (tree/canvas/properties) composite",
"listExample": {
"title": "Exam Management",
"subtitle": "List page template example",
"searchPlaceholder": "Search exam name...",
"newExam": "New Exam"
},
"detailExample": {
"title": "2026 Spring Final Exam",
"subtitle": "Detail page template example"
},
"formExample": {
"title": "New Exam",
"subtitle": "Form page template example"
},
"workbenchExample": {
"title": "Lesson Plan Editor",
"subtitle": "Workbench page template example"
}
}
}
},
"auth": {
"title": "Login"
},
"dashboard": {
"title": "Welcome",
"greeting": "Welcome, {name}",
"subtitle": "{email} · Roles: {roles} · Data scope: {dataScope}",
"subtitleNoRoles": "None",
"error": {
"loadFailed": "Dashboard failed to load: {message}"
},
"empty": {
"title": "No data",
"description": "Dashboard data is not ready yet"
},
"stats": {
"classes": "Total Classes",
"exams": "Total Exams",
"pendingGrading": "Pending Grading"
},
"classes": {
"title": "My Classes",
"count": "{count} total",
"viewAll": "View all",
"emptyTitle": "No classes",
"emptyDescription": "Please contact the administrator to assign classes",
"grade": "Grade: {gradeId}",
"studentCount": "{count} students",
"viewStudents": "View students"
}
},
"classes": {
"title": "Classes"
},
"exams": {
"title": "Exams",
"list": {
"title": "Exams",
"description": "View and manage all exams",
"new": "New Exam",
"searchPlaceholder": "Search exam name...",
"statusFilter": "Filter by status",
"statusAll": "All statuses",
"statusDraft": "Draft",
"statusPublished": "Published",
"statusInProgress": "In Progress",
"statusScored": "Completed",
"total": "{count} total",
"colName": "Name",
"colStatus": "Status",
"colExamDate": "Exam Date",
"colDuration": "Duration",
"colTotalScore": "Total Score",
"colActions": "Actions",
"viewDetail": "View Detail →",
"mswNotice": "List query contract is pending, please ensure NEXT_PUBLIC_MSW=1 is enabled.",
"unitScore": "pts"
},
"detail": {
"title": "Exam Detail",
"edit": "Edit",
"notFound": "Exam not found, may have been deleted",
"createdAtPrefix": "Created on {date}",
"sectionBasic": "Basic Info",
"sectionStatus": "Status Info",
"fieldTitle": "Title",
"fieldDescription": "Description",
"fieldExamDate": "Exam Date",
"fieldDuration": "Duration",
"fieldTotalScore": "Total Score",
"fieldClassId": "Class ID",
"fieldSubjectId": "Subject ID",
"fieldCurrentStatus": "Current Status",
"fieldStatusChangedAt": "Status Changed At",
"fieldStatusChangedBy": "Status Changed By",
"fieldCreatedBy": "Created By",
"fieldCreatedAt": "Created At",
"fieldUpdatedAt": "Updated At",
"unitScore": "pts"
},
"new": {
"title": "New Exam",
"description": "Fill in exam basic information",
"submit": "Create Exam",
"success": "Exam created successfully",
"error": "Creation failed",
"classId": "Class ID",
"subjectId": "Subject ID",
"titleLabel": "Title",
"titlePlaceholder": "e.g. 2026 Spring Midterm",
"descriptionLabel": "Description",
"descriptionPlaceholder": "Exam scope, notes, etc.",
"examDate": "Exam Date",
"duration": "Duration (minutes)",
"totalScore": "Total Score",
"errorClassRequired": "Please fill in Class ID",
"errorTitleRequired": "Please fill in exam title",
"errorDateRequired": "Please select exam date",
"contractPending": "Create exam contract is @contract-pending, currently backed by MSW. Will switch to real submission once backend mutation is ready."
},
"analytics": {
"title": "Exam Analytics",
"subtitle": "Exam ID: {examId}",
"exportCsv": "Export CSV",
"notFound": "No analytics data found, grading may not be complete",
"sectionSummary": "Summary",
"sectionDistribution": "Score Distribution",
"sectionQuestionAccuracy": "Question Accuracy",
"sectionRankings": "Student Rankings",
"summaryExpected": "Expected",
"summaryAttended": "Attended",
"summaryAvg": "Average",
"summaryMax": "Max",
"summaryMin": "Min",
"summaryPassRate": "Pass Rate",
"emptyDistribution": "No score distribution data",
"emptyQuestionAccuracy": "No question accuracy data",
"emptyRankings": "No ranking data",
"colOrder": "#",
"colQuestionTitle": "Question",
"colCorrectRate": "Correct Rate",
"colAvgScore": "Avg Score",
"colMaxScore": "Max Score",
"colRank": "Rank",
"colStudentNo": "Student No.",
"colStudentName": "Name",
"colTotalScore": "Total Score",
"colLevel": "Level"
},
"build": {
"title": "Build Exam",
"subtitle": "Current total: {total} pts · Pass score: {pass} pts",
"save": "Save Build",
"saveSuccess": "Build saved successfully",
"saveFailed": "Save failed: {message}",
"notFound": "No build data found",
"alreadyAdded": "This question has already been added",
"libraryTitle": "Question Library",
"searchPlaceholder": "Search question content...",
"filterType": "Filter by type",
"filterDifficulty": "Filter by difficulty",
"allTypes": "All types",
"allDifficulties": "All difficulties",
"typeSingle": "Single Choice",
"typeMultiple": "Multiple Choice",
"typeFill": "Fill in the Blank",
"typeEssay": "Essay",
"diffEasy": "Easy",
"diffMedium": "Medium",
"diffHard": "Hard",
"loadingLibrary": "Loading library...",
"loadLibraryFailed": "Library load failed",
"libraryEmpty": "No questions match the filter",
"libraryTotal": "{total} questions total",
"addToExam": "Add to exam",
"unitScore": "pts",
"selectedTitle": "Selected Questions",
"selectedCount": "{count} questions",
"selectedEmpty": "Add questions from the library on the left",
"scoreLabel": "Score",
"moveUp": "Move up",
"moveDown": "Move down",
"remove": "Remove",
"previewTitle": "Preview",
"previewTotal": "Total Score",
"previewBaseline": "Baseline: {baseline} pts",
"previewPassScore": "Pass Score",
"previewTypeBreakdown": "Type Breakdown"
},
"edit": {
"title": "Edit Exam Paper",
"subtitle": "Total: {total} pts · {count} questions",
"save": "Save Paper",
"saveSuccess": "Paper saved successfully",
"saveFailed": "Save failed: {message}",
"notFound": "No paper content found",
"propertiesTitle": "Paper Properties",
"propExamId": "Exam ID",
"propTotalScore": "Total Score",
"propQuestionCount": "Question Count",
"propUpdatedAt": "Last Updated",
"contractPending": "Rich text editor contract is @contract-pending, currently backed by MSW. Will switch to real data once backend is ready.",
"editorTitle": "Paper Content",
"toolbarBold": "Bold",
"toolbarItalic": "Italic",
"toolbarUnderline": "Underline",
"toolbarBulletList": "Bullet list",
"toolbarOrderedList": "Ordered list"
}
},
"homework": {
"title": "Homework",
"list": {
"title": "Homework",
"description": "View and manage all homework",
"new": "Assign Homework",
"searchPlaceholder": "Search homework name...",
"statusFilter": "Filter by status",
"statusAll": "All statuses",
"statusDraft": "Draft",
"statusPublished": "Published",
"statusClosed": "Closed",
"statusArchived": "Archived",
"total": "{count} total",
"colName": "Name",
"colStatus": "Status",
"colDueDate": "Due Date",
"colGracePeriod": "Grace Period",
"colActions": "Actions",
"viewDetail": "View Detail →",
"mswNotice": "List query contract is pending, please ensure NEXT_PUBLIC_MSW=1 is enabled."
},
"detail": {
"title": "Homework Detail",
"edit": "Edit",
"notFound": "Homework not found, may have been deleted",
"createdAtPrefix": "Created on {date}",
"sectionBasic": "Basic Info",
"sectionSubmissions": "Submissions",
"sectionInlineGrade": "Inline Grade",
"fieldTitle": "Title",
"fieldDescription": "Description",
"fieldDueDate": "Due Date",
"fieldGracePeriod": "Grace Period",
"fieldStatus": "Status",
"fieldClassId": "Class ID",
"fieldSubjectId": "Subject ID",
"fieldCreatedBy": "Created By",
"fieldUpdatedAt": "Updated At",
"viewAllSubmissions": "All Submissions",
"noSubmissions": "No submissions yet",
"colStudent": "Student",
"colStatus": "Status",
"colSubmittedAt": "Submitted At",
"colScore": "Score",
"colActions": "Actions",
"grade": "Grade",
"gradeStudentId": "Student ID",
"gradeScore": "Score",
"gradeFeedback": "Feedback",
"gradeFeedbackPlaceholder": "Write feedback to the student...",
"gradeSubmit": "Submit Grade",
"gradeSubmitting": "Submitting...",
"gradeSuccess": "Grade submitted successfully",
"gradeError": "Grading failed",
"gradeErrorStudentRequired": "Please fill in Student ID",
"gradeErrorScoreInvalid": "Score is invalid, please enter a non-negative number",
"gradeContractPending": "Grade contract is @contract-pending, currently backed by MSW."
},
"new": {
"title": "Assign Homework",
"description": "Fill in homework basic information",
"submit": "Assign",
"success": "Homework assigned successfully",
"error": "Assignment failed",
"classId": "Class ID",
"subjectId": "Subject ID",
"titleLabel": "Title",
"titlePlaceholder": "e.g. Set and Function Practice",
"descriptionLabel": "Description",
"descriptionPlaceholder": "Scope, notes, etc.",
"dueDate": "Due Date",
"gracePeriod": "Grace Period (hours)",
"gracePeriodHint": "Hours of grace after the deadline, 0 means no grace",
"errorClassRequired": "Please fill in Class ID",
"errorTitleRequired": "Please fill in homework title",
"errorDateRequired": "Please select due date",
"contractPending": "Assign homework contract is @contract-pending, currently backed by MSW. Will switch to real submission once backend mutation is ready."
},
"submissions": {
"title": "Submission Grading",
"description": "View all homework submissions and grade them",
"searchPlaceholder": "Search student/no/homework...",
"classIdFilter": "Filter by class",
"classIdPlaceholder": "Class ID",
"statusFilter": "Filter by status",
"statusAll": "All statuses",
"statusSubmitted": "Submitted",
"statusGrading": "Grading",
"statusGraded": "Graded",
"statusReturned": "Returned",
"statusLate": "Late",
"total": "{count} total",
"colStudent": "Student",
"colHomework": "Homework",
"colClass": "Class",
"colStatus": "Status",
"colSubmittedAt": "Submitted At",
"colScore": "Score",
"colActions": "Actions",
"toGrade": "Grade",
"mswNotice": "Submission query contract is pending, please ensure NEXT_PUBLIC_MSW=1 is enabled."
},
"grading": {
"title": "Grade: {name}",
"titleLoading": "Loading grade...",
"subtitle": "Homework: {homework} · No.: {no}",
"notFound": "Submission not found, may have been deleted",
"sectionStudent": "Student Info",
"sectionAnswers": "Answers",
"sectionNavigation": "Navigation",
"fieldStudentName": "Name",
"fieldStudentNo": "Student No.",
"fieldClass": "Class",
"fieldHomework": "Homework",
"fieldStatus": "Status",
"fieldSubmittedAt": "Submitted At",
"fieldFeedback": "Current Feedback",
"questionType": "Type",
"maxScore": "Max Score",
"maxScoreTotal": "Total Max",
"studentAnswer": "Student Answer",
"score": "Score",
"teacherComment": "Teacher Comment",
"overallFeedback": "Overall Feedback",
"overallFeedbackPlaceholder": "Write overall feedback to the student...",
"applyAi": "Apply AI Suggestion",
"aiSuggestionLabel": "AI Suggestion",
"aiSuggestionApplied": "AI suggestion applied",
"prev": "Previous",
"next": "Next",
"position": "{current}/{total}",
"submit": "Submit Grade",
"submitting": "Submitting...",
"success": "Grade submitted successfully",
"error": "Grading failed",
"errorScoreInvalid": "Score for question {qid} is invalid",
"contractPending": "Grading contract is @contract-pending, currently backed by MSW."
},
"scan": {
"title": "Scan Grade: {name}",
"titleLoading": "Loading scan grade...",
"subtitle": "Homework: {homework}",
"notFound": "Submission not found, may have been deleted",
"scanPreview": "Scan Preview",
"imagePlaceholder": "Scan image (submissionId: {id})",
"imageContractPending": "Scan image preview contract is @contract-pending, currently a placeholder.",
"recognizedAnswers": "Recognized Answers",
"recognizedAnswer": "Recognized Answer",
"recognizedCorrect": "Recognized correct",
"recognizedIncorrect": "Recognized incorrect",
"questionType": "Type",
"aiSuggestionLabel": "AI Suggestion",
"noAnswers": "No answer data",
"gradingForm": "Grading Form",
"scoreLabel": "Score for {qid}",
"totalScore": "Current Total",
"applyAllAi": "Apply All AI Suggestions",
"aiAppliedCount": "Applied {count} AI suggestions",
"confidence": "Confidence",
"overallFeedback": "Overall Feedback",
"overallFeedbackPlaceholder": "Write overall feedback to the student...",
"save": "Save Grade",
"saving": "Saving...",
"saveSuccess": "Scan grading saved successfully",
"saveError": "Save failed",
"errorScoreInvalid": "Score for question {qid} is invalid",
"unitScore": "pts",
"contractPending": "Scan grading contract is @contract-pending, currently backed by MSW."
},
"assignment": {
"title": "Batch Grading by Homework",
"description": "Homework: {title}",
"descriptionLoading": "Loading homework info...",
"backToHomework": "Back to Homework Detail",
"total": "{count} submissions",
"mswNotice": "Assignment submissions query contract is pending, please ensure NEXT_PUBLIC_MSW=1 is enabled.",
"sectionHomework": "Homework Info",
"sectionSubmissions": "Submissions",
"sectionAiGrading": "AI Batch Grading",
"homeworkTitle": "Title",
"homeworkId": "Homework ID",
"className": "Class",
"dueDate": "Due Date",
"colStudent": "Student",
"colStatus": "Status",
"colSubmittedAt": "Submitted At",
"colScore": "Score",
"colActions": "Actions",
"scanGrade": "Scan Grade",
"detailGrade": "Detail Grade",
"statsTotalStudents": "Expected",
"statsSubmitted": "Submitted",
"statsGraded": "Graded",
"statsPending": "Pending",
"statsAvgScore": "Avg",
"statsSubmissionRate": "Submission Rate",
"aiBatchGrade": "Batch Auto Grade",
"aiTriggered": "AI grading request triggered",
"aiSummary": "Processed {processed}/{total}, avg confidence {avg}",
"aiEmpty": "No AI suggestions yet, click the button to trigger",
"aiContractPending": "AI grading contract is @contract-pending, currently backed by MSW.",
"suggestedScore": "Suggested",
"confidence": "Confidence"
},
"error": {
"title": "Homework page error",
"unknown": "Unknown error",
"retry": "Retry"
}
},
"grades": {
"title": "Grades",
"list": {
"title": "Grades",
"description": "View and manage all student grades",
"entry": "Record Grade",
"searchPlaceholder": "Search by student name/no...",
"statusFilter": "Filter by status",
"statusAll": "All statuses",
"statusGraded": "Graded",
"statusPending": "Pending",
"statusExempt": "Exempt",
"statusAbsent": "Absent",
"total": "{count} total",
"colStudent": "Student",
"colClass": "Class",
"colSource": "Source",
"colScore": "Score",
"colStatus": "Status",
"colGradedAt": "Graded At",
"mswNotice": "List query contract is pending, please ensure NEXT_PUBLIC_MSW=1 is enabled."
},
"entry": {
"title": "Record Grade",
"description": "Fill in student grade information",
"submit": "Submit",
"success": "Grade recorded successfully",
"error": "Submission failed",
"classId": "Class ID",
"studentId": "Student ID",
"examId": "Exam ID (optional)",
"examIdPlaceholder": "e.g. exam-001",
"homeworkId": "Homework ID (optional)",
"homeworkIdPlaceholder": "e.g. hw-001",
"score": "Score",
"totalScore": "Total Score",
"feedbackLabel": "Feedback",
"feedbackPlaceholder": "Write feedback to the student...",
"errorClassRequired": "Please fill in Class ID",
"errorStudentRequired": "Please fill in Student ID",
"errorScoreInvalid": "Score is invalid, please enter a non-negative number",
"errorTotalInvalid": "Total score is invalid, please enter a positive number",
"errorSourceRequired": "At least one of Exam ID or Homework ID is required",
"contractPending": "Grade submission contract is @contract-pending, currently backed by MSW. Will switch to real submission once backend mutation is ready."
},
"analytics": {
"title": "Grade Analytics",
"subtitle": "Scope: {scope}",
"notFound": "No analytics data found, grading may not be complete",
"sectionSummary": "Summary",
"sectionDistribution": "Score Distribution",
"sectionRankings": "Student Rankings",
"summaryTotal": "Expected",
"summaryGraded": "Graded",
"summaryAvg": "Average",
"summaryMax": "Max",
"summaryMin": "Min",
"summaryPassRate": "Pass Rate",
"summaryPassScore": "Pass Score",
"emptyDistribution": "No score distribution data",
"emptyRankings": "No ranking data",
"colRank": "Rank",
"colStudentNo": "Student No.",
"colStudentName": "Name",
"colScore": "Score",
"colLevel": "Level"
},
"stats": {
"title": "Grade Statistics",
"subtitle": "Class: {classId} · Period: {period}",
"notFound": "No statistics data found",
"empty": "No subject statistics data",
"sectionSummary": "Subject Summary Cards",
"sectionTable": "Subject Statistics Table",
"studentCount": "{count} students",
"avgScore": "Average",
"maxScore": "Max",
"minScore": "Min",
"passRate": "Pass Rate",
"passCount": "Pass Count",
"failCount": "Fail Count",
"colSubject": "Subject",
"colClass": "Class",
"colStudentCount": "Students",
"colAvgScore": "Average",
"colMaxScore": "Max",
"colMinScore": "Min",
"colPassRate": "Pass Rate",
"colPassCount": "Pass Count",
"colFailCount": "Fail Count"
},
"reportCard": {
"title": "Report Card",
"titleWithName": "{name}'s Report Card",
"subtitle": "No.: {no} · Class: {className} · Period: {period}",
"notFound": "No report card data found",
"sectionBasic": "Student Info",
"sectionEntries": "Subject Grades",
"sectionSummary": "Summary",
"fieldStudentName": "Name",
"fieldStudentNo": "Student No.",
"fieldClass": "Class",
"fieldPeriod": "Period",
"colSubject": "Subject",
"colSource": "Source",
"colScore": "Score",
"colLevel": "Level",
"colFeedback": "Feedback",
"colGradedAt": "Graded At",
"emptyEntries": "No subject grades",
"totalAvgScore": "Total Average",
"totalRank": "Total Rank",
"totalStudents": "Total Students",
"rankValue": "Rank {rank} / {total}"
},
"error": {
"title": "Grades page error",
"unknown": "Unknown error",
"retry": "Retry"
}
},
"analytics": {
"title": "Analytics"
},
"knowledgeGraph": {
"title": "Knowledge Graph"
},
"notifications": {
"title": "Notifications"
},
"aiAssist": {
"title": "AI Assist"
},
"aiLessonPlan": {
"title": "AI Lesson Plan"
},
"aiReport": {
"title": "AI Report"
},
"attendance": {
"title": "Attendance"
},
"questions": {
"title": "Questions"
},
"textbooks": {
"title": "Textbooks"
},
"lessonPlans": {
"title": "Lesson Plans"
},
"coursePlans": {
"title": "Course Plans"
},
"diagnostic": {
"title": "Diagnostic"
},
"errorBook": {
"title": "Error Book"
},
"practice": {
"title": "Practice"
},
"elective": {
"title": "Elective"
},
"leave": {
"title": "Leave Requests"
},
"scheduleChanges": {
"title": "Schedule Changes"
},
"settings": {
"title": "Settings"
},
"students": {
"title": "Students"
}
}