116 lines
3.4 KiB
TypeScript
116 lines
3.4 KiB
TypeScript
import type { AiReviewSection, AiReviewSectionStatus, AiReviewSectionType } from './types';
|
|
|
|
export const SECTION_TYPE_LABELS: Record<AiReviewSectionType, string> = {
|
|
students: '学生',
|
|
rooms: '宿舍',
|
|
transfers: '换宿',
|
|
checkins: '入住记录',
|
|
};
|
|
|
|
export const SECTION_ORDER: AiReviewSectionType[] = ['students', 'rooms', 'transfers', 'checkins'];
|
|
|
|
const SECTION_DEPENDENCIES: Record<AiReviewSectionType, AiReviewSectionType[]> = {
|
|
students: [],
|
|
rooms: [],
|
|
transfers: ['students', 'rooms'],
|
|
checkins: [],
|
|
};
|
|
|
|
export function sectionType(section: Pick<AiReviewSection, 'key' | 'type'>): AiReviewSectionType {
|
|
if (
|
|
section.type === 'students' ||
|
|
section.type === 'rooms' ||
|
|
section.type === 'transfers' ||
|
|
section.type === 'checkins'
|
|
) {
|
|
return section.type;
|
|
}
|
|
const key = section.key as AiReviewSectionType;
|
|
if (key === 'students' || key === 'rooms' || key === 'transfers' || key === 'checkins') {
|
|
return key;
|
|
}
|
|
const prefix = SECTION_ORDER.find((type) => section.key.startsWith(`${type}_`));
|
|
return prefix ?? 'students';
|
|
}
|
|
|
|
export function sectionCount(section: AiReviewSection): number {
|
|
return section.rows.length;
|
|
}
|
|
|
|
export function sectionStatus(section: AiReviewSection): AiReviewSectionStatus {
|
|
return section.status ?? 'pending';
|
|
}
|
|
|
|
export function sectionResultText(section: AiReviewSection): string {
|
|
if (!section.resultSummary) return '';
|
|
try {
|
|
const parsed = JSON.parse(section.resultSummary) as { message?: unknown };
|
|
if (typeof parsed.message === 'string') return parsed.message;
|
|
} catch {
|
|
// Older data may store a plain text summary.
|
|
}
|
|
return section.resultSummary;
|
|
}
|
|
|
|
export const SECTION_STATUS_LABELS: Record<AiReviewSectionStatus, string> = {
|
|
pending: '待确认',
|
|
submitted: '已导入',
|
|
failed: '失败',
|
|
skipped: '已跳过',
|
|
};
|
|
|
|
export type GroupStatus = 'pending' | 'partial' | 'submitted' | 'failed' | 'importing';
|
|
|
|
export const GROUP_STATUS_LABELS: Record<GroupStatus, string> = {
|
|
pending: '待确认',
|
|
partial: '部分完成',
|
|
submitted: '已导入',
|
|
failed: '失败',
|
|
importing: '导入中',
|
|
};
|
|
|
|
export function groupSections(
|
|
sections: AiReviewSection[],
|
|
type: AiReviewSectionType,
|
|
): AiReviewSection[] {
|
|
return sections.filter((section) => sectionType(section) === type);
|
|
}
|
|
|
|
export function groupStatus(
|
|
sections: AiReviewSection[],
|
|
type: AiReviewSectionType,
|
|
submittingKey: string | null,
|
|
submittingGroup: boolean,
|
|
activeType?: AiReviewSectionType,
|
|
): GroupStatus {
|
|
const items = groupSections(sections, type);
|
|
if (items.length === 0) return 'pending';
|
|
if (
|
|
(submittingGroup && type === activeType) ||
|
|
items.some((item) => submittingKey === item.key)
|
|
) {
|
|
return 'importing';
|
|
}
|
|
if (items.some((item) => sectionStatus(item) === 'failed')) return 'failed';
|
|
if (items.every((item) => sectionStatus(item) === 'submitted')) return 'submitted';
|
|
return 'partial';
|
|
}
|
|
|
|
export function dependencyHint(
|
|
sections: AiReviewSection[],
|
|
type: AiReviewSectionType,
|
|
): { step: number; title: string } | null {
|
|
for (const dependencyType of SECTION_DEPENDENCIES[type] ?? []) {
|
|
const matches = groupSections(sections, dependencyType);
|
|
if (matches.length === 0) {
|
|
return { step: -1, title: SECTION_TYPE_LABELS[dependencyType] };
|
|
}
|
|
for (const section of matches) {
|
|
if (sectionStatus(section) !== 'submitted') {
|
|
return { step: sections.indexOf(section), title: section.title };
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|