@studious-lms/server 1.22.0 → 1.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +0 -1
- package/data/curriculum/nepal-cdc-g4-g5/README.md +90 -0
- package/data/curriculum/nepal-cdc-g4-g5/manifest.json +97 -0
- package/data/curriculum/nepal-cdc-g4-g5/proficiency-scale.json +76 -0
- package/data/curriculum/nepal-cdc-g4-g5/subjects/english.json +6936 -0
- package/data/curriculum/nepal-cdc-g4-g5/subjects/health-physical-creative-arts.json +3401 -0
- package/data/curriculum/nepal-cdc-g4-g5/subjects/mathematics.json +1511 -0
- package/data/curriculum/nepal-cdc-g4-g5/subjects/nepali.json +7928 -0
- package/data/curriculum/nepal-cdc-g4-g5/subjects/science-technology.json +2420 -0
- package/data/curriculum/nepal-cdc-g4-g5/subjects/social-studies.json +2177 -0
- package/dist/lib/assignmentTypeDefaults.d.ts +41 -0
- package/dist/lib/assignmentTypeDefaults.d.ts.map +1 -0
- package/dist/lib/assignmentTypeDefaults.js +167 -0
- package/dist/lib/assignmentTypeDefaults.js.map +1 -0
- package/dist/lib/config/env.d.ts +0 -8
- package/dist/lib/config/env.d.ts.map +1 -1
- package/dist/lib/config/env.js +2 -5
- package/dist/lib/config/env.js.map +1 -1
- package/dist/lib/feePdf.d.ts +19 -0
- package/dist/lib/feePdf.d.ts.map +1 -0
- package/dist/lib/feePdf.js +167 -0
- package/dist/lib/feePdf.js.map +1 -0
- package/dist/lib/feePdfFormat.d.ts +4 -0
- package/dist/lib/feePdfFormat.d.ts.map +1 -0
- package/dist/lib/feePdfFormat.js +16 -0
- package/dist/lib/feePdfFormat.js.map +1 -0
- package/dist/lib/idCardPdf.d.ts +26 -0
- package/dist/lib/idCardPdf.d.ts.map +1 -0
- package/dist/lib/idCardPdf.js +272 -0
- package/dist/lib/idCardPdf.js.map +1 -0
- package/dist/lib/pdfLetterhead.d.ts +41 -0
- package/dist/lib/pdfLetterhead.d.ts.map +1 -0
- package/dist/lib/pdfLetterhead.js +86 -0
- package/dist/lib/pdfLetterhead.js.map +1 -0
- package/dist/lib/transcriptPdf.d.ts +55 -0
- package/dist/lib/transcriptPdf.d.ts.map +1 -0
- package/dist/lib/transcriptPdf.js +303 -0
- package/dist/lib/transcriptPdf.js.map +1 -0
- package/dist/models/academicCalendar.d.ts +198 -0
- package/dist/models/academicCalendar.d.ts.map +1 -0
- package/dist/models/academicCalendar.js +106 -0
- package/dist/models/academicCalendar.js.map +1 -0
- package/dist/models/agenda.d.ts +8 -0
- package/dist/models/agenda.d.ts.map +1 -1
- package/dist/models/announcement.d.ts +2 -0
- package/dist/models/announcement.d.ts.map +1 -1
- package/dist/models/assignment.d.ts +66 -0
- package/dist/models/assignment.d.ts.map +1 -1
- package/dist/models/attendance.d.ts +659 -21
- package/dist/models/attendance.d.ts.map +1 -1
- package/dist/models/attendance.js +544 -75
- package/dist/models/attendance.js.map +1 -1
- package/dist/models/auth.d.ts +2 -0
- package/dist/models/auth.d.ts.map +1 -1
- package/dist/models/class.d.ts +12 -0
- package/dist/models/class.d.ts.map +1 -1
- package/dist/models/class.js +3 -2
- package/dist/models/class.js.map +1 -1
- package/dist/models/event.d.ts +16 -0
- package/dist/models/event.d.ts.map +1 -1
- package/dist/models/fees.d.ts +554 -0
- package/dist/models/fees.d.ts.map +1 -0
- package/dist/models/fees.js +278 -0
- package/dist/models/fees.js.map +1 -0
- package/dist/models/file.d.ts +29 -0
- package/dist/models/file.d.ts.map +1 -1
- package/dist/models/folder.d.ts +2 -0
- package/dist/models/folder.d.ts.map +1 -1
- package/dist/models/labChat.d.ts +2 -0
- package/dist/models/labChat.d.ts.map +1 -1
- package/dist/models/marketplace.d.ts +178 -0
- package/dist/models/marketplace.d.ts.map +1 -0
- package/dist/models/marketplace.js +115 -0
- package/dist/models/marketplace.js.map +1 -0
- package/dist/models/moduleItem.d.ts +304 -0
- package/dist/models/moduleItem.d.ts.map +1 -0
- package/dist/models/moduleItem.js +78 -0
- package/dist/models/moduleItem.js.map +1 -0
- package/dist/models/newtonChat.d.ts +1 -0
- package/dist/models/newtonChat.d.ts.map +1 -1
- package/dist/models/school.d.ts +193 -0
- package/dist/models/school.d.ts.map +1 -1
- package/dist/models/school.js +80 -2
- package/dist/models/school.js.map +1 -1
- package/dist/models/section.d.ts +4 -0
- package/dist/models/section.d.ts.map +1 -1
- package/dist/models/worksheet.d.ts +19 -0
- package/dist/models/worksheet.d.ts.map +1 -1
- package/dist/models/worksheet.js +2 -2
- package/dist/models/worksheet.js.map +1 -1
- package/dist/routers/_app.d.ts +11605 -7133
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +12 -2
- package/dist/routers/_app.js.map +1 -1
- package/dist/routers/academicCalendar.d.ts +229 -0
- package/dist/routers/academicCalendar.d.ts.map +1 -0
- package/dist/routers/academicCalendar.js +75 -0
- package/dist/routers/academicCalendar.js.map +1 -0
- package/dist/routers/admin.d.ts +2 -0
- package/dist/routers/admin.d.ts.map +1 -1
- package/dist/routers/agenda.d.ts +8 -0
- package/dist/routers/agenda.d.ts.map +1 -1
- package/dist/routers/assignment.d.ts +75 -0
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +10 -2
- package/dist/routers/assignment.js.map +1 -1
- package/dist/routers/attendance.d.ts +103 -0
- package/dist/routers/attendance.d.ts.map +1 -1
- package/dist/routers/attendance.js +8 -5
- package/dist/routers/attendance.js.map +1 -1
- package/dist/routers/class.d.ts +27 -0
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +6 -2
- package/dist/routers/class.js.map +1 -1
- package/dist/routers/event.d.ts +8 -0
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/fees.d.ts +513 -0
- package/dist/routers/fees.d.ts.map +1 -0
- package/dist/routers/fees.js +88 -0
- package/dist/routers/fees.js.map +1 -0
- package/dist/routers/file.d.ts +2 -0
- package/dist/routers/file.d.ts.map +1 -1
- package/dist/routers/gradebook.d.ts +4 -1
- package/dist/routers/gradebook.d.ts.map +1 -1
- package/dist/routers/marketplace.d.ts +371 -0
- package/dist/routers/marketplace.d.ts.map +1 -0
- package/dist/routers/marketplace.js +87 -0
- package/dist/routers/marketplace.js.map +1 -0
- package/dist/routers/moduleItem.d.ts +263 -0
- package/dist/routers/moduleItem.d.ts.map +1 -0
- package/dist/routers/moduleItem.js +60 -0
- package/dist/routers/moduleItem.js.map +1 -0
- package/dist/routers/parent.d.ts +169 -0
- package/dist/routers/parent.d.ts.map +1 -0
- package/dist/routers/parent.js +49 -0
- package/dist/routers/parent.js.map +1 -0
- package/dist/routers/school.d.ts +476 -6
- package/dist/routers/school.d.ts.map +1 -1
- package/dist/routers/school.js +130 -4
- package/dist/routers/school.js.map +1 -1
- package/dist/routers/worksheet.d.ts +27 -0
- package/dist/routers/worksheet.d.ts.map +1 -1
- package/dist/routers/worksheet.js +10 -3
- package/dist/routers/worksheet.js.map +1 -1
- package/dist/services/academicCalendar.d.ts +173 -0
- package/dist/services/academicCalendar.d.ts.map +1 -0
- package/dist/services/academicCalendar.js +214 -0
- package/dist/services/academicCalendar.js.map +1 -0
- package/dist/services/adminInstitution.d.ts +2 -0
- package/dist/services/adminInstitution.d.ts.map +1 -1
- package/dist/services/agenda.d.ts +8 -0
- package/dist/services/agenda.d.ts.map +1 -1
- package/dist/services/assignment.d.ts +75 -0
- package/dist/services/assignment.d.ts.map +1 -1
- package/dist/services/assignment.js +91 -12
- package/dist/services/assignment.js.map +1 -1
- package/dist/services/attendance.d.ts +323 -10
- package/dist/services/attendance.d.ts.map +1 -1
- package/dist/services/attendance.js +385 -25
- package/dist/services/attendance.js.map +1 -1
- package/dist/services/attendanceMerge.d.ts +53 -0
- package/dist/services/attendanceMerge.d.ts.map +1 -0
- package/dist/services/attendanceMerge.js +87 -0
- package/dist/services/attendanceMerge.js.map +1 -0
- package/dist/services/attendanceRate.d.ts +24 -0
- package/dist/services/attendanceRate.d.ts.map +1 -0
- package/dist/services/attendanceRate.js +31 -0
- package/dist/services/attendanceRate.js.map +1 -0
- package/dist/services/attendanceStats.d.ts +22 -0
- package/dist/services/attendanceStats.d.ts.map +1 -0
- package/dist/services/attendanceStats.js +45 -0
- package/dist/services/attendanceStats.js.map +1 -0
- package/dist/services/class.d.ts +27 -0
- package/dist/services/class.d.ts.map +1 -1
- package/dist/services/class.js +26 -3
- package/dist/services/class.js.map +1 -1
- package/dist/services/event.d.ts +8 -0
- package/dist/services/event.d.ts.map +1 -1
- package/dist/services/fees.d.ts +422 -0
- package/dist/services/fees.d.ts.map +1 -0
- package/dist/services/fees.js +310 -0
- package/dist/services/fees.js.map +1 -0
- package/dist/services/feesAuthz.d.ts +49 -0
- package/dist/services/feesAuthz.d.ts.map +1 -0
- package/dist/services/feesAuthz.js +52 -0
- package/dist/services/feesAuthz.js.map +1 -0
- package/dist/services/file.d.ts +4 -0
- package/dist/services/file.d.ts.map +1 -1
- package/dist/services/gradebook.d.ts +18 -1
- package/dist/services/gradebook.d.ts.map +1 -1
- package/dist/services/gradebook.js +57 -2
- package/dist/services/gradebook.js.map +1 -1
- package/dist/services/idCard.d.ts +11 -0
- package/dist/services/idCard.d.ts.map +1 -0
- package/dist/services/idCard.js +122 -0
- package/dist/services/idCard.js.map +1 -0
- package/dist/services/marketplace.d.ts +301 -0
- package/dist/services/marketplace.d.ts.map +1 -0
- package/dist/services/marketplace.js +628 -0
- package/dist/services/marketplace.js.map +1 -0
- package/dist/services/moduleItem.d.ts +196 -0
- package/dist/services/moduleItem.d.ts.map +1 -0
- package/dist/services/moduleItem.js +188 -0
- package/dist/services/moduleItem.js.map +1 -0
- package/dist/services/notification.d.ts +58 -0
- package/dist/services/notification.d.ts.map +1 -1
- package/dist/services/notification.js +30 -2
- package/dist/services/notification.js.map +1 -1
- package/dist/services/parentPortal.d.ts +133 -0
- package/dist/services/parentPortal.d.ts.map +1 -0
- package/dist/services/parentPortal.js +328 -0
- package/dist/services/parentPortal.js.map +1 -0
- package/dist/services/school.d.ts +122 -3
- package/dist/services/school.d.ts.map +1 -1
- package/dist/services/school.js +179 -47
- package/dist/services/school.js.map +1 -1
- package/dist/services/studentImport.d.ts +34 -0
- package/dist/services/studentImport.d.ts.map +1 -0
- package/dist/services/studentImport.js +236 -0
- package/dist/services/studentImport.js.map +1 -0
- package/dist/services/transcript.d.ts +61 -0
- package/dist/services/transcript.d.ts.map +1 -0
- package/dist/services/transcript.js +243 -0
- package/dist/services/transcript.js.map +1 -0
- package/dist/services/worksheet.d.ts +22 -0
- package/dist/services/worksheet.d.ts.map +1 -1
- package/dist/services/worksheet.js +94 -2
- package/dist/services/worksheet.js.map +1 -1
- package/dist/trpc.d.ts +1 -1
- package/dist/trpc.d.ts.map +1 -1
- package/dist/trpc.js +5 -5
- package/dist/trpc.js.map +1 -1
- package/dist/utils/adminPanelAccess.d.ts +4 -5
- package/dist/utils/adminPanelAccess.d.ts.map +1 -1
- package/dist/utils/adminPanelAccess.js +12 -34
- package/dist/utils/adminPanelAccess.js.map +1 -1
- package/package.json +2 -1
- package/prisma/migrations/20260804120000_add_attendance_records_and_excusals/migration.sql +114 -0
- package/prisma/migrations/20260804120100_add_fee_plans_invoices_payments/migration.sql +101 -0
- package/prisma/migrations/20260804120200_add_marketplace_listings/migration.sql +54 -0
- package/prisma/migrations/20260804120300_content_platform_b3_b9/migration.sql +63 -0
- package/prisma/migrations/20260804120400_c_academic_ops/migration.sql +77 -0
- package/prisma/schema.prisma +371 -11
- package/scripts/import-nepal-cdc-curriculum.ts +379 -0
- package/src/lib/assignmentTypeDefaults.ts +222 -0
- package/src/lib/config/env.ts +0 -3
- package/src/lib/feePdf.ts +199 -0
- package/src/lib/feePdfFormat.ts +14 -0
- package/src/lib/idCardPdf.ts +336 -0
- package/src/lib/pdfLetterhead.ts +124 -0
- package/src/lib/transcriptPdf.ts +502 -0
- package/src/models/academicCalendar.ts +139 -0
- package/src/models/attendance.ts +704 -73
- package/src/models/class.ts +1 -0
- package/src/models/fees.ts +349 -0
- package/src/models/marketplace.ts +158 -0
- package/src/models/moduleItem.ts +107 -0
- package/src/models/school.ts +95 -0
- package/src/models/worksheet.ts +2 -0
- package/src/routers/_app.ts +10 -0
- package/src/routers/academicCalendar.ts +103 -0
- package/src/routers/assignment.ts +8 -0
- package/src/routers/attendance.ts +7 -3
- package/src/routers/class.ts +4 -0
- package/src/routers/fees.ts +126 -0
- package/src/routers/marketplace.ts +134 -0
- package/src/routers/moduleItem.ts +89 -0
- package/src/routers/parent.ts +68 -0
- package/src/routers/school.ts +184 -1
- package/src/routers/worksheet.ts +11 -0
- package/src/services/academicCalendar.ts +299 -0
- package/src/services/assignment.ts +122 -12
- package/src/services/attendance.ts +548 -26
- package/src/services/attendanceMerge.ts +139 -0
- package/src/services/attendanceRate.ts +41 -0
- package/src/services/attendanceStats.ts +63 -0
- package/src/services/class.ts +25 -1
- package/src/services/fees.ts +424 -0
- package/src/services/feesAuthz.ts +77 -0
- package/src/services/gradebook.ts +70 -0
- package/src/services/idCard.ts +147 -0
- package/src/services/marketplace.ts +785 -0
- package/src/services/moduleItem.ts +274 -0
- package/src/services/notification.ts +43 -0
- package/src/services/parentPortal.ts +380 -0
- package/src/services/school.ts +243 -45
- package/src/services/studentImport.ts +273 -0
- package/src/services/transcript.ts +313 -0
- package/src/services/worksheet.ts +111 -0
- package/src/trpc.ts +3 -3
- package/src/utils/adminPanelAccess.ts +12 -35
- package/tests/unit/assignmentTypeDefaults.test.ts +52 -0
- package/tests/unit/lib/feePdf.test.ts +15 -0
- package/tests/unit/lib/idCardPdf.test.ts +40 -0
- package/tests/unit/lib/transcriptPdf.test.ts +111 -0
- package/tests/unit/services/attendanceMerge.test.ts +90 -0
- package/tests/unit/services/attendanceRate.test.ts +38 -0
- package/tests/unit/services/attendanceStats.test.ts +54 -0
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Attendance model – attendance records, present/late/absent students per event.
|
|
3
|
-
*/
|
|
4
1
|
|
|
5
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="04968f1d-49ec-52b6-ab2b-a86286e300ce")}catch(e){}}();
|
|
6
3
|
import { prisma } from "../lib/prisma.js";
|
|
7
4
|
import { classTeacherOrSchoolAdminWhere } from "./access.js";
|
|
5
|
+
const studentProfileSelect = {
|
|
6
|
+
id: true,
|
|
7
|
+
username: true,
|
|
8
|
+
profile: {
|
|
9
|
+
select: {
|
|
10
|
+
displayName: true,
|
|
11
|
+
profilePicture: true,
|
|
12
|
+
profilePictureThumbnail: true,
|
|
13
|
+
bio: true,
|
|
14
|
+
location: true,
|
|
15
|
+
website: true,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
};
|
|
8
19
|
const attendanceInclude = {
|
|
9
20
|
event: {
|
|
10
21
|
select: {
|
|
@@ -16,50 +27,27 @@ const attendanceInclude = {
|
|
|
16
27
|
color: true,
|
|
17
28
|
},
|
|
18
29
|
},
|
|
19
|
-
present: {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
profile: {
|
|
24
|
-
select: {
|
|
25
|
-
displayName: true,
|
|
26
|
-
profilePicture: true,
|
|
27
|
-
profilePictureThumbnail: true,
|
|
28
|
-
bio: true,
|
|
29
|
-
location: true,
|
|
30
|
-
website: true,
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
late: {
|
|
30
|
+
present: { select: studentProfileSelect },
|
|
31
|
+
late: { select: studentProfileSelect },
|
|
32
|
+
absent: { select: studentProfileSelect },
|
|
33
|
+
records: {
|
|
36
34
|
select: {
|
|
37
35
|
id: true,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
website: true,
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
absent: {
|
|
52
|
-
select: {
|
|
53
|
-
id: true,
|
|
54
|
-
username: true,
|
|
55
|
-
profile: {
|
|
36
|
+
studentId: true,
|
|
37
|
+
status: true,
|
|
38
|
+
note: true,
|
|
39
|
+
excusedByExcusalId: true,
|
|
40
|
+
updatedById: true,
|
|
41
|
+
updatedAt: true,
|
|
42
|
+
student: { select: studentProfileSelect },
|
|
43
|
+
excusedByExcusal: {
|
|
56
44
|
select: {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
45
|
+
id: true,
|
|
46
|
+
reason: true,
|
|
47
|
+
note: true,
|
|
48
|
+
startDate: true,
|
|
49
|
+
endDate: true,
|
|
50
|
+
revokedAt: true,
|
|
63
51
|
},
|
|
64
52
|
},
|
|
65
53
|
},
|
|
@@ -78,14 +66,27 @@ const attendanceUpdateInclude = {
|
|
|
78
66
|
present: { select: { id: true, username: true } },
|
|
79
67
|
late: { select: { id: true, username: true } },
|
|
80
68
|
absent: { select: { id: true, username: true } },
|
|
81
|
-
|
|
82
|
-
/** @returns Class with students for attendance. */
|
|
83
|
-
export function findClassWithStudents(classId) {
|
|
84
|
-
return prisma.class.findUnique({
|
|
85
|
-
where: { id: classId },
|
|
69
|
+
records: {
|
|
86
70
|
select: {
|
|
87
71
|
id: true,
|
|
88
|
-
|
|
72
|
+
studentId: true,
|
|
73
|
+
status: true,
|
|
74
|
+
note: true,
|
|
75
|
+
excusedByExcusalId: true,
|
|
76
|
+
updatedById: true,
|
|
77
|
+
updatedAt: true,
|
|
78
|
+
student: { select: { id: true, username: true } },
|
|
79
|
+
excusedByExcusal: {
|
|
80
|
+
select: { id: true, reason: true, note: true },
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
const excusalInclude = {
|
|
86
|
+
students: {
|
|
87
|
+
select: {
|
|
88
|
+
studentId: true,
|
|
89
|
+
student: {
|
|
89
90
|
select: {
|
|
90
91
|
id: true,
|
|
91
92
|
username: true,
|
|
@@ -94,14 +95,38 @@ export function findClassWithStudents(classId) {
|
|
|
94
95
|
displayName: true,
|
|
95
96
|
profilePicture: true,
|
|
96
97
|
profilePictureThumbnail: true,
|
|
97
|
-
bio: true,
|
|
98
|
-
location: true,
|
|
99
|
-
website: true,
|
|
100
98
|
},
|
|
101
99
|
},
|
|
102
100
|
},
|
|
103
101
|
},
|
|
104
102
|
},
|
|
103
|
+
},
|
|
104
|
+
createdBy: {
|
|
105
|
+
select: {
|
|
106
|
+
id: true,
|
|
107
|
+
username: true,
|
|
108
|
+
profile: { select: { displayName: true } },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
revokedBy: {
|
|
112
|
+
select: {
|
|
113
|
+
id: true,
|
|
114
|
+
username: true,
|
|
115
|
+
profile: { select: { displayName: true } },
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
/** @returns Class with students for attendance. */
|
|
120
|
+
export function findClassWithStudents(classId) {
|
|
121
|
+
return prisma.class.findUnique({
|
|
122
|
+
where: { id: classId },
|
|
123
|
+
select: {
|
|
124
|
+
id: true,
|
|
125
|
+
schoolId: true,
|
|
126
|
+
students: {
|
|
127
|
+
select: studentProfileSelect,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
105
130
|
});
|
|
106
131
|
}
|
|
107
132
|
/** @returns Class if user is a teacher or school admin. */
|
|
@@ -114,6 +139,7 @@ export function findClassWithTeacher(classId, teacherId) {
|
|
|
114
139
|
select: {
|
|
115
140
|
id: true,
|
|
116
141
|
name: true,
|
|
142
|
+
schoolId: true,
|
|
117
143
|
students: { select: { id: true } },
|
|
118
144
|
},
|
|
119
145
|
});
|
|
@@ -128,6 +154,15 @@ export function findEventsByClassId(classId) {
|
|
|
128
154
|
export function findAttendanceByEventId(eventId) {
|
|
129
155
|
return prisma.attendance.findFirst({
|
|
130
156
|
where: { eventId },
|
|
157
|
+
include: {
|
|
158
|
+
records: {
|
|
159
|
+
select: {
|
|
160
|
+
studentId: true,
|
|
161
|
+
status: true,
|
|
162
|
+
excusedByExcusalId: true,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
131
166
|
});
|
|
132
167
|
}
|
|
133
168
|
/** @returns Attendance for class and event (eventId null = class-level). */
|
|
@@ -142,10 +177,19 @@ export function findAttendanceByClassAndEvent(classId, eventId) {
|
|
|
142
177
|
select: {
|
|
143
178
|
id: true,
|
|
144
179
|
name: true,
|
|
180
|
+
startTime: true,
|
|
145
181
|
},
|
|
146
182
|
},
|
|
147
183
|
late: { select: { id: true } },
|
|
148
184
|
absent: { select: { id: true } },
|
|
185
|
+
records: {
|
|
186
|
+
select: {
|
|
187
|
+
studentId: true,
|
|
188
|
+
status: true,
|
|
189
|
+
excusedByExcusalId: true,
|
|
190
|
+
note: true,
|
|
191
|
+
},
|
|
192
|
+
},
|
|
149
193
|
},
|
|
150
194
|
});
|
|
151
195
|
}
|
|
@@ -160,45 +204,470 @@ export function findManyAttendance(classId, eventId) {
|
|
|
160
204
|
orderBy: { date: "desc" },
|
|
161
205
|
});
|
|
162
206
|
}
|
|
163
|
-
/** Create an attendance
|
|
207
|
+
/** Create an attendance session with legacy lists + per-student records. */
|
|
164
208
|
export function createAttendance(data) {
|
|
209
|
+
const lateIds = data.lateIds ?? [];
|
|
210
|
+
const absentIds = data.absentIds ?? [];
|
|
211
|
+
const excusedIds = data.excusedIds ?? [];
|
|
212
|
+
const records = data.records ??
|
|
213
|
+
buildRecordsFromLists({
|
|
214
|
+
presentIds: data.presentIds,
|
|
215
|
+
lateIds,
|
|
216
|
+
absentIds,
|
|
217
|
+
excusedIds,
|
|
218
|
+
updatedById: data.updatedById,
|
|
219
|
+
});
|
|
165
220
|
return prisma.attendance.create({
|
|
166
221
|
data: {
|
|
167
222
|
classId: data.classId,
|
|
168
223
|
eventId: data.eventId ?? undefined,
|
|
169
|
-
date: new Date(),
|
|
224
|
+
date: data.date ?? new Date(),
|
|
170
225
|
present: { connect: data.presentIds.map((id) => ({ id })) },
|
|
171
|
-
late:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
226
|
+
late: lateIds.length ? { connect: lateIds.map((id) => ({ id })) } : undefined,
|
|
227
|
+
absent: absentIds.length ? { connect: absentIds.map((id) => ({ id })) } : undefined,
|
|
228
|
+
records: {
|
|
229
|
+
create: records.map((record) => ({
|
|
230
|
+
studentId: record.studentId,
|
|
231
|
+
status: record.status,
|
|
232
|
+
note: record.note ?? undefined,
|
|
233
|
+
excusedByExcusalId: record.excusedByExcusalId ?? undefined,
|
|
234
|
+
updatedById: record.updatedById ?? data.updatedById ?? undefined,
|
|
235
|
+
})),
|
|
236
|
+
},
|
|
177
237
|
},
|
|
178
238
|
include: attendanceUpdateInclude,
|
|
179
239
|
});
|
|
180
240
|
}
|
|
181
241
|
/** Create attendance for a specific event. */
|
|
182
242
|
export function createAttendanceForEvent(data) {
|
|
183
|
-
return
|
|
243
|
+
return createAttendance({
|
|
244
|
+
classId: data.classId,
|
|
245
|
+
eventId: data.eventId,
|
|
246
|
+
date: data.date,
|
|
247
|
+
presentIds: data.presentIds,
|
|
248
|
+
lateIds: data.lateIds,
|
|
249
|
+
absentIds: data.absentIds,
|
|
250
|
+
excusedIds: data.excusedIds,
|
|
251
|
+
records: data.records,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
/** Update attendance present/late/absent lists and sync per-student records. */
|
|
255
|
+
export async function updateAttendance(id, data) {
|
|
256
|
+
const studentIds = data.records.map((r) => r.studentId);
|
|
257
|
+
return prisma.$transaction(async (tx) => {
|
|
258
|
+
// Replace per-student rows for students in this update set.
|
|
259
|
+
await tx.attendanceRecord.deleteMany({
|
|
260
|
+
where: {
|
|
261
|
+
attendanceId: id,
|
|
262
|
+
studentId: { in: studentIds },
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
if (data.records.length > 0) {
|
|
266
|
+
await tx.attendanceRecord.createMany({
|
|
267
|
+
data: data.records.map((record) => ({
|
|
268
|
+
attendanceId: id,
|
|
269
|
+
studentId: record.studentId,
|
|
270
|
+
status: record.status,
|
|
271
|
+
note: record.note ?? null,
|
|
272
|
+
excusedByExcusalId: record.excusedByExcusalId ?? null,
|
|
273
|
+
updatedById: record.updatedById ?? null,
|
|
274
|
+
})),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return tx.attendance.update({
|
|
278
|
+
where: { id },
|
|
279
|
+
data: {
|
|
280
|
+
present: { set: data.presentIds.map((id) => ({ id })) },
|
|
281
|
+
late: { set: data.lateIds.map((id) => ({ id })) },
|
|
282
|
+
absent: { set: data.absentIds.map((id) => ({ id })) },
|
|
283
|
+
},
|
|
284
|
+
include: attendanceUpdateInclude,
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
function buildRecordsFromLists(input) {
|
|
289
|
+
const records = [];
|
|
290
|
+
for (const studentId of input.presentIds) {
|
|
291
|
+
records.push({ studentId, status: "PRESENT", updatedById: input.updatedById });
|
|
292
|
+
}
|
|
293
|
+
for (const studentId of input.lateIds) {
|
|
294
|
+
records.push({ studentId, status: "LATE", updatedById: input.updatedById });
|
|
295
|
+
}
|
|
296
|
+
for (const studentId of input.absentIds) {
|
|
297
|
+
records.push({ studentId, status: "ABSENT", updatedById: input.updatedById });
|
|
298
|
+
}
|
|
299
|
+
for (const studentId of input.excusedIds) {
|
|
300
|
+
records.push({ studentId, status: "EXCUSED", updatedById: input.updatedById });
|
|
301
|
+
}
|
|
302
|
+
return records;
|
|
303
|
+
}
|
|
304
|
+
/** Active (non-revoked) excusals covering a calendar date for given students. */
|
|
305
|
+
export function findActiveExcusalsForStudents(input) {
|
|
306
|
+
if (input.studentIds.length === 0) {
|
|
307
|
+
return Promise.resolve([]);
|
|
308
|
+
}
|
|
309
|
+
const day = toDateOnly(input.date);
|
|
310
|
+
return prisma.studentExcusal.findMany({
|
|
311
|
+
where: {
|
|
312
|
+
schoolId: input.schoolId,
|
|
313
|
+
revokedAt: null,
|
|
314
|
+
startDate: { lte: day },
|
|
315
|
+
endDate: { gte: day },
|
|
316
|
+
students: { some: { studentId: { in: input.studentIds } } },
|
|
317
|
+
},
|
|
318
|
+
include: {
|
|
319
|
+
students: { select: { studentId: true } },
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
/** Active excusals overlapping a date range for school students. */
|
|
324
|
+
export function findActiveExcusalsInRange(input) {
|
|
325
|
+
const start = toDateOnly(input.startDate);
|
|
326
|
+
const end = toDateOnly(input.endDate);
|
|
327
|
+
return prisma.studentExcusal.findMany({
|
|
328
|
+
where: {
|
|
329
|
+
schoolId: input.schoolId,
|
|
330
|
+
revokedAt: null,
|
|
331
|
+
startDate: { lte: end },
|
|
332
|
+
endDate: { gte: start },
|
|
333
|
+
...(input.studentIds?.length
|
|
334
|
+
? { students: { some: { studentId: { in: input.studentIds } } } }
|
|
335
|
+
: {}),
|
|
336
|
+
},
|
|
337
|
+
include: {
|
|
338
|
+
students: { select: { studentId: true } },
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
export function listExcusalsForSchool(input) {
|
|
343
|
+
return prisma.studentExcusal.findMany({
|
|
344
|
+
where: {
|
|
345
|
+
schoolId: input.schoolId,
|
|
346
|
+
...(input.includeRevoked ? {} : { revokedAt: null }),
|
|
347
|
+
...(input.studentId
|
|
348
|
+
? { students: { some: { studentId: input.studentId } } }
|
|
349
|
+
: {}),
|
|
350
|
+
},
|
|
351
|
+
include: excusalInclude,
|
|
352
|
+
orderBy: [{ startDate: "desc" }, { createdAt: "desc" }],
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
export function findExcusalById(excusalId) {
|
|
356
|
+
return prisma.studentExcusal.findUnique({
|
|
357
|
+
where: { id: excusalId },
|
|
358
|
+
include: {
|
|
359
|
+
...excusalInclude,
|
|
360
|
+
students: { select: { studentId: true } },
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
export function createStudentExcusal(data) {
|
|
365
|
+
return prisma.studentExcusal.create({
|
|
184
366
|
data: {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
367
|
+
schoolId: data.schoolId,
|
|
368
|
+
startDate: toDateOnly(data.startDate),
|
|
369
|
+
endDate: toDateOnly(data.endDate),
|
|
370
|
+
reason: data.reason,
|
|
371
|
+
note: data.note ?? null,
|
|
372
|
+
createdById: data.createdById,
|
|
373
|
+
students: {
|
|
374
|
+
create: data.studentIds.map((studentId) => ({ studentId })),
|
|
375
|
+
},
|
|
188
376
|
},
|
|
377
|
+
include: excusalInclude,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
export function updateStudentExcusal(excusalId, data) {
|
|
381
|
+
return prisma.$transaction(async (tx) => {
|
|
382
|
+
if (data.studentIds) {
|
|
383
|
+
await tx.studentExcusalStudent.deleteMany({ where: { excusalId } });
|
|
384
|
+
await tx.studentExcusalStudent.createMany({
|
|
385
|
+
data: data.studentIds.map((studentId) => ({ excusalId, studentId })),
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
return tx.studentExcusal.update({
|
|
389
|
+
where: { id: excusalId },
|
|
390
|
+
data: {
|
|
391
|
+
...(data.startDate !== undefined
|
|
392
|
+
? { startDate: toDateOnly(data.startDate) }
|
|
393
|
+
: {}),
|
|
394
|
+
...(data.endDate !== undefined ? { endDate: toDateOnly(data.endDate) } : {}),
|
|
395
|
+
...(data.reason !== undefined ? { reason: data.reason } : {}),
|
|
396
|
+
...(data.note !== undefined ? { note: data.note } : {}),
|
|
397
|
+
},
|
|
398
|
+
include: excusalInclude,
|
|
399
|
+
});
|
|
189
400
|
});
|
|
190
401
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
where: { id },
|
|
402
|
+
export function revokeStudentExcusal(excusalId, revokedById) {
|
|
403
|
+
return prisma.studentExcusal.update({
|
|
404
|
+
where: { id: excusalId },
|
|
195
405
|
data: {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
absent: { set: data.absentIds.map((id) => ({ id })) },
|
|
406
|
+
revokedAt: new Date(),
|
|
407
|
+
revokedById,
|
|
199
408
|
},
|
|
200
|
-
include:
|
|
409
|
+
include: excusalInclude,
|
|
201
410
|
});
|
|
202
411
|
}
|
|
412
|
+
/** Apply EXCUSED status for an excusal across matching class attendance sessions. */
|
|
413
|
+
export async function applyExcusalToAttendance(input) {
|
|
414
|
+
if (input.studentIds.length === 0) {
|
|
415
|
+
return { sessionsTouched: 0, recordsUpserted: 0 };
|
|
416
|
+
}
|
|
417
|
+
const start = startOfUtcDay(input.startDate);
|
|
418
|
+
const end = endOfUtcDay(input.endDate);
|
|
419
|
+
const events = await prisma.event.findMany({
|
|
420
|
+
where: {
|
|
421
|
+
class: {
|
|
422
|
+
schoolId: input.schoolId,
|
|
423
|
+
students: { some: { id: { in: input.studentIds } } },
|
|
424
|
+
},
|
|
425
|
+
startTime: { gte: start, lte: end },
|
|
426
|
+
},
|
|
427
|
+
select: {
|
|
428
|
+
id: true,
|
|
429
|
+
classId: true,
|
|
430
|
+
startTime: true,
|
|
431
|
+
class: {
|
|
432
|
+
select: {
|
|
433
|
+
id: true,
|
|
434
|
+
students: {
|
|
435
|
+
where: { id: { in: input.studentIds } },
|
|
436
|
+
select: { id: true },
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
let sessionsTouched = 0;
|
|
443
|
+
let recordsUpserted = 0;
|
|
444
|
+
for (const event of events) {
|
|
445
|
+
if (!event.classId || !event.class)
|
|
446
|
+
continue;
|
|
447
|
+
const classStudentIds = event.class.students.map((s) => s.id);
|
|
448
|
+
if (classStudentIds.length === 0)
|
|
449
|
+
continue;
|
|
450
|
+
let attendance = await prisma.attendance.findFirst({
|
|
451
|
+
where: { classId: event.classId, eventId: event.id },
|
|
452
|
+
include: {
|
|
453
|
+
present: { select: { id: true } },
|
|
454
|
+
late: { select: { id: true } },
|
|
455
|
+
absent: { select: { id: true } },
|
|
456
|
+
records: {
|
|
457
|
+
select: {
|
|
458
|
+
studentId: true,
|
|
459
|
+
status: true,
|
|
460
|
+
excusedByExcusalId: true,
|
|
461
|
+
note: true,
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
});
|
|
466
|
+
if (!attendance) {
|
|
467
|
+
// Default everyone present, then override excused students.
|
|
468
|
+
const allStudentIds = await prisma.class
|
|
469
|
+
.findUnique({
|
|
470
|
+
where: { id: event.classId },
|
|
471
|
+
select: { students: { select: { id: true } } },
|
|
472
|
+
})
|
|
473
|
+
.then((c) => c?.students.map((s) => s.id) ?? []);
|
|
474
|
+
const excusedSet = new Set(classStudentIds);
|
|
475
|
+
const presentIds = allStudentIds.filter((id) => !excusedSet.has(id));
|
|
476
|
+
attendance = await createAttendance({
|
|
477
|
+
classId: event.classId,
|
|
478
|
+
eventId: event.id,
|
|
479
|
+
date: event.startTime,
|
|
480
|
+
presentIds,
|
|
481
|
+
lateIds: [],
|
|
482
|
+
absentIds: [],
|
|
483
|
+
records: [
|
|
484
|
+
...presentIds.map((studentId) => ({
|
|
485
|
+
studentId,
|
|
486
|
+
status: "PRESENT",
|
|
487
|
+
updatedById: input.updatedById,
|
|
488
|
+
})),
|
|
489
|
+
...classStudentIds.map((studentId) => ({
|
|
490
|
+
studentId,
|
|
491
|
+
status: "EXCUSED",
|
|
492
|
+
note: null,
|
|
493
|
+
excusedByExcusalId: input.excusalId,
|
|
494
|
+
updatedById: input.updatedById,
|
|
495
|
+
})),
|
|
496
|
+
],
|
|
497
|
+
});
|
|
498
|
+
sessionsTouched += 1;
|
|
499
|
+
recordsUpserted += classStudentIds.length;
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
const presentIds = attendance.present
|
|
503
|
+
.map((s) => s.id)
|
|
504
|
+
.filter((id) => !classStudentIds.includes(id));
|
|
505
|
+
const lateIds = attendance.late
|
|
506
|
+
.map((s) => s.id)
|
|
507
|
+
.filter((id) => !classStudentIds.includes(id));
|
|
508
|
+
const absentIds = attendance.absent
|
|
509
|
+
.map((s) => s.id)
|
|
510
|
+
.filter((id) => !classStudentIds.includes(id));
|
|
511
|
+
const recordByStudent = new Map(attendance.records.map((r) => [r.studentId, r]));
|
|
512
|
+
const nextRecords = [];
|
|
513
|
+
for (const [studentId, existing] of recordByStudent) {
|
|
514
|
+
if (classStudentIds.includes(studentId)) {
|
|
515
|
+
nextRecords.push({
|
|
516
|
+
studentId,
|
|
517
|
+
status: "EXCUSED",
|
|
518
|
+
note: existing.note,
|
|
519
|
+
excusedByExcusalId: input.excusalId,
|
|
520
|
+
updatedById: input.updatedById,
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
nextRecords.push({
|
|
525
|
+
studentId,
|
|
526
|
+
status: existing.status,
|
|
527
|
+
note: existing.note,
|
|
528
|
+
excusedByExcusalId: existing.excusedByExcusalId,
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
for (const studentId of classStudentIds) {
|
|
533
|
+
if (!recordByStudent.has(studentId)) {
|
|
534
|
+
nextRecords.push({
|
|
535
|
+
studentId,
|
|
536
|
+
status: "EXCUSED",
|
|
537
|
+
excusedByExcusalId: input.excusalId,
|
|
538
|
+
updatedById: input.updatedById,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
await updateAttendance(attendance.id, {
|
|
543
|
+
presentIds,
|
|
544
|
+
lateIds,
|
|
545
|
+
absentIds,
|
|
546
|
+
records: nextRecords,
|
|
547
|
+
});
|
|
548
|
+
sessionsTouched += 1;
|
|
549
|
+
recordsUpserted += classStudentIds.length;
|
|
550
|
+
}
|
|
551
|
+
return { sessionsTouched, recordsUpserted };
|
|
552
|
+
}
|
|
553
|
+
/** Clear EXCUSED locks tied to a revoked/updated excusal (leave status EXCUSED unless cleared). */
|
|
554
|
+
export async function clearExcusalLocks(excusalId) {
|
|
555
|
+
return prisma.attendanceRecord.updateMany({
|
|
556
|
+
where: { excusedByExcusalId: excusalId },
|
|
557
|
+
data: { excusedByExcusalId: null },
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
export function listSchoolStudentMemberships(schoolId) {
|
|
561
|
+
return prisma.schoolMembership.findMany({
|
|
562
|
+
where: {
|
|
563
|
+
schoolId,
|
|
564
|
+
role: "STUDENT",
|
|
565
|
+
status: "ACTIVE",
|
|
566
|
+
},
|
|
567
|
+
select: {
|
|
568
|
+
userId: true,
|
|
569
|
+
user: {
|
|
570
|
+
select: {
|
|
571
|
+
id: true,
|
|
572
|
+
username: true,
|
|
573
|
+
profile: {
|
|
574
|
+
select: {
|
|
575
|
+
displayName: true,
|
|
576
|
+
profilePicture: true,
|
|
577
|
+
profilePictureThumbnail: true,
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
studentIn: {
|
|
581
|
+
where: { schoolId },
|
|
582
|
+
select: { id: true, name: true, section: true, subject: true },
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
orderBy: { createdAt: "asc" },
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
export function listAttendanceRecordsForSchoolStudents(input) {
|
|
591
|
+
if (input.studentIds.length === 0) {
|
|
592
|
+
return Promise.resolve([]);
|
|
593
|
+
}
|
|
594
|
+
return prisma.attendanceRecord.findMany({
|
|
595
|
+
where: {
|
|
596
|
+
studentId: { in: input.studentIds },
|
|
597
|
+
attendance: {
|
|
598
|
+
class: { schoolId: input.schoolId },
|
|
599
|
+
...(input.from || input.to
|
|
600
|
+
? {
|
|
601
|
+
date: {
|
|
602
|
+
...(input.from ? { gte: input.from } : {}),
|
|
603
|
+
...(input.to ? { lte: input.to } : {}),
|
|
604
|
+
},
|
|
605
|
+
}
|
|
606
|
+
: {}),
|
|
607
|
+
},
|
|
608
|
+
},
|
|
609
|
+
select: {
|
|
610
|
+
studentId: true,
|
|
611
|
+
status: true,
|
|
612
|
+
attendance: {
|
|
613
|
+
select: {
|
|
614
|
+
id: true,
|
|
615
|
+
date: true,
|
|
616
|
+
classId: true,
|
|
617
|
+
eventId: true,
|
|
618
|
+
class: { select: { id: true, name: true } },
|
|
619
|
+
event: { select: { id: true, name: true, startTime: true } },
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
export function listTodaySchoolAttendanceSessions(schoolId, day) {
|
|
626
|
+
const start = startOfUtcDay(day);
|
|
627
|
+
const end = endOfUtcDay(day);
|
|
628
|
+
return prisma.attendance.findMany({
|
|
629
|
+
where: {
|
|
630
|
+
class: { schoolId },
|
|
631
|
+
OR: [
|
|
632
|
+
{ date: { gte: start, lte: end } },
|
|
633
|
+
{ event: { startTime: { gte: start, lte: end } } },
|
|
634
|
+
],
|
|
635
|
+
},
|
|
636
|
+
select: {
|
|
637
|
+
id: true,
|
|
638
|
+
date: true,
|
|
639
|
+
classId: true,
|
|
640
|
+
eventId: true,
|
|
641
|
+
class: {
|
|
642
|
+
select: {
|
|
643
|
+
id: true,
|
|
644
|
+
name: true,
|
|
645
|
+
section: true,
|
|
646
|
+
subject: true,
|
|
647
|
+
_count: { select: { students: true } },
|
|
648
|
+
},
|
|
649
|
+
},
|
|
650
|
+
event: {
|
|
651
|
+
select: { id: true, name: true, startTime: true, endTime: true },
|
|
652
|
+
},
|
|
653
|
+
records: {
|
|
654
|
+
select: {
|
|
655
|
+
studentId: true,
|
|
656
|
+
status: true,
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
orderBy: { date: "asc" },
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
export function toDateOnly(date) {
|
|
664
|
+
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
|
665
|
+
}
|
|
666
|
+
export function startOfUtcDay(date) {
|
|
667
|
+
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0, 0));
|
|
668
|
+
}
|
|
669
|
+
export function endOfUtcDay(date) {
|
|
670
|
+
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 23, 59, 59, 999));
|
|
671
|
+
}
|
|
203
672
|
//# sourceMappingURL=attendance.js.map
|
|
204
|
-
//# debugId=
|
|
673
|
+
//# debugId=04968f1d-49ec-52b6-ab2b-a86286e300ce
|