export interface ParsePointer { __type: 'Pointer'; className: string; objectId: string; } export interface AdminIdentity { passwordResetRequired?: boolean; objectId: string; username: string; displayName: string; company?: ParsePointer; roles: string[]; isSuperAdmin: boolean; operationsRole: OperationsRole; } export interface AdminSession { passwordResetRequired?: boolean; objectId: string; username: string; sessionToken: string; company?: ParsePointer; displayName?: string; roles?: string[]; isSuperAdmin?: boolean; operationsRole?: OperationsRole; } export type OperationsRole = 'ops-manager' | 'ops-staff' | 'ops-auditor'; export interface CloudEnvelope { success: boolean; data: T; error?: string; message?: string; } export interface LegacyCloudEnvelope { retcode: number; retmsg?: string; result: T; } export interface AppMigrationStatus { implemented: string[]; blocked: Record; } export interface AdminResourceCatalogItem { className: string; label: string; writable: boolean; route: string; } export interface AdminResourceCatalog { total: number; resources: AdminResourceCatalogItem[]; } export interface AdminFieldSchema { name: string; type: string; targetClass?: string; required?: boolean; writable: boolean; } export interface AdminClassSchema { className: string; label: string; fields: AdminFieldSchema[]; writable: boolean; creatable?: boolean; supportsSoftDelete: boolean; } export interface ResourcePage { className: string; page: number; pageSize: number; total: number; results: Record[]; } export interface DashboardMetric { className: string; label: string; count: number; route: string; } export interface DashboardData { identity: AdminIdentity; metrics: DashboardMetric[]; migration: { sourceObjects: number; migratedClasses: number; registeredRecords: number; cloudFunctions: number; }; } export interface OperationsPage = Record> { page: number; pageSize: number; total: number; items: T[]; refreshedAt?: string; } export type LegacySyncHealth = 'healthy' | 'delayed' | 'error' | 'not_configured' | 'never_synced'; export interface LegacySyncDataset extends Record { key: string; label: string; legacyCount: number | null; parseCount: number; difference: number | null; cursor: string; lastSuccessAt: string; lastAttemptAt: string; lagSeconds: number | null; health: LegacySyncHealth; healthLabel: string; pendingFailures: number; conflicts: number; checksum?: string; } export interface LegacySyncStatus { configured: boolean; bridgeReachable: boolean; bridgeUrl: string; manifestTime: string; serverTime: string; refreshedAt: string; overallHealth: LegacySyncHealth; overallHealthLabel: string; totalFailures: number; totalConflicts: number; datasets: LegacySyncDataset[]; message: string; } export interface LegacySyncRunResult { dataset: string; processed: number; created: number; updated: number; deleted: number; conflicts: number; failures: number; hasMore: boolean; cursor: string; startedAt: string; finishedAt: string; pages?: number; } export interface OperationsSyncIndicator { state: 'synced' | 'delayed' | 'not_configured'; label: string; lastSuccessAt: string; lagSeconds: number | null; } export interface MemberSummary { total: number; todayNew: number; sevenDayNew: number; studying: number; followUp: number; } export interface MemberAgentOption extends Record { objectId: string; userId: number; displayName: string; } export interface MemberPage extends OperationsPage { summary: MemberSummary; agents: MemberAgentOption[]; sync?: OperationsSyncIndicator; sourceStatus?: { available: boolean; message: string; total: number; memberCount: number; coachEvidenceCount: number; storeCount: number; unknownCount: number; refreshedAt: string; }; } export interface MemberImportPreviewRow { rowNumber: number; displayName: string; nickname: string; mobileMasked: string; gender: string; grade: string; parentUserId: number; action: 'create' | 'resumed' | 'error'; issues: string[]; } export interface MemberImportPreview { batchId: string; fileName: string; expiresAt: string; summary: { total: number; create: number; resumed: number; error: number }; rows: MemberImportPreviewRow[]; } export interface MemberImportResult { batchId: string; total: number; created: number; resumed: number; items: Array<{ objectId: string; userId: number; displayName: string; mobileMasked: string; resumed: boolean }>; } export interface MemberListSort { field: 'registeredAt' | 'lastLoginAt' | 'lastLessonAt' | 'nextAppointmentAt' | 'remainingPeriods'; direction: 'asc' | 'desc'; } export interface OperationsDashboardMetric { key: string; label: string; value: number; hint: string; route: string; tone: 'blue' | 'green' | 'amber' | 'rose'; } export type ScheduleTrendPeriod = 'week' | 'month' | 'year'; export interface ScheduleTrendPoint { key: string; label: string; shortLabel: string; courseCount: number; studentCount: number; completedCount: number; isToday: boolean; } export interface ScheduleTrendData { period: ScheduleTrendPeriod; periodLabel: string; dateFrom: string; dateTo: string; points: ScheduleTrendPoint[]; todayCourseCount: number; todayStudentCount: number; totalCourseCount: number; totalStudentCount: number; maxValue: number; refreshedAt: string; liveSourceAvailable: boolean; liveSourceMessage: string; } export interface OperationsDashboardData { identity: AdminIdentity; date: string; metrics: OperationsDashboardMetric[]; alerts: { key: string; label: string; value: number; route: string }[]; refreshedAt?: string; liveSourceAvailable?: boolean; liveSourceMessage?: string; scheduleTrend?: ScheduleTrendData; sync?: OperationsSyncIndicator; payroll?: { month: string; coachCount: number; lessonCount: number; payableAmount: number; sourceStatus: PayrollSourceStatus; refreshedAt: string; }; } export interface OperationsUser extends Record { objectId: string; userId: number; displayName: string; username: string; mobile: string; mobileMasked: string; groupId: number; groupName: string; parentUserId: number; parentDisplayName: string; periods30: number; periods60: number; trialPeriods: number; activeCourseCount: number; completedCourseCount: number; studyRecordCount: number; appointmentCount: number; learningStatus: 'trial' | 'studying' | 'completed' | 'not_started'; learningStatusLabel: string; learningStatusReason: string; registeredAt: string; lastLoginAt: string; loginCount: number; status: 'active' | 'disabled'; statusLabel: string; canRevealMobile: boolean; updatedAt: string; nickname?: string; lastLessonAt?: string; nextAppointmentAt?: string; riskFlags?: string[]; identityType?: 'member'; gender?: string; grade?: string; noteCount?: number; liveOnly?: boolean; writeReady?: boolean; dataSource?: 'parse' | 'legacy-live'; readOnlyReason?: string; } export interface OperationsUserDetail extends OperationsUser { email: string; balances: { purse: number; silverCoin: number; userExp: number; userPoint: number; dummyPurse: number; credit: number; }; relation: { parentUserId: number; parentDisplayName: string }; permissions: { canManage: boolean; canEdit: boolean; canAdjustBalance: boolean; canResetPassword: boolean; canRevealMobile: boolean }; agents: MemberAgentOption[]; } export type LearningReportStatus = 'available' | 'incomplete' | 'missing' | 'historical'; export interface LearningReportSummary extends Record { reportCount: number; studentCount: number; learnedCount: number; masteredCount: number; reviewDueCount: number; masteryRate: number; } export interface LearningReportSourceStatus extends Record { state: 'synced' | 'delayed' | 'not_configured'; label: string; message: string; lastSuccessAt: string; lagSeconds: number | null; } export interface LearningReportListItem extends Record { targetId: string; generalId: number; studentObjectId: string; studentId: number; studentName: string; studentMobileMasked: string; coachId: number; coachName: string; courseId: number; courseName: string; appointmentId: number; appointmentAt: string; studyAt: string; studyDate: string; classTypeLabel: string; learnedCount: number; masteredCount: number; reviewDueCount: number; unmarkedCount: number; masteryRate: number; status: LearningReportStatus; statusLabel: string; canExport: boolean; updatedAt: string; } export interface LearningReportWord extends Record { index: number; generalId: number; word: string; phonetic: string; meaning: string; example: string; result: 'mastered' | 'review' | 'unmarked'; resultLabel: string; } export interface LearningReportDetail extends LearningReportListItem { words: LearningReportWord[]; reviewSchedule: Array<{ day: number; date: string }>; score: number; studentComment: string; coachComment: string; generatedAt: string; sourceStatus: LearningReportSourceStatus; countMismatch: boolean; } export interface LearningReportPage extends OperationsPage { summary: LearningReportSummary; sourceStatus: LearningReportSourceStatus; } export interface CoachSummary extends Record { objectId: string; userId: number; username: string; displayName: string; mobile: string; mobileMasked: string; parentUserId: number; storeName: string; status: 'active' | 'disabled'; statusLabel: string; todayAppointments: number; pendingAppointments: number; totalAppointments: number; lastAppointmentAt: string; nextAppointmentAt?: string; monthLessons: number; monthHours: number; estimatedPay: number; averageScore: number; lastLessonAt: string; availabilityConfigured: boolean; updatedAt: string; source?: 'hybrid' | 'legacy-live'; sourceLabel?: string; readOnly?: boolean; registeredAt?: string; hasCurrentBusiness?: boolean; } export interface CoachPageSummary extends Record { total: number; active: number; disabled: number; monthActive: number; liveOnly: number; } export interface StoreSummary extends Record { objectId: string; userId: number; username: string; storeName: string; contactName: string; mobile: string; mobileMasked: string; parentStoreId: number; parentStoreName: string; memberCount: number; activeStudyingMemberCount?: number; coachCount: number; todayAppointments: number; totalAppointments: number; lastAppointmentAt: string; registeredAt: string; lastLoginAt: string; loginCount: number; status: 'active' | 'disabled'; statusLabel: string; updatedAt: string; source?: 'parse' | 'hybrid' | 'legacy-live'; sourceLabel?: string; readOnly?: boolean; } export interface StoreOption extends Record { objectId: string; userId: number; displayName: string; } export interface StorePage extends OperationsPage { canManage: boolean; activeStudyingMemberCountError?: string; summary: { total: number; active: number; disabled: number; members: number; coaches: number; todayAppointments: number; liveOnly?: number; }; parents: StoreOption[]; liveSourceAvailable?: boolean; liveSourceMessage?: string; legacyStoreCount?: number; sync?: OperationsSyncIndicator; } export interface StoreCourseSummary extends Record { courseId: number; courseName: string; appointmentCount: number; completedCount: number; studentCount: number; completionRate: number; lastAppointmentAt: string; } export interface StoreDetailData extends Record { store: StoreSummary; month: string; summary: { memberCount: number; coachCount: number; monthAppointments: number; completedAppointments: number; activeStudents: number; lessonCount: number; totalHours: number; estimatedCoachPay: number; }; members: OperationsUser[]; coaches: CoachSummary[]; appointments: ScheduleAppointment[]; courses: StoreCourseSummary[]; commissionStatus: { available: boolean; message: string; }; payrollSourceStatus: PayrollSourceStatus; refreshedAt: string; sync?: OperationsSyncIndicator; } export interface CoachStoreOption extends Record { objectId: string; userId: number; displayName: string; } export interface CoachPage extends OperationsPage { summary: CoachPageSummary; canManage: boolean; stores: CoachStoreOption[]; liveSourceAvailable?: boolean; liveSourceMessage?: string; liveSourceCount?: number; dataDateFrom?: string; dataDateTo?: string; payrollMonth?: string; payrollSourceStatus?: PayrollSourceStatus; payrollRefreshedAt?: string; sync?: OperationsSyncIndicator; } export interface ScheduleAppointment extends Record { bindingId?: string; sourceKey?: string; statusUnverified?: boolean; targetId: string; generalId: number; studentObjectId: string; studentId: number; studentName: string; coachObjectId: string; coachId: number; coachName: string; courseId: number; courseName: string; classType: number; classTypeLabel: string; deliveryMethod: string; courseCategoryKey?: string; courseCategoryName?: string; durationMinutes?: number; deliveryMode?: TeachingDeliveryMode; teachingRuleId?: string; teacherPaySnapshot?: number; creditCostsSnapshot?: TeachingCreditCost[]; creditHoldStatus?: string; creditHoldReason?: string; startsAt: string; endsAt: string; statusCode: number; statusLabel: string; cancelled: boolean; cancellationReason: string; coachAccountMissing: boolean; availabilityWarning: boolean; source: 'parse' | 'legacy-live'; sourceLabel: string; readOnly: boolean; updatedAt: string; } export interface ScheduleCalendarData { source?: string; workflowIssues?: Array<{appointmentGeneralId:string;message:string;targetId:string;updatedAt:string}>; dateFrom: string; dateTo: string; refreshedAt: string; sourceRefreshedAt?: string; totalAppointments: number; dataDateFrom: string; dataDateTo: string; liveSourceAvailable: boolean; liveSourceMessage: string; liveSourceCount: number; coaches: CoachSummary[]; appointments: ScheduleAppointment[]; unassigned: ScheduleAppointment[]; members: Array<{ objectId: string; userId: number; displayName: string; mobileMasked: string; periods30: number; periods60: number; trialPeriods: number; userPoint: number; learningStatusLabel: string; liveOnly?: boolean; writeReady?: boolean; readOnly?: boolean; readOnlyReason?: string }>; courses: Array<{ bindingId?: string; courseId: number; courseName: string; memberIds: number[]; availableToAll?: boolean; stageKey?: TeachingStageKey | ''; stageName?: string; categoryKey?: string; categoryName?: string; mappingConfirmed?: boolean; suggestedCategoryName?: string; supportedDurations?: number[] }>; sync?: OperationsSyncIndicator; } export interface CoachAvailability extends Record { objectId: string; coachId: number; ruleType: 'weekly' | 'date'; weekday: number; date: string; startTime: string; endTime: string; available: boolean; reason: string; updatedAt: string; } export interface CoachDetailData { coach: CoachSummary; profile: { username: string; mobile: string; storeName: string; statusLabel: string; sourceLabel: string; }; currentPayroll: PayrollCoachSummary; availability: CoachAvailability[]; appointments: ScheduleAppointment[]; lessons: PayrollLine[]; reviews: PayrollLine[]; payrolls: PayrollSummary[]; reviewTaskCount: number; payrollMonth: string; payrollSourceStatus: PayrollSourceStatus; refreshedAt: string; dataDateFrom: string; dataDateTo: string; } export interface PayRate extends Record { objectId: string; classType: number; classTypeLabel: string; amount: number; durationHours: number; effectiveFrom: string; active: boolean; isDefault?: boolean; updatedAt: string; } export type TeachingDeliveryMode = 'online' | 'offline'; export type TeachingCreditAccount = 'Purse' | 'SilverCoin' | 'UserExp' | 'UserPoint'; export type TeachingStageKey = 'primary' | 'middle' | 'high' | 'other'; export interface TeachingCourseCategory extends Record { objectId: string; key: string; name: string; sortOrder: number; active: boolean; system: boolean; updatedAt: string; } export interface TeachingCreditCost extends Record { account: TeachingCreditAccount; label: string; amount: number; } export interface TeachingRule extends Record { objectId: string; categoryKey: string; categoryName: string; durationMinutes: number; deliveryMode: TeachingDeliveryMode; deliveryLabel: string; teacherPay: number; creditCosts: TeachingCreditCost[]; effectiveFrom: string; active: boolean; isDefault?: boolean; updatedAt: string; } export interface CourseTeachingMapping extends Record { objectId: string; courseId: number; courseName: string; stageKey: TeachingStageKey | ''; stageName: string; categoryKey: string; categoryName: string; suggestedCategoryKey: string; suggestedCategoryName: string; categoryConfirmed: boolean; stageConfirmed: boolean; confirmed: boolean; updatedAt: string; } export interface TeachingStandardsData { categories: TeachingCourseCategory[]; rules: TeachingRule[]; mappings: CourseTeachingMapping[]; summary: { courseCount: number; confirmedCount: number; unmappedCount: number; activeRuleCount: number; }; refreshedAt: string; } export interface CreditReservationReconciliation { items: Array<{ appointmentId: string; appointmentGeneralId: number; studentId: number; studentName: string; courseId: number; courseName: string; startsAt: string; categoryName: string; durationMinutes: number; deliveryLabel: string; creditCosts: TeachingCreditCost[]; status: 'ready' | 'insufficient' | 'unmapped' | 'missing_rule' | 'missing_student'; statusLabel: string; reason: string; }>; summary: { total: number; ready: number; insufficient: number; exceptions: number; }; refreshedAt: string; } export type PayrollStatus = 'draft' | 'reviewed' | 'paid'; export interface PayrollSummary extends Record { version?: number; objectId: string; month: string; status: PayrollStatus; statusLabel: string; coachCount: number; lessonCount: number; totalHours: number; baseAmount: number; reviewCount?: number; reviewAmount?: number; adjustmentAmount: number; payableAmount: number; exceptionCount: number; type4Count?: number; reviewedAt: string; paidAt: string; paymentMethod: string; paymentReference: string; updatedAt: string; } export interface PayrollLine extends Record { objectId: string; kind: 'lesson' | 'adjustment' | 'review_reward'; reviewRewardId?: string; reviewTaskId?: string; plannedDate?: string; coachId: number; coachName: string; coachObjectId?: string; storeId: number; storeName: string; studentName: string; courseName: string; legacyGeneralId: number; lessonId: string; lessonAt: string; originalMonth: string; settlementMonth: string; classType: number; classTypeLabel: string; courseCategoryKey?: string; courseCategoryName?: string; deliveryMode?: TeachingDeliveryMode | ''; deliveryLabel?: string; durationMinutes?: number; teachingRuleId?: string; durationHours: number; rateAmount: number; amount: number; score?: number; comment?: string; description: string; exception: string; excluded?: boolean; exclusionReason?: string; } export interface PayrollDetail extends PayrollSummary { lines: PayrollLine[]; exceptions: PayrollLine[]; coachItems?: PayrollCoachSummary[]; sourceStatus?: PayrollSourceStatus; refreshedAt?: string; } export interface PayrollCoachSummary extends Record { coachId: number; coachName: string; coachObjectId?: string; storeId: number; storeName: string; type30Count: number; type60Count: number; trialCount: number; type4Count: number; lessonCount: number; totalHours: number; baseAmount: number; reviewCount?: number; reviewAmount?: number; adjustmentAmount: number; payableAmount: number; averageScore?: number; latestLessonAt: string; } export interface PayrollSourceStatus extends Record { available: boolean; mode: 'live' | 'cache' | 'parse'; message: string; total: number; refreshedAt: string; sync?: { state: 'not_configured' | 'synced' | 'delayed' | 'inconsistent' | 'error' | 'batch_verified'; settlementReady: boolean; message: string; checkedAt: string; lastSuccessAt: string; lagSeconds: number | null; datasets: Array<{ key: string; state: string; lastSuccessAt: string; lagSeconds: number | null; pendingFailures: number; conflicts: number; legacyCount: number | null; localCount: number | null; complete?: boolean }>; }; } export interface PayrollOverview extends PayrollDetail { incomeAmount?: number; settlementAmount?: number | null; settlementAdjustmentAmount?: number; settlementCarryAmount?: number; coachItems: PayrollCoachSummary[]; sourceStatus: PayrollSourceStatus; batches: PayrollSummary[]; stores: Array<{ storeId: number; storeName: string }>; } export interface PayrollExportData extends Record { excluded?: PayrollLine[]; fileName: string; summary: PayrollSummary; coachItems: PayrollCoachSummary[]; lines: PayrollLine[]; exceptions: PayrollLine[]; } export interface VocabularyNode extends Record { objectId: string; nodeId: number; name: string; parentId: number; depth: number; orderId: number; directWordCount: number; wordCount: number; childCount: number; status: number; canContainWords: boolean; } export interface VocabularyWord extends Record { targetId: string; generalId: number; itemId: number; word: string; meaning: string; phonetic: string; example: string; audioUrl: string; nodeId: number; unitName: string; bookName: string; stageName: string; orderId: number; status: number; statusLabel: string; createdAt: string; updatedAt: string; } export interface VocabularyTree { items: VocabularyNode[]; total: number; totalWords: number; refreshedAt: string; } export interface VocabularyImportPreview { batchId: string; status: string; summary: { total: number; create: number; update: number; unchanged: number; duplicate: number; error: number; missingMedia: number }; items: Array>; createdAt: string; } export interface SpecialTrainingItem extends Record { targetId: string; generalId: number; itemId: number; title: string; synopsis: string; coverUrl: string; tags: string[]; contentHtml: string; videoUrl: string; author: string; source: string; orderId: number; hits: number; status: number; statusLabel: string; createdAt: string; updatedAt: string; } export interface SpecialTrainingPage extends OperationsPage { sourceStatus?: { available: boolean; message: string; legacyTotal: number; created: number; updated: number; refreshedAt: string; } | null; } export interface MediaUploadTicket { ticketId: string; method: 'PUT'; uploadUrl: string; publicUrl: string; objectKey: string; headers: Record; expiresAt: string; } export interface ReviewReward { objectId: string; taskId: string; coachId: number; coachName: string; studentName: string; courseName: string; storeId: number; storeName: string; plannedDate: string; completedAt: string; incomeMonth: string; amount: number; status: 'earned' | 'historical' | 'exception'; statusLabel: string; exception: string; batchId?: string; settlementMonth?: string; canConfirm?: boolean; } export interface ReviewRewardPage { snapshot?: string; nextCursor?: string; items: ReviewReward[]; total: number; page: number; pageSize: number; summary: { reviewCount: number; reviewAmount: number; historicalCount: number; exceptionCount: number }; } export interface ReviewRewardFilters { month: string; storeId: number; search: string; coachId: number; revision?: number; resetRevision?: number; snapshot?: string; afterId?: string; exportMode?: boolean; }