feat: add tenant student operations console

This commit is contained in:
Codex
2026-06-29 14:55:40 +08:00
parent 6e182977fc
commit 134f7830dd
7 changed files with 665 additions and 24 deletions

View File

@@ -30,19 +30,100 @@ export interface TenantClassItem {
}
export interface TenantStudentItem {
membershipId?: string;
userId: string;
username?: string | null;
name?: string | null;
phone?: string | null;
email?: string | null;
status?: string;
profileId?: string;
regionId?: string | null;
regionName?: string | null;
selectedSchoolId?: string | null;
selectedSchoolName?: string | null;
selectedMajorId?: string | null;
selectedMajorName?: string | null;
questionsAnsweredToday?: number;
masteredWordsCount?: number;
lastSeenAt?: string | null;
classes?: Record<string, unknown>[];
}
export interface TenantStudentInput {
userId?: string;
username?: string | null;
name?: string | null;
phone?: string | null;
email?: string | null;
regionId?: string | null;
selectedSchoolId?: string | null;
selectedMajorId?: string | null;
status?: string;
stats?: Record<string, unknown>;
progress?: Record<string, unknown>;
moduleSelections?: Record<string, unknown>;
}
export interface TenantTeacherItem {
membershipId?: string;
userId: string;
username?: string | null;
name?: string | null;
phone?: string | null;
email?: string | null;
status?: string;
classCount?: number;
}
export interface BulkOperationResult {
total?: number;
successCount?: number;
errorCount?: number;
items?: Record<string, unknown>[];
errors?: Array<{
index?: number;
code?: string;
message?: string;
}>;
}
export interface TenantStudentNoteItem {
id: string;
studentUserId?: string;
noteType?: string;
content?: string;
visibility?: string;
isPinned?: boolean;
metadata?: Record<string, unknown>;
createdByName?: string | null;
createdByUsername?: string | null;
createdAt?: string;
updatedAt?: string;
}
export interface TenantStudentFollowupItem {
id: string;
studentUserId?: string;
studentName?: string | null;
studentPhone?: string | null;
assignedToUserId?: string | null;
assignedToName?: string | null;
classId?: string | null;
className?: string | null;
title?: string;
description?: string | null;
followupType?: string;
priority?: string;
status?: string;
dueAt?: string | null;
completedAt?: string | null;
completedBy?: string | null;
metadata?: Record<string, unknown>;
createdAt?: string;
updatedAt?: string;
}
export interface ImportJobItem {
id: string;
importType?: string;
@@ -447,12 +528,109 @@ export async function loadTenantClasses(limit = 50) {
return apiRequest<{ items?: TenantClassItem[]; scoped?: boolean }>('/api/tenant-admin/classes', { query: { limit } });
}
export async function loadTenantStudents(query: { keyword?: string; classId?: string; limit?: number } = {}) {
export async function loadTenantStudents(query: { keyword?: string; classId?: string; status?: string; limit?: number } = {}) {
return apiRequest<{ items?: TenantStudentItem[]; scoped?: boolean }>('/api/tenant-admin/students', {
query: { ...query, limit: query.limit || 50 },
});
}
export async function upsertTenantStudent(input: TenantStudentInput) {
return apiRequest<{ item?: Record<string, unknown> }>('/api/tenant-admin/students', {
method: 'PUT',
body: input,
});
}
export async function bulkUpsertTenantStudents(students: TenantStudentInput[]) {
return apiRequest<BulkOperationResult>('/api/tenant-admin/students/bulk-upsert', {
method: 'POST',
body: { students },
});
}
export async function updateTenantStudentStatus(input: { userId: string; status: string; reason?: string | null }) {
return apiRequest<{ item?: Record<string, unknown> }>('/api/tenant-admin/students/status', {
method: 'POST',
body: input,
});
}
export async function bulkAssignTenantClassMembers(input: {
classId: string;
assignments: Array<{
userId?: string;
username?: string | null;
name?: string | null;
phone?: string | null;
email?: string | null;
memberType?: string;
status?: string;
metadata?: Record<string, unknown>;
}>;
}) {
return apiRequest<BulkOperationResult>('/api/tenant-admin/classes/members/bulk-assign', {
method: 'POST',
body: input,
});
}
export async function loadTenantTeachers(query: { keyword?: string; limit?: number } = {}) {
return apiRequest<{ items?: TenantTeacherItem[] }>('/api/tenant-admin/teachers', {
query: { ...query, limit: query.limit || 50 },
});
}
export async function loadTenantStudentNotes(studentUserId: string, limit = 20) {
return apiRequest<{ items?: TenantStudentNoteItem[] }>('/api/tenant-admin/students/notes', {
query: { studentUserId, limit },
});
}
export async function upsertTenantStudentNote(input: {
id?: string;
studentUserId: string;
noteType?: string;
content: string;
visibility?: string;
isPinned?: boolean;
metadata?: Record<string, unknown>;
}) {
return apiRequest<{ item?: TenantStudentNoteItem }>('/api/tenant-admin/students/notes', {
method: 'PUT',
body: input,
});
}
export async function loadTenantStudentFollowups(query: {
studentUserId?: string;
status?: string;
assignedToUserId?: string;
limit?: number;
} = {}) {
return apiRequest<{ items?: TenantStudentFollowupItem[] }>('/api/tenant-admin/students/followups', {
query: { ...query, limit: query.limit || 50 },
});
}
export async function upsertTenantStudentFollowup(input: {
id?: string;
studentUserId: string;
assignedToUserId?: string | null;
classId?: string | null;
title: string;
description?: string | null;
followupType?: string;
priority?: string;
status?: string;
dueAt?: string | null;
metadata?: Record<string, unknown>;
}) {
return apiRequest<{ item?: TenantStudentFollowupItem }>('/api/tenant-admin/students/followups', {
method: 'PUT',
body: input,
});
}
export async function loadContentEntriesAdmin() {
return apiRequest<{ items?: ContentEntryAdminItem[] }>('/api/tenant-content/content-entries');
}