feat: refine admin forms, attendance and finance workflows

Squash merge PR #23.

Included changes:
- complete occupancy check-in required fields/default payload
- improve responsive admin management pages
- fix attendance edge cases and attendance period config
- refine wallet/finance-related workflow handling

Checks:
- npm run typecheck -w apps/admin
- npm run typecheck -w apps/server
This commit is contained in:
2026-07-18 12:54:10 +00:00
parent 92d303ed01
commit 375c7ec60b
64 changed files with 5169 additions and 2404 deletions

View File

@@ -3,16 +3,53 @@ import {
IsOptional,
IsString,
IsInt,
IsBoolean,
IsDateString,
IsIn,
ValidateNested,
IsNotEmpty,
ArrayNotEmpty,
Matches,
Max,
Min,
} from 'class-validator';
import { Type } from 'class-transformer';
export class AttendancePeriodConfigItemDto {
@IsString()
@IsNotEmpty()
periodKey: string;
@IsString()
@IsNotEmpty()
label: string;
@IsString()
@Matches(/^([01]\d|2[0-3]):[0-5]\d$/)
startTime: string;
@IsString()
@Matches(/^([01]\d|2[0-3]):[0-5]\d$/)
endTime: string;
@IsOptional()
@IsInt()
sortOrder?: number;
@IsOptional()
@IsBoolean()
enabled?: boolean;
}
export class SaveAttendancePeriodConfigsDto {
@IsArray()
@ArrayNotEmpty()
@ValidateNested({ each: true })
@Type(() => AttendancePeriodConfigItemDto)
periods: AttendancePeriodConfigItemDto[];
}
export class AttendanceRecordItem {
@IsInt()
@IsNotEmpty()
@@ -27,7 +64,6 @@ export class AttendanceRecordItem {
attendanceDate: string;
@IsString()
@IsIn(['morning_reading', 'morning', 'afternoon', 'evening_study', 'night_check'])
@IsNotEmpty()
session: string;
@@ -59,6 +95,11 @@ export class AttendanceSummaryQueryDto {
@Type(() => Number)
classId?: number;
@IsOptional()
@IsInt()
@Type(() => Number)
scheduleId?: number;
@IsOptional()
@IsDateString()
dateFrom?: string;
@@ -66,6 +107,26 @@ export class AttendanceSummaryQueryDto {
@IsOptional()
@IsDateString()
dateTo?: string;
@IsOptional()
@IsString()
session?: string;
}
export class RefreshDingTalkAttendanceDto {
@IsDateString()
@IsNotEmpty()
date: string;
@IsOptional()
@IsInt()
@Type(() => Number)
classId?: number;
@IsOptional()
@IsString()
session?: string;
}
export class AttendanceCalendarQueryDto {
@@ -112,6 +173,17 @@ export class QueryDingRawDto {
pageSize?: number;
}
export class AttendanceScheduleOptionsQueryDto {
@IsInt()
@Type(() => Number)
@IsNotEmpty()
classId: number;
@IsDateString()
@IsNotEmpty()
date: string;
}
export class QueryAttendanceRecordsDto {
@IsOptional()
@IsInt()
@@ -137,7 +209,7 @@ export class QueryAttendanceRecordsDto {
@IsOptional()
@IsString()
@IsIn(['present', 'late', 'absent', 'leave'])
@IsIn(['present', 'late', 'absent', 'leave', 'pending'])
status?: string;
@IsOptional()