import { apiRequest } from './api'; export interface TenantDashboard { scope?: Record; cards?: { students?: Record; learning?: Record; content?: Record; activationCodes?: Record; feedback?: Record; }; paymentStats?: Record; trends?: Record[]; activeHours?: Record[]; questionDistribution?: Record[]; subjectTop?: Record[]; regionStats?: Record[]; planSales?: Record[]; recentActivities?: Record[]; } export interface TenantClassItem { id: string; name: string; code?: string | null; status?: string; regionName?: string | null; studentCount?: number; teacherCount?: number; } 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[]; } 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; progress?: Record; moduleSelections?: Record; } 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[]; 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; 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; createdAt?: string; updatedAt?: string; } export interface TenantStudentCrmPushInput { studentUserIds: string[]; source?: string; title: string; message?: string | null; followupType?: string; priority?: string; assignedToUserId?: string | null; classId?: string | null; dueAt?: string | null; metadata?: Record; idempotencyKey?: string; } export interface TenantStudentFollowupReport { item?: { range?: Record; filters?: Record; summary?: { total?: number; open?: number; inProgress?: number; done?: number; cancelled?: number; highPriority?: number; crmPushTasks?: number; openBacklog?: number; overdueBacklog?: number; completedInRange?: number; completionRate?: number; completedInRangeRate?: number; avgCompleteHours?: number; }; crmQueue?: { total?: number; pending?: number; sent?: number; failed?: number; studentCount?: number; }; byStatus?: Record[]; byType?: Record[]; byPriority?: Record[]; byAssignee?: Record[]; byClass?: Record[]; dailyTrend?: Record[]; overdueItems?: TenantStudentFollowupItem[]; }; } export interface TenantStudentSupervisionRules { windowDays?: number; inactivityDays?: number; minAnswers?: number; lowAccuracyThreshold?: number; wrongQuestionThreshold?: number; vocabularyDueThreshold?: number; staleSessionDays?: number; } export interface TenantStudentSupervisionCandidate { studentUserId: string; studentName?: string | null; studentPhone?: string | null; assignedToUserId?: string | null; classId?: string | null; title?: string; description?: string; followupType?: string; priority?: string; riskScore?: number; reasons?: Record[]; evidence?: Record; } export interface TenantStudentSupervisionPreview { item?: { rules?: TenantStudentSupervisionRules; filters?: Record; totalCandidates?: number; candidates?: TenantStudentSupervisionCandidate[]; }; } export interface TenantStudentSupervisionSchedule { enabled?: boolean; frequency?: 'manual' | 'daily' | 'weekly'; hour?: number; minute?: number; timezone?: string; weekdays?: number[]; } export interface TenantStudentSupervisionRuleItem { id: string; name: string; status?: string; rules?: TenantStudentSupervisionRules; schedule?: TenantStudentSupervisionSchedule; classId?: string | null; className?: string | null; assignedToUserId?: string | null; assignedToName?: string | null; limit?: number; metadata?: Record; lastRunAt?: string | null; nextRunAt?: string | null; lastResult?: Record; createdAt?: string; updatedAt?: string; } export interface TenantStudentSupervisionRulesPayload { items?: TenantStudentSupervisionRuleItem[]; scoped?: boolean; } export interface ImportJobItem { id: string; importType?: string; sourceFormat?: string; status?: string; sourceName?: string | null; totalCount?: number; validCount?: number; successCount?: number; errorCount?: number; warningCount?: number; insertedCount?: number; updatedCount?: number; skippedCount?: number; executionMode?: string; queuedAt?: string | null; lockedAt?: string | null; lockedBy?: string | null; attemptCount?: number; maxAttempts?: number; nextAttemptAt?: string | null; errorMessage?: string | null; createdAt?: string; updatedAt?: string; finishedAt?: string | null; summary?: Record; } export interface ImportJobDetail { item?: ImportJobItem; issueSummary?: Record; itemStatusSummary?: Record; recentIssues?: ImportIssueItem[]; importPostCheck?: Record | null; worker?: { executionMode?: string; queuedAt?: string | null; lockedAt?: string | null; lockedBy?: string | null; attemptCount?: number; maxAttempts?: number; nextAttemptAt?: string | null; lastWorkerError?: Record; }; } export interface ContentEntryAdminItem { id: string; name: string; entryType?: string; visibility?: string; isActive?: boolean; regionName?: string | null; } export interface PublicQuestionBankItem { grantId?: string; sourceQuestionBankId?: string; sourceQuestionBankName?: string; sourceRegionName?: string | null; name?: string; regionName?: string | null; grantScope?: string; questionCount?: number; adoptedId?: string | null; adoptionStatus?: string | null; syncStatus?: string | null; targetEntryId?: string | null; targetCollectionId?: string | null; copiedQuestionCount?: number; lastSyncedAt?: string | null; adoption?: Record | null; } export interface PublicQuestionBankConflictItem { sourceQuestionId?: string; targetQuestionId?: string | null; action?: string; sourceHash?: string; previousSourceHash?: string | null; targetHash?: string | null; } export interface PublicQuestionBankConflictsResult { adoptionId?: string; syncStatus?: string; status?: string; lastSyncedAt?: string | null; updatedAt?: string; conflictCount?: number; counts?: Record; conflicts?: PublicQuestionBankConflictItem[]; } export interface TenantContentNotificationItem { id: string; notificationType?: string; status?: 'unread' | 'read' | 'dismissed' | 'resolved'; severity?: 'info' | 'success' | 'warning' | 'error'; adoptionId?: string | null; sourceQuestionBankId?: string | null; title?: string; message?: string; actionLabel?: string | null; actionPath?: string | null; metadata?: Record; createdAt?: string; updatedAt?: string; readAt?: string | null; resolvedAt?: string | null; } export interface UserNotificationAdminItem { id: string; userId?: string; userName?: string | null; userPhone?: string | null; userAvatarUrl?: string | null; notificationType?: string; status?: 'unread' | 'read' | 'dismissed' | 'archived'; severity?: 'info' | 'success' | 'warning' | 'error'; title?: string; message?: string; actionLabel?: string | null; actionPath?: string | null; sourceType?: string | null; sourceId?: string | null; metadata?: Record; createdBy?: string | null; createdByName?: string | null; readAt?: string | null; createdAt?: string; updatedAt?: string; } export interface ImportIssueItem { id: string; rowNo?: number | null; severity?: string; code?: string; fieldPath?: string | null; message?: string; externalId?: string | null; itemStatus?: string | null; createdAt?: string; } export interface ImportFieldMapping { importType?: string; title?: string; description?: string; fields?: Array<{ field?: string; label?: string; required?: boolean; aliases?: string[]; description?: string; example?: unknown; }>; requiredFields?: string[]; } export interface ImportTemplateItem { importType?: string; format?: string; fileName?: string; mimeType?: string; contentBase64?: string; contentPreview?: string; fields?: Record[]; } export interface MediaAnalyticsSummary { scope?: Record; assetAccess?: { totalEvents?: number; grantedEvents?: number; deniedEvents?: number; downloads?: number; previews?: number; uploadSigns?: number; uploadConfirms?: number; uniqueUsers?: number; watermarkEvents?: number; }; videoPlay?: { totalEvents?: number; issuedEvents?: number; startedEvents?: number; completedEvents?: number; freePlays?: number; svipPlays?: number; quotaPlays?: number; consumedQuota?: number; watchedSeconds?: number; avgCompletionRate?: number; uniqueUsers?: number; watermarkEvents?: number; }; assetTop?: Record[]; videoTop?: Record[]; daily?: Record[]; traceMatches?: { assets?: number; videos?: number } | null; } export interface MediaAssetAccessEvent { id: string; assetId?: string | null; assetTitle?: string | null; assetType?: string | null; userId?: string | null; userLabel?: string | null; actorRole?: string; accessType?: string; visibility?: string | null; storageProvider?: string | null; disposition?: string | null; expiresInSec?: number | null; signatureMode?: string | null; result?: string; denyCode?: string | null; ipAddress?: string | null; userAgent?: string | null; watermarkTraceId?: string | null; watermark?: Record | null; createdAt?: string; } export interface MediaVideoPlayEvent { id: string; videoId?: string; videoTitle?: string | null; assetId?: string | null; questionId?: string | null; userId?: string; userLabel?: string | null; status?: string; accessMode?: string; consumedQuota?: number; playback?: Record | null; signedUrlExpiresAt?: string | null; ipAddress?: string | null; userAgent?: string | null; signatureMode?: string | null; provider?: string | null; watermarkTraceId?: string | null; watermark?: Record | null; createdAt?: string; updatedAt?: string; } export interface ImportPreviewResult { job?: { id: string; status?: string; totalCount?: number; validCount?: number; errorCount?: number; warningCount?: number; }; items?: Array<{ rowNo?: number; status?: string; externalId?: string | null; normalized?: Record | null; issues?: ImportIssueItem[]; }>; issues?: ImportIssueItem[]; } export type ImportType = 'questions' | 'vocabulary' | 'handbook' | 'scoreline' | 'videos'; export type ImportSourceFormat = 'json' | 'csv' | 'excel'; export interface ImportRequestBody { sourceFormat?: ImportSourceFormat; sourceName?: string; payload?: unknown; items?: unknown[]; csvText?: string; fileBase64?: string; allowPartial?: boolean; executionMode?: 'sync' | 'async'; previewJobId?: string; jobId?: string; regionId?: string; entryId?: string; contentNodeId?: string; collectionId?: string; fieldMappingOverrides?: Record; fieldMappingPreset?: ImportFieldMapping | null; } export interface CouponItem { id: string; code: string; status?: string; campaignName?: string | null; planId?: string | null; discountType?: string | null; discountValue?: number | string | null; validFrom?: string | null; validTo?: string | null; usedCount?: number; maxUses?: number | null; minOrderAmountCents?: number; maxDiscountCents?: number | null; perUserLimit?: number; firstOrderOnly?: boolean; allowedPlanIds?: string[]; allowedRegionIds?: string[]; metadata?: Record; source?: string | null; remark?: string | null; } export interface CouponInput { id?: string; code: string; planId?: string | null; discountType?: 'fixed' | 'percent' | null; discountValue?: number | string | null; validFrom?: string | null; validTo?: string | null; maxUses?: number | null; status?: 'active' | 'disabled' | 'archived'; campaignName?: string | null; minOrderAmountCents?: number; maxDiscountCents?: number | null; perUserLimit?: number; firstOrderOnly?: boolean; allowedPlanIds?: string[]; allowedRegionIds?: string[]; metadata?: Record; source?: string | null; remark?: string | null; } export interface CouponRedemptionItem { id: string; couponId?: string | null; couponCode?: string | null; campaignName?: string | null; userId?: string | null; userName?: string | null; userPhone?: string | null; planId?: string | null; planName?: string | null; orderId?: string | null; orderNo?: string | null; status?: string; discountAppliedCents?: number; regionId?: string | null; regionName?: string | null; source?: string | null; remark?: string | null; claimedAt?: string | null; usedAt?: string | null; createdAt?: string; } export interface CouponReport { startDate?: string; endDate?: string; claimCount?: number; usedCount?: number; discountCents?: number; paidAmountCents?: number; conversionRate?: number; byCoupon?: Array>; byCampaign?: Array>; daily?: Array>; } export interface PointActivityTaskItem { id: string; legacyId?: string | null; code?: string; title?: string; description?: string | null; taskType?: 'daily_check_in' | 'feedback_submit' | 'feedback_resolved' | 'practice_complete' | 'vocabulary_review' | 'mock_exam_submit' | 'manual'; rewardPoints?: number; claimLimitPerUser?: number; periodType?: 'once' | 'daily' | 'weekly' | 'monthly' | 'unlimited'; validFrom?: string | null; validTo?: string | null; status?: 'active' | 'disabled' | 'archived'; order?: number; metadata?: Record; claimCount?: number; claimUserCount?: number; createdAt?: string; updatedAt?: string; } export interface PointActivityTaskInput { id?: string; legacyId?: string | null; code: string; title: string; description?: string | null; taskType?: PointActivityTaskItem['taskType']; rewardPoints: number; claimLimitPerUser?: number; periodType?: PointActivityTaskItem['periodType']; validFrom?: string | null; validTo?: string | null; status?: PointActivityTaskItem['status']; order?: number; metadata?: Record; } export interface PointExchangeItem { id: string; legacyId?: string | null; code?: string; title?: string; description?: string | null; costPoints?: number; itemType?: 'coupon' | 'manual' | 'asset' | 'custom'; couponId?: string | null; couponCode?: string | null; assetId?: string | null; stockTotal?: number | null; stockUsed?: number; stockRemaining?: number | null; perUserLimit?: number; validFrom?: string | null; validTo?: string | null; status?: 'active' | 'disabled' | 'archived'; order?: number; metadata?: Record; orderCount?: number; createdAt?: string; updatedAt?: string; } export interface PointExchangeItemInput { id?: string; legacyId?: string | null; code: string; title: string; description?: string | null; costPoints: number; itemType?: PointExchangeItem['itemType']; couponId?: string | null; assetId?: string | null; stockTotal?: number | null; perUserLimit?: number; validFrom?: string | null; validTo?: string | null; status?: PointExchangeItem['status']; order?: number; metadata?: Record; } export interface PointsRiskReport { range?: { timeRange?: '7d' | '30d' | '90d'; startDate?: string; endDate?: string; }; thresholds?: Record; summary?: { eventCount?: number; affectedUsers?: number; earnedPoints?: number; spentPoints?: number; netPoints?: number; maxEarnedEvent?: number; maxSpentEvent?: number; suspiciousUserCount?: number; suspiciousEventCount?: number; riskScore?: number; }; eventBreakdown?: Array>; suspiciousUsers?: Array>; suspiciousEvents?: Array>; suspiciousTasks?: Array>; suspiciousExchanges?: Array>; dailyTrend?: Array>; } export interface FeedbackReport { range?: { timeRange?: '7d' | '30d' | '90d'; startDate?: string; endDate?: string; }; summary?: { total?: number; newInRange?: number; pending?: number; accepted?: number; rejected?: number; resolved?: number; closed?: number; highPriority?: number; pendingBacklog?: number; handledInRange?: number; rewardedReports?: number; rewardPoints?: number; resolutionRate?: number; handledRate?: number; rejectionRate?: number; avgHandleHours?: number; }; byStatus?: Array>; byType?: Array>; byCategory?: Array>; byPriority?: Array>; dailyTrend?: Array>; topQuestions?: Array>; topHandlers?: Array>; recentUnhandled?: Array>; } export interface CodeBatchItem { id: string; name?: string | null; batchNo?: string | null; totalCount?: number; usedCount?: number; status?: string; } export interface CrmConfigItem { id?: string; enabled?: boolean; url?: string | null; secretRef?: string | null; formName?: string | null; examType?: string | null; timeoutSec?: number; delaySec?: number; assignmentMode?: 'none' | 'direct' | 'round_robin' | 'referrer'; assignmentPool?: string[]; assignmentCursor?: number; assignmentConfig?: Record; updatedAt?: string; } export interface CrmQueueItem { id: string; recordId?: string | null; status?: string; scheduledAt?: string | null; attempts?: number; nextAttemptAt?: string | null; lastError?: string | null; lastHttpCode?: number | null; leadId?: string | null; sentAt?: string | null; source?: string | null; targetUrl?: string | null; payload?: Record; createdAt?: string; } export interface CommissionSettingsItem { defaultRate?: number | string; minSettlementCents?: number; settlementCycle?: string; config?: Record; updatedAt?: string; } export interface CommissionSummaryItem { tenantId?: string; startDate?: string; endDate?: string; referrerUserId?: string | null; defaultRate?: number | string; minSettlementCents?: number; sourceCount?: number; paidUserCount?: number; grossAmountCents?: number; commissionAmountCents?: number; byReferrer?: Record[]; } export interface CommissionOrderItem { sourceType?: string; sourceId?: string; sourceNo?: string | null; referrerUserId?: string; referrerName?: string | null; referrerPhone?: string | null; studentUserId?: string | null; studentName?: string | null; grossAmountCents?: number; commissionRate?: number | string; commissionAmountCents?: number; rateSource?: string; settlementId?: string | null; sourcePaidAt?: string | null; } export interface CommissionSettlementItem { id: string; settlementNo?: string; referrerUserId?: string; referrerName?: string | null; referrerPhone?: string | null; status?: string; periodStart?: string; periodEnd?: string; sourceCount?: number; paidUserCount?: number; grossAmountCents?: number; commissionAmountCents?: number; defaultRate?: number | string; effectiveRate?: number | string; reviewedBy?: string | null; reviewedAt?: string | null; paidBy?: string | null; paidAt?: string | null; paymentMethod?: string | null; paymentAccount?: string | null; remark?: string | null; metadata?: Record; createdAt?: string; updatedAt?: string; } export interface CommissionSettlementExportItem { settlementId?: string; filename?: string; format?: 'csv' | 'json'; mimeType?: string; rowCount?: number; contentBase64?: string; sha256?: string; sizeBytes?: number; } export interface CommissionSettlementProofItem { id: string; settlementId?: string; proofType?: string; status?: string; title?: string | null; description?: string | null; assetId?: string | null; externalUrl?: string | null; amountCents?: number | null; paymentMethod?: string | null; paymentAccount?: string | null; paidAt?: string | null; submittedBy?: string | null; submittedByName?: string | null; reviewedBy?: string | null; reviewedByName?: string | null; reviewedAt?: string | null; reviewNote?: string | null; metadata?: Record; createdAt?: string; updatedAt?: string; } export interface TenantOverview { id?: string; name?: string; slug?: string; status?: string; brandName?: string; shortName?: string; slogan?: string | null; logoUrl?: string | null; theme?: Record; featureFlags?: Record; adminFeatureFlags?: Record; publicConfig?: Record; } export interface TenantThemeTemplateItem { code: string; name: string; description?: string | null; previewImageUrl?: string | null; theme?: Record; publicAssets?: Record; sortOrder?: number; status?: string; updatedAt?: string; } export interface TenantThemeConfigItem { tenantId?: string; activeTemplateCode?: string | null; activeTemplateName?: string | null; activeTheme?: Record; activePublicAssets?: Record; draftTemplateCode?: string | null; draftTemplateName?: string | null; draftTheme?: Record; draftPublicAssets?: Record; status?: string; publishedAt?: string | null; updatedAt?: string | null; brandingTheme?: Record; brandingPublicAssets?: Record; } export interface TenantThemeDraftInput { templateCode: string; theme?: Record; publicAssets?: Record; } export interface TenantPermissionCatalogItem { key: string; label?: string; description?: string; } export interface TenantPermissionsPayload { current?: Record; permissions?: TenantPermissionCatalogItem[]; menuGroups?: TenantPermissionCatalogItem[]; fieldGroups?: TenantPermissionCatalogItem[]; fieldCatalog?: TenantPermissionCatalogItem[]; roleDefaults?: Record; } export interface TenantRoleTemplateItem { id: string; code: string; name: string; description?: string | null; baseRole?: string; status?: string; permissions?: Record; menuPermissions?: Record; modulePermissions?: Record; fieldPermissions?: Record; dataScope?: Record; isSystem?: boolean; sortOrder?: number; createdAt?: string; updatedAt?: string; } export interface TenantRoleTemplateInput { id?: string; code: string; name: string; description?: string | null; baseRole: string; status?: string; permissions?: Record; menuPermissions?: Record; modulePermissions?: Record; fieldPermissions?: Record; dataScope?: Record; sortOrder?: number; } export interface TenantMemberItem { id: string; userId: string; username?: string | null; name?: string | null; phone?: string | null; email?: string | null; avatarUrl?: string | null; primaryRole?: string | null; role: string; status?: string; permissions?: Record; roleTemplateId?: string | null; roleTemplateCode?: string | null; roleTemplateName?: string | null; menuPermissions?: Record; modulePermissions?: Record; fieldPermissions?: Record; dataScope?: Record; legacyRole?: string | null; lastSeenAt?: string | null; createdAt?: string; updatedAt?: string; } export interface TenantMemberInput { membershipId?: string; userId?: string; username?: string | null; name?: string | null; phone?: string | null; email?: string | null; primaryRole?: string | null; role: string; status?: string; permissions?: Record; roleTemplateId?: string | null; } export async function loadTenantDashboard(timeRange = '30d') { return apiRequest<{ item?: TenantDashboard }>('/api/tenant-admin/dashboard', { query: { timeRange } }); } export async function loadTenantOverview() { return apiRequest<{ item?: TenantOverview }>('/api/tenant-admin/overview'); } export async function loadThemeTemplates() { return apiRequest<{ items?: TenantThemeTemplateItem[] }>('/api/tenant-admin/theme-templates'); } export async function loadTenantTheme() { return apiRequest<{ item?: TenantThemeConfigItem }>('/api/tenant-admin/theme'); } export async function previewTenantTheme(input: TenantThemeDraftInput) { return apiRequest<{ item?: TenantThemeConfigItem & { templateName?: string | null } }>('/api/tenant-admin/theme/preview', { method: 'POST', body: input, }); } export async function publishTenantTheme(input: { useDraft?: boolean; templateCode?: string; theme?: Record; publicAssets?: Record } = {}) { return apiRequest<{ item?: TenantThemeConfigItem & { templateName?: string | null } }>('/api/tenant-admin/theme/publish', { method: 'POST', body: { useDraft: input.useDraft ?? true, ...input }, }); } export async function loadTenantPermissions() { return apiRequest('/api/tenant-admin/permissions'); } 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; 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 }>('/api/tenant-admin/students', { method: 'PUT', body: input, }); } export async function bulkUpsertTenantStudents(students: TenantStudentInput[]) { return apiRequest('/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 }>('/api/tenant-admin/students/status', { method: 'POST', body: input, }); } export async function pushTenantStudentsToCrm(input: TenantStudentCrmPushInput) { return apiRequest('/api/tenant-admin/students/crm-push', { 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; }>; }) { return apiRequest('/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; }) { 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 loadTenantStudentFollowupReport(query: { timeRange?: '7d' | '30d' | '90d'; startDate?: string; endDate?: string; assignedToUserId?: string; followupType?: string; classId?: string; limit?: number; } = {}) { return apiRequest('/api/tenant-admin/students/followups/report', { query: { ...query, limit: query.limit || 10 }, }); } export async function previewTenantStudentSupervision(query: TenantStudentSupervisionRules & { classId?: string; assignedToUserId?: string; limit?: number; } = {}) { return apiRequest('/api/tenant-admin/students/supervision/preview', { query: { ...query, limit: query.limit || 20 }, }); } export async function loadTenantStudentSupervisionRules(query: { status?: string; limit?: number; } = {}) { return apiRequest('/api/tenant-admin/students/supervision/rules', { query: { ...query, limit: query.limit || 50 }, }); } export async function upsertTenantStudentSupervisionRule(input: { id?: string; name: string; status?: string; rules?: TenantStudentSupervisionRules; schedule?: TenantStudentSupervisionSchedule; classId?: string | null; assignedToUserId?: string | null; limit?: number; metadata?: Record; }) { return apiRequest<{ item?: TenantStudentSupervisionRuleItem }>('/api/tenant-admin/students/supervision/rules', { method: 'PUT', body: input, }); } export async function generateTenantStudentSupervision(input: { rules?: TenantStudentSupervisionRules; classId?: string | null; assignedToUserId?: string | null; studentUserIds?: string[]; dueAt?: string | null; batchKey?: string; limit?: number; metadata?: Record; }) { return apiRequest('/api/tenant-admin/students/supervision/generate', { method: 'POST', body: input, }); } 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; }) { 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'); } export async function loadImportJobs(limit = 20) { return apiRequest<{ items?: ImportJobItem[] }>('/api/tenant-content/imports', { query: { limit } }); } export async function loadImportJobDetail(jobId: string) { return apiRequest('/api/tenant-content/imports/detail', { query: { jobId } }); } export async function loadImportIssues(jobId: string) { return apiRequest<{ items?: ImportIssueItem[] }>('/api/tenant-content/imports/issues', { query: { jobId } }); } export async function loadPublicQuestionBanks() { return apiRequest<{ items?: PublicQuestionBankItem[] }>('/api/tenant-content/public-question-banks'); } export async function loadTenantContentNotifications(input: { status?: 'unread' | 'read' | 'dismissed' | 'resolved'; notificationType?: string; adoptionId?: string; limit?: number; } = {}) { return apiRequest<{ items?: TenantContentNotificationItem[]; summary?: Record }>('/api/tenant-content/notifications', { query: input, }); } export async function loadUserNotifications(input: { userId?: string; status?: UserNotificationAdminItem['status']; notificationType?: string; limit?: number; } = {}) { return apiRequest<{ items?: UserNotificationAdminItem[]; summary?: Record }>('/api/tenant-admin/user-notifications', { query: { ...input, limit: input.limit || 100 }, }); } export async function updateTenantContentNotificationStatus(input: { notificationIds: string[]; status: 'read' | 'dismissed' | 'resolved'; }) { return apiRequest<{ item?: Record }>('/api/tenant-content/notifications/status', { method: 'POST', body: input, }); } export async function adoptPublicQuestionBank(input: { grantId: string; entryName?: string; collectionName?: string; copyLimit?: number }) { return apiRequest<{ item?: Record }>('/api/tenant-content/public-question-banks/adopt', { method: 'POST', body: input, }); } export async function syncPublicQuestionBank(input: { adoptionId: string; copyLimit?: number }) { return apiRequest<{ item?: Record; sync?: Record }>('/api/tenant-content/public-question-banks/sync', { method: 'POST', body: input, }); } export async function loadPublicQuestionBankConflicts(adoptionId: string) { return apiRequest<{ item?: PublicQuestionBankConflictsResult }>('/api/tenant-content/public-question-banks/conflicts', { query: { adoptionId }, }); } export async function resolvePublicQuestionBankConflict(input: { adoptionId: string; sourceQuestionId: string; resolution: 'accept_platform' | 'keep_local' }) { return apiRequest<{ item?: Record }>('/api/tenant-content/public-question-banks/conflicts/resolve', { method: 'POST', body: input, }); } export async function resolvePublicQuestionBankConflicts(input: { adoptionId: string; sourceQuestionIds: string[]; resolution: 'accept_platform' | 'keep_local'; limit?: number; }) { return apiRequest<{ item?: Record }>('/api/tenant-content/public-question-banks/conflicts/resolve-batch', { method: 'POST', body: input, }); } export async function runImportPostCheck(jobId: string) { return apiRequest<{ item?: Record }>('/api/tenant-content/imports/post-check', { method: 'POST', body: { jobId }, }); } export async function loadImportPostCheck(jobId: string) { return apiRequest<{ item?: Record }>('/api/tenant-content/imports/post-check', { query: { jobId }, }); } export async function loadImportFieldMapping(importType: string) { return apiRequest<{ item?: ImportFieldMapping }>('/api/tenant-content/imports/field-mapping', { query: { importType }, }); } export async function loadImportTemplate(importType: string, format: 'json' | 'csv' = 'json') { return apiRequest<{ item?: ImportTemplateItem }>('/api/tenant-content/imports/templates', { query: { importType, format }, }); } export async function loadMediaAnalyticsSummary(query: { timeRange?: '7d' | '30d' | '90d'; assetId?: string; videoId?: string; userId?: string; traceId?: string; limit?: number; } = {}) { return apiRequest('/api/tenant-content/media-analytics/summary', { query }); } export async function loadMediaAssetAccessEvents(query: { timeRange?: '7d' | '30d' | '90d'; assetId?: string; userId?: string; accessType?: string; result?: 'granted' | 'denied'; traceId?: string; limit?: number; } = {}) { return apiRequest<{ scope?: Record; items?: MediaAssetAccessEvent[] }>('/api/tenant-content/media-analytics/asset-events', { query: { ...query, limit: query.limit || 100 }, }); } export async function loadMediaVideoPlayEvents(query: { timeRange?: '7d' | '30d' | '90d'; videoId?: string; userId?: string; accessMode?: 'free' | 'svip' | 'video_quota'; status?: 'issued' | 'started' | 'completed' | 'expired' | 'revoked'; traceId?: string; limit?: number; } = {}) { return apiRequest<{ scope?: Record; items?: MediaVideoPlayEvent[] }>('/api/tenant-content/media-analytics/video-events', { query: { ...query, limit: query.limit || 100 }, }); } export async function previewContentImport(importType: ImportType, body: ImportRequestBody) { return apiRequest(`/api/tenant-content/imports/preview/${importType}`, { method: 'POST', body, }); } export async function executeContentImport(importType: ImportType, body: ImportRequestBody) { return apiRequest<{ item?: Record; preview?: ImportPreviewResult }>(`/api/tenant-content/imports/${importType}`, { method: 'POST', body, }); } export async function loadCoupons(query: { status?: string; campaignName?: string; limit?: number } = {}) { return apiRequest<{ items?: CouponItem[] }>('/api/tenant-admin/coupons', { query }); } export async function upsertCoupon(input: CouponInput) { return apiRequest<{ item?: CouponItem }>('/api/tenant-admin/coupons', { method: 'PUT', body: input, }); } export async function loadCouponRedemptions(query: { couponId?: string; status?: 'claimed' | 'pending' | 'used' | 'cancelled' | 'expired'; limit?: number; } = {}) { return apiRequest<{ items?: CouponRedemptionItem[] }>('/api/tenant-admin/coupons/redemptions', { query: { ...query, limit: query.limit || 50 }, }); } export async function loadCouponReport(query: { startDate?: string; endDate?: string; couponId?: string; campaignName?: string; } = {}) { return apiRequest<{ item?: CouponReport }>('/api/tenant-admin/coupons/report', { query }); } export async function loadPointActivityTasks(query: { status?: string; taskType?: string; limit?: number } = {}) { return apiRequest<{ items?: PointActivityTaskItem[] }>('/api/tenant-admin/point-activity-tasks', { query: { ...query, limit: query.limit || 100 }, }); } export async function upsertPointActivityTask(input: PointActivityTaskInput) { return apiRequest<{ item?: PointActivityTaskItem }>('/api/tenant-admin/point-activity-tasks', { method: 'PUT', body: input, }); } export async function loadPointActivityClaims(query: { taskId?: string; userId?: string; limit?: number } = {}) { return apiRequest<{ items?: Record[] }>('/api/tenant-admin/point-activity-claims', { query: { ...query, limit: query.limit || 100 }, }); } export async function loadPointExchangeItems(query: { status?: string; itemType?: string; limit?: number } = {}) { return apiRequest<{ items?: PointExchangeItem[] }>('/api/tenant-admin/point-exchange-items', { query: { ...query, limit: query.limit || 100 }, }); } export async function upsertPointExchangeItem(input: PointExchangeItemInput) { return apiRequest<{ item?: PointExchangeItem }>('/api/tenant-admin/point-exchange-items', { method: 'PUT', body: input, }); } export async function loadPointExchangeOrders(query: { itemId?: string; userId?: string; status?: 'completed' | 'pending_fulfillment' | 'cancelled'; limit?: number; } = {}) { return apiRequest<{ items?: Record[] }>('/api/tenant-admin/point-exchange-orders', { query: { ...query, limit: query.limit || 100 }, }); } export async function loadPointsRiskReport(query: { timeRange?: '7d' | '30d' | '90d'; highEarnedThreshold?: number; highClaimThreshold?: number; highEventPointsThreshold?: number; highRedeemThreshold?: number; limit?: number; } = {}) { return apiRequest<{ item?: PointsRiskReport }>('/api/tenant-admin/points-risk-report', { query: { ...query, limit: query.limit || 20 }, }); } export async function loadFeedbackReport(query: { timeRange?: '7d' | '30d' | '90d'; limit?: number; } = {}) { return apiRequest<{ item?: FeedbackReport }>('/api/tenant-admin/feedbacks/report', { query: { ...query, limit: query.limit || 20 }, }); } export async function loadCodeBatches() { return apiRequest<{ items?: CodeBatchItem[] }>('/api/tenant-admin/code-batches'); } export async function loadActivationCodes(limit = 20) { return apiRequest<{ items?: Record[] }>('/api/tenant-admin/activation-codes', { query: { limit } }); } export async function loadTenantDomains() { return apiRequest<{ items?: Record[] }>('/api/tenant-admin/domains'); } export async function loadPaymentAccounts() { return apiRequest<{ items?: Record[] }>('/api/tenant-admin/payment-accounts'); } export async function loadAuthProviders() { return apiRequest<{ items?: Record[] }>('/api/tenant-admin/auth-providers'); } export async function loadRoleTemplates() { return apiRequest<{ items?: TenantRoleTemplateItem[] }>('/api/tenant-admin/role-templates'); } export async function upsertRoleTemplate(input: TenantRoleTemplateInput) { return apiRequest<{ item?: TenantRoleTemplateItem }>('/api/tenant-admin/role-templates', { method: 'PUT', body: input, }); } export async function disableRoleTemplate(roleTemplateId: string) { return apiRequest<{ item?: TenantRoleTemplateItem }>('/api/tenant-admin/role-templates/disable', { method: 'POST', body: { roleTemplateId }, }); } export async function loadTenantMembers(query: { keyword?: string; role?: string; status?: string; limit?: number } = {}) { return apiRequest<{ items?: TenantMemberItem[] }>('/api/tenant-admin/members', { query: { ...query, limit: query.limit || 50 }, }); } export async function upsertTenantMember(input: TenantMemberInput) { return apiRequest<{ item?: TenantMemberItem }>('/api/tenant-admin/members', { method: 'PUT', body: input, }); } export async function disableTenantMember(membershipId: string) { return apiRequest<{ item?: TenantMemberItem }>('/api/tenant-admin/members/disable', { method: 'POST', body: { membershipId }, }); } export async function loadCrmConfig() { return apiRequest<{ item?: CrmConfigItem | null }>('/api/crm/config'); } export async function upsertCrmConfig(input: { enabled?: boolean; url?: string | null; secretRef?: string | null; formName?: string | null; examType?: string | null; timeoutSec?: number; delaySec?: number; assignmentMode?: 'none' | 'direct' | 'round_robin' | 'referrer'; assignmentPool?: string[]; assignmentConfig?: Record; }) { return apiRequest<{ item?: CrmConfigItem }>('/api/crm/config', { method: 'PUT', body: input, }); } export async function loadCrmQueue(limit = 20, status?: string) { return apiRequest<{ items?: CrmQueueItem[] }>('/api/crm/queue', { query: { limit, status } }); } export async function loadCommissionSettings() { return apiRequest<{ item?: CommissionSettingsItem }>('/api/commission/settings'); } export async function updateCommissionSettings(input: { defaultRate: number; minSettlementCents?: number; settlementCycle?: string; config?: Record; }) { return apiRequest<{ item?: CommissionSettingsItem }>('/api/commission/settings', { method: 'PUT', body: input, }); } export async function updateMemberCommissionRate(input: { userId: string; commissionRate?: number | null; commissionConfig?: Record; }) { return apiRequest<{ item?: Record }>('/api/commission/member-rate', { method: 'PUT', body: input, }); } export async function loadCommissionSummary(query: { startDate?: string; endDate?: string; referrerUserId?: string } = {}) { return apiRequest<{ item?: CommissionSummaryItem }>('/api/commission/summary', { query }); } export async function loadCommissionOrders(query: { startDate?: string; endDate?: string; referrerUserId?: string; limit?: number } = {}) { return apiRequest<{ items?: CommissionOrderItem[]; scope?: Record }>('/api/commission/orders', { query: { ...query, limit: query.limit || 50 }, }); } export async function loadCommissionSettlements(query: { status?: string; referrerUserId?: string; limit?: number } = {}) { return apiRequest<{ items?: CommissionSettlementItem[] }>('/api/commission/settlements', { query: { ...query, limit: query.limit || 50 }, }); } export async function exportCommissionSettlement(settlementId: string, format: 'csv' | 'json' = 'csv') { return apiRequest<{ item?: CommissionSettlementExportItem }>('/api/commission/settlements/export', { query: { settlementId, format }, }); } export async function loadCommissionSettlementProofs(settlementId: string) { return apiRequest<{ items?: CommissionSettlementProofItem[] }>('/api/commission/settlements/proofs', { query: { settlementId }, }); } export async function generateCommissionSettlement(input: { referrerUserId: string; startDate: string; endDate: string; status?: string; remark?: string | null; }) { return apiRequest<{ item?: CommissionSettlementItem & { items?: CommissionOrderItem[] } }>('/api/commission/settlements/generate', { method: 'POST', body: input, }); } export async function updateCommissionSettlementStatus(input: { settlementId: string; status: string; paymentMethod?: string | null; paymentAccount?: string | null; remark?: string | null; metadata?: Record; }) { return apiRequest<{ item?: CommissionSettlementItem }>('/api/commission/settlements/status', { method: 'POST', body: input, }); } export async function createCommissionSettlementProof(input: { settlementId: string; proofType?: string; title?: string | null; description?: string | null; assetId?: string | null; externalUrl?: string | null; amountCents?: number | null; paymentMethod?: string | null; paymentAccount?: string | null; paidAt?: string | null; metadata?: Record; }) { return apiRequest<{ item?: CommissionSettlementProofItem }>('/api/commission/settlements/proofs', { method: 'POST', body: input, }); } export async function updateCommissionSettlementProofStatus(input: { proofId: string; status: string; reviewNote?: string | null; metadata?: Record; }) { return apiRequest<{ item?: CommissionSettlementProofItem }>('/api/commission/settlements/proofs/status', { method: 'POST', body: input, }); }