@brite-future-schools-contracts/contracts 1.0.7 → 2.0.2

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.
Files changed (40) hide show
  1. package/dist/chunk-AEZO7MJD.js +127 -0
  2. package/dist/chunk-AEZO7MJD.js.map +1 -0
  3. package/dist/chunk-IR6JUP6L.js +48 -0
  4. package/dist/chunk-IR6JUP6L.js.map +1 -0
  5. package/dist/chunk-PCHAGP5X.js +1 -0
  6. package/dist/contracts/index.cjs +179 -0
  7. package/dist/contracts/index.cjs.map +1 -0
  8. package/dist/contracts/index.d.cts +201 -0
  9. package/dist/contracts/index.d.ts +159 -7896
  10. package/dist/contracts/index.js +22 -1281
  11. package/dist/contracts/index.js.map +1 -1
  12. package/dist/index.cjs +222 -0
  13. package/dist/index.cjs.map +1 -0
  14. package/dist/index.d.cts +4 -0
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +62 -1393
  17. package/dist/index.js.map +1 -1
  18. package/dist/schemas/index.cjs +181 -0
  19. package/dist/schemas/index.cjs.map +1 -0
  20. package/dist/schemas/index.d.cts +145 -0
  21. package/dist/schemas/index.d.ts +130 -841
  22. package/dist/schemas/index.js +42 -572
  23. package/dist/schemas/index.js.map +1 -1
  24. package/package.json +32 -59
  25. package/dist/chunk-7A7N5DCT.mjs +0 -854
  26. package/dist/chunk-7A7N5DCT.mjs.map +0 -1
  27. package/dist/chunk-OJCH3PZZ.mjs +0 -495
  28. package/dist/chunk-OJCH3PZZ.mjs.map +0 -1
  29. package/dist/contracts/index.d.mts +0 -7938
  30. package/dist/contracts/index.mjs +0 -36
  31. package/dist/index.d.mts +0 -4
  32. package/dist/index.mjs +0 -149
  33. package/dist/index.mjs.map +0 -1
  34. package/dist/schemas/index.d.mts +0 -856
  35. package/dist/schemas/index.mjs +0 -117
  36. package/dist/schemas/index.mjs.map +0 -1
  37. package/src/contracts/index.ts +0 -1096
  38. package/src/index.ts +0 -2
  39. package/src/schemas/index.ts +0 -524
  40. /package/dist/{contracts/index.mjs.map → chunk-PCHAGP5X.js.map} +0 -0
@@ -1,1096 +0,0 @@
1
- import { oc } from "@orpc/contract";
2
- import { z } from "zod";
3
- import {
4
- loginInputSchema,
5
- loginOutputSchema,
6
- mfaVerifyInputSchema,
7
- changePasswordInputSchema,
8
- refreshTokenOutputSchema,
9
- refreshInputSchema,
10
- branchSchema,
11
- createBranchInputSchema,
12
- studentSchema,
13
- createStudentInputSchema,
14
- listStudentsInputSchema,
15
- guardianSchema,
16
- sponsorSchema,
17
- sponsorTypeEnum,
18
- subjectSchema,
19
- assessmentScoreInputSchema,
20
- reportCardStatusEnum,
21
- gradeLevelEnum,
22
- assessmentTypeEnum,
23
- feeItemSchema,
24
- recordPaymentInputSchema,
25
- invoiceStatusEnum,
26
- staffSchema,
27
- leaveRequestInputSchema,
28
- leaveStatusEnum,
29
- tripLogInputSchema,
30
- busRegisterEntrySchema,
31
- busRegisterStatusEnum,
32
- vehicleSchema,
33
- vehicleStatusEnum,
34
- vehicleTypeEnum,
35
- vehicleOwnershipEnum,
36
- routeSchema,
37
- tripLogSchema,
38
- assetCategoryEnum,
39
- assetConditionEnum,
40
- maintenanceTicketStatusEnum,
41
- borrowingStatusEnum,
42
- incidentSeverityEnum,
43
- incidentStatusEnum,
44
- disciplineRulingEnum,
45
- payrollRunStatusEnum,
46
- sendBulkSmsInputSchema,
47
- paginationSchema,
48
- uuidSchema,
49
- userSchema,
50
- } from "../schemas";
51
-
52
- // ─── Auth Router ──────────────────────────────────────────────────────────────
53
-
54
- export const authContract = {
55
- login: oc.input(loginInputSchema).output(loginOutputSchema),
56
-
57
- refresh: oc.input(refreshInputSchema).output(refreshTokenOutputSchema),
58
-
59
- logout: oc.output(z.object({ success: z.boolean() })),
60
-
61
- me: oc.output(
62
- userSchema.extend({
63
- roles: z.array(
64
- z.object({
65
- id: uuidSchema,
66
- name: z.string(),
67
- branchId: uuidSchema.nullable(),
68
- }),
69
- ),
70
- }),
71
- ),
72
-
73
- verifyMfa: oc
74
- .input(mfaVerifyInputSchema)
75
- .output(z.object({ verified: z.boolean() })),
76
-
77
- changePassword: oc
78
- .input(changePasswordInputSchema)
79
- .output(z.object({ success: z.boolean() })),
80
-
81
- switchBranch: oc
82
- .input(z.object({ branchId: uuidSchema }))
83
- .output(z.object({ accessToken: z.string() })),
84
- };
85
-
86
- // ─── Branches Router ──────────────────────────────────────────────────────────
87
-
88
- export const branchesContract = {
89
- list: oc.output(z.array(branchSchema)),
90
-
91
- get: oc.input(z.object({ id: uuidSchema })).output(branchSchema),
92
-
93
- create: oc.input(createBranchInputSchema).output(branchSchema),
94
-
95
- update: oc
96
- .input(createBranchInputSchema.partial().extend({ id: uuidSchema }))
97
- .output(branchSchema),
98
-
99
- updateFeatureFlags: oc
100
- .input(z.object({ branchId: uuidSchema, flags: z.record(z.boolean()) }))
101
- .output(branchSchema),
102
- };
103
-
104
- // ─── Students Router ──────────────────────────────────────────────────────────
105
-
106
- const studentWithGuardians = studentSchema.extend({
107
- guardians: z.array(guardianSchema),
108
- sponsors: z.array(sponsorSchema),
109
- currentClass: z
110
- .object({ id: uuidSchema, name: z.string(), grade: z.string() })
111
- .nullable(),
112
- });
113
-
114
- export const studentsContract = {
115
- list: oc.input(listStudentsInputSchema).output(
116
- z.object({
117
- data: z.array(studentSchema),
118
- total: z.number(),
119
- page: z.number(),
120
- limit: z.number(),
121
- }),
122
- ),
123
-
124
- get: oc.input(z.object({ id: uuidSchema })).output(studentWithGuardians),
125
-
126
- create: oc.input(createStudentInputSchema).output(studentSchema),
127
-
128
- update: oc
129
- .input(createStudentInputSchema.partial().extend({ id: uuidSchema }))
130
- .output(studentSchema),
131
-
132
- enroll: oc
133
- .input(
134
- z.object({
135
- studentId: uuidSchema,
136
- classId: uuidSchema,
137
- termId: uuidSchema,
138
- }),
139
- )
140
- .output(z.object({ success: z.boolean() })),
141
-
142
- transfer: oc
143
- .input(
144
- z.object({
145
- studentId: uuidSchema,
146
- toBranchId: uuidSchema,
147
- reason: z.string(),
148
- }),
149
- )
150
- .output(z.object({ success: z.boolean() })),
151
-
152
- linkGuardian: oc
153
- .input(
154
- z.object({
155
- studentId: uuidSchema,
156
- guardianId: uuidSchema,
157
- isPrimary: z.boolean(),
158
- }),
159
- )
160
- .output(z.object({ success: z.boolean() })),
161
-
162
- linkSponsor: oc
163
- .input(
164
- z.object({
165
- studentId: uuidSchema,
166
- sponsorId: uuidSchema,
167
- termId: uuidSchema,
168
- feeItemIds: z.array(uuidSchema),
169
- }),
170
- )
171
- .output(z.object({ success: z.boolean() })),
172
-
173
- listGuardians: oc
174
- .input(
175
- z.object({
176
- branchId: uuidSchema.optional(),
177
- search: z.string().optional(),
178
- }),
179
- )
180
- .output(
181
- z.array(
182
- guardianSchema.extend({
183
- email: z.string().nullable(),
184
- linkedStudents: z.array(
185
- z.object({
186
- id: uuidSchema,
187
- firstName: z.string(),
188
- lastName: z.string(),
189
- admissionNo: z.string(),
190
- status: z.string(),
191
- }),
192
- ),
193
- }),
194
- ),
195
- ),
196
-
197
- createGuardian: oc
198
- .input(
199
- z.object({
200
- firstName: z.string().min(1),
201
- middleName: z.string().optional(),
202
- lastName: z.string().min(1),
203
- relationship: z.string().min(1),
204
- phone: z.string().min(9),
205
- email: z.string().email().optional(),
206
- nationalId: z.string().optional(),
207
- occupation: z.string().optional(),
208
- address: z.string().optional(),
209
- }),
210
- )
211
- .output(guardianSchema),
212
-
213
- listClasses: oc
214
- .input(z.object({ branchId: uuidSchema.optional() }))
215
- .output(
216
- z.array(
217
- z.object({
218
- id: uuidSchema,
219
- name: z.string(),
220
- grade: z.string(),
221
- level: z.string(),
222
- }),
223
- ),
224
- ),
225
-
226
- listGrades: oc
227
- .input(z.object({ branchId: uuidSchema.optional() }))
228
- .output(
229
- z.array(
230
- z.object({
231
- id: uuidSchema,
232
- name: z.string(),
233
- level: z.string(),
234
- }),
235
- ),
236
- ),
237
-
238
- listTerms: oc
239
- .input(z.object({ branchId: uuidSchema.optional() }))
240
- .output(
241
- z.array(
242
- z.object({
243
- id: uuidSchema,
244
- name: z.string(),
245
- isCurrent: z.boolean(),
246
- startDate: z.string(),
247
- endDate: z.string(),
248
- }),
249
- ),
250
- ),
251
-
252
- listSponsors: oc
253
- .input(z.object({ branchId: uuidSchema.optional() }))
254
- .output(z.array(sponsorSchema)),
255
- };
256
-
257
- // ─── Academics Router ─────────────────────────────────────────────────────────
258
-
259
- export const academicsContract = {
260
- subjects: {
261
- list: oc
262
- .input(
263
- z.object({
264
- branchId: uuidSchema,
265
- gradeLevel: gradeLevelEnum.optional(),
266
- }),
267
- )
268
- .output(z.array(subjectSchema)),
269
- create: oc
270
- .input(
271
- z.object({
272
- name: z.string(),
273
- code: z.string(),
274
- gradeLevel: gradeLevelEnum,
275
- departmentId: uuidSchema.optional(),
276
- }),
277
- )
278
- .output(subjectSchema),
279
- },
280
-
281
- timetable: {
282
- get: oc.input(z.object({ classId: uuidSchema, termId: uuidSchema })).output(
283
- z.array(
284
- z.object({
285
- id: uuidSchema,
286
- dayOfWeek: z.number().min(1).max(5),
287
- periodNo: z.number(),
288
- subject: subjectSchema,
289
- teacherName: z.string(),
290
- room: z.string().nullable(),
291
- startTime: z.string(),
292
- endTime: z.string(),
293
- }),
294
- ),
295
- ),
296
- upsert: oc
297
- .input(
298
- z.object({
299
- classId: uuidSchema,
300
- termId: uuidSchema,
301
- slots: z.array(
302
- z.object({
303
- dayOfWeek: z.number().min(1).max(5),
304
- periodNo: z.number(),
305
- subjectId: uuidSchema,
306
- teacherId: uuidSchema,
307
- room: z.string().optional(),
308
- startTime: z.string(),
309
- endTime: z.string(),
310
- }),
311
- ),
312
- }),
313
- )
314
- .output(z.object({ success: z.boolean() })),
315
- },
316
-
317
- assessments: {
318
- list: oc
319
- .input(
320
- z.object({
321
- classId: uuidSchema,
322
- termId: uuidSchema,
323
- type: assessmentTypeEnum.optional(),
324
- }),
325
- )
326
- .output(
327
- z.array(
328
- z.object({
329
- id: uuidSchema,
330
- title: z.string(),
331
- type: assessmentTypeEnum,
332
- date: z.string(),
333
- maxScore: z.number(),
334
- subjectId: uuidSchema,
335
- }),
336
- ),
337
- ),
338
- create: oc
339
- .input(
340
- z.object({
341
- title: z.string(),
342
- type: assessmentTypeEnum,
343
- classId: uuidSchema,
344
- subjectId: uuidSchema,
345
- termId: uuidSchema,
346
- date: z.string(),
347
- maxScore: z.number(),
348
- }),
349
- )
350
- .output(z.object({ id: uuidSchema })),
351
- enterScores: oc
352
- .input(z.object({ scores: z.array(assessmentScoreInputSchema) }))
353
- .output(z.object({ saved: z.number() })),
354
- },
355
-
356
- reportCards: {
357
- list: oc
358
- .input(
359
- z.object({
360
- branchId: uuidSchema,
361
- termId: uuidSchema,
362
- classId: uuidSchema.optional(),
363
- status: reportCardStatusEnum.optional(),
364
- }),
365
- )
366
- .output(
367
- z.array(
368
- z.object({
369
- id: uuidSchema,
370
- studentId: uuidSchema,
371
- studentName: z.string(),
372
- status: reportCardStatusEnum,
373
- generatedAt: z.string().datetime().nullable(),
374
- }),
375
- ),
376
- ),
377
- get: oc
378
- .input(z.object({ studentId: uuidSchema, termId: uuidSchema }))
379
- .output(
380
- z.object({
381
- id: uuidSchema,
382
- studentId: uuidSchema,
383
- termId: uuidSchema,
384
- status: reportCardStatusEnum,
385
- scores: z.array(z.any()),
386
- teacherComments: z.string().nullable(),
387
- generatedAt: z.string().datetime().nullable(),
388
- }),
389
- ),
390
- updateStatus: oc
391
- .input(
392
- z.object({
393
- id: uuidSchema,
394
- status: reportCardStatusEnum,
395
- comments: z.string().optional(),
396
- }),
397
- )
398
- .output(z.object({ success: z.boolean() })),
399
- },
400
-
401
- attendance: {
402
- markClass: oc
403
- .input(
404
- z.object({
405
- classId: uuidSchema,
406
- date: z.string(),
407
- subjectId: uuidSchema,
408
- records: z.array(
409
- z.object({
410
- studentId: uuidSchema,
411
- present: z.boolean(),
412
- note: z.string().optional(),
413
- }),
414
- ),
415
- }),
416
- )
417
- .output(z.object({ saved: z.number() })),
418
- getClassAttendance: oc
419
- .input(
420
- z.object({ classId: uuidSchema, from: z.string(), to: z.string() }),
421
- )
422
- .output(
423
- z.array(
424
- z.object({
425
- studentId: uuidSchema,
426
- studentName: z.string(),
427
- presentDays: z.number(),
428
- absentDays: z.number(),
429
- percentage: z.number(),
430
- }),
431
- ),
432
- ),
433
- },
434
- };
435
-
436
- // ─── Finance Router ───────────────────────────────────────────────────────────
437
-
438
- export const financeContract = {
439
- feeItems: {
440
- list: oc
441
- .input(z.object({ branchId: uuidSchema }))
442
- .output(z.array(feeItemSchema)),
443
- create: oc
444
- .input(
445
- z.object({
446
- name: z.string(),
447
- category: z.any(),
448
- defaultAmount: z.number(),
449
- applicableGrades: z.array(gradeLevelEnum),
450
- }),
451
- )
452
- .output(feeItemSchema),
453
- },
454
-
455
- invoices: {
456
- listForStudent: oc
457
- .input(z.object({ studentId: uuidSchema, termId: uuidSchema.optional() }))
458
- .output(
459
- z.array(
460
- z.object({
461
- id: uuidSchema,
462
- termId: uuidSchema,
463
- payerType: z.string(),
464
- totalAmount: z.number(),
465
- paidAmount: z.number(),
466
- balance: z.number(),
467
- status: invoiceStatusEnum,
468
- dueDate: z.string().nullable(),
469
- }),
470
- ),
471
- ),
472
- generate: oc
473
- .input(
474
- z.object({
475
- branchId: uuidSchema,
476
- termId: uuidSchema,
477
- studentIds: z.array(uuidSchema).optional(),
478
- }),
479
- )
480
- .output(z.object({ generated: z.number() })),
481
- },
482
-
483
- payments: {
484
- record: oc
485
- .input(recordPaymentInputSchema)
486
- .output(z.object({ id: uuidSchema, receipt: z.string() })),
487
- listForStudent: oc.input(z.object({ studentId: uuidSchema })).output(
488
- z.array(
489
- z.object({
490
- id: uuidSchema,
491
- amount: z.number(),
492
- method: z.string(),
493
- reference: z.string().nullable(),
494
- paymentDate: z.string(),
495
- createdAt: z.string(),
496
- }),
497
- ),
498
- ),
499
- },
500
-
501
- blocks: {
502
- list: oc
503
- .input(
504
- z.object({
505
- branchId: uuidSchema,
506
- termId: uuidSchema,
507
- active: z.boolean().optional(),
508
- }),
509
- )
510
- .output(
511
- z.array(
512
- z.object({
513
- id: uuidSchema,
514
- studentId: uuidSchema,
515
- studentName: z.string(),
516
- blockType: z.string(),
517
- isActive: z.boolean(),
518
- balanceAtBlock: z.number(),
519
- }),
520
- ),
521
- ),
522
- lift: oc
523
- .input(z.object({ blockId: uuidSchema, reason: z.string() }))
524
- .output(z.object({ success: z.boolean() })),
525
- },
526
-
527
- reports: {
528
- termCollection: oc
529
- .input(z.object({ branchId: uuidSchema, termId: uuidSchema }))
530
- .output(
531
- z.object({
532
- totalExpected: z.number(),
533
- totalCollected: z.number(),
534
- totalOutstanding: z.number(),
535
- collectionRate: z.number(),
536
- byPaymentMethod: z.record(z.number()),
537
- }),
538
- ),
539
- },
540
- };
541
-
542
- // ─── HR Router ────────────────────────────────────────────────────────────────
543
-
544
- export const hrContract = {
545
- staff: {
546
- list: oc
547
- .input(
548
- paginationSchema.extend({
549
- branchId: uuidSchema,
550
- search: z.string().optional(),
551
- departmentId: uuidSchema.optional(),
552
- }),
553
- )
554
- .output(z.object({ data: z.array(staffSchema), total: z.number() })),
555
- get: oc.input(z.object({ id: uuidSchema })).output(staffSchema),
556
- create: oc
557
- .input(
558
- staffSchema
559
- .omit({ id: true })
560
- .partial({
561
- contractEnd: true,
562
- tscNumber: true,
563
- kraPin: true,
564
- nhifNo: true,
565
- nssfNo: true,
566
- bankName: true,
567
- bankAccount: true,
568
- departmentId: true,
569
- }),
570
- )
571
- .output(staffSchema),
572
- },
573
-
574
- leave: {
575
- request: oc
576
- .input(leaveRequestInputSchema)
577
- .output(z.object({ id: uuidSchema })),
578
- list: oc
579
- .input(
580
- z.object({
581
- branchId: uuidSchema,
582
- staffId: uuidSchema.optional(),
583
- status: leaveStatusEnum.optional(),
584
- }),
585
- )
586
- .output(z.array(z.any())),
587
- approve: oc
588
- .input(
589
- z.object({
590
- id: uuidSchema,
591
- approved: z.boolean(),
592
- comment: z.string().optional(),
593
- }),
594
- )
595
- .output(z.object({ success: z.boolean() })),
596
- },
597
-
598
- attendance: {
599
- mark: oc
600
- .input(
601
- z.object({
602
- staffId: uuidSchema,
603
- date: z.string(),
604
- checkIn: z.string().optional(),
605
- checkOut: z.string().optional(),
606
- status: z.enum(["present", "absent", "late", "half_day"]),
607
- }),
608
- )
609
- .output(z.object({ success: z.boolean() })),
610
- getSummary: oc
611
- .input(
612
- z.object({ staffId: uuidSchema, month: z.number(), year: z.number() }),
613
- )
614
- .output(
615
- z.object({
616
- presentDays: z.number(),
617
- absentDays: z.number(),
618
- lateDays: z.number(),
619
- }),
620
- ),
621
- },
622
- };
623
-
624
- // ─── Payroll Router ───────────────────────────────────────────────────────────
625
-
626
- export const payrollContract = {
627
- runs: {
628
- list: oc
629
- .input(z.object({ branchId: uuidSchema, year: z.number().optional() }))
630
- .output(
631
- z.array(
632
- z.object({
633
- id: uuidSchema,
634
- month: z.number(),
635
- year: z.number(),
636
- status: payrollRunStatusEnum,
637
- netTotal: z.number(),
638
- }),
639
- ),
640
- ),
641
- initiate: oc
642
- .input(
643
- z.object({ branchId: uuidSchema, month: z.number(), year: z.number() }),
644
- )
645
- .output(z.object({ id: uuidSchema, preview: z.array(z.any()) })),
646
- approve: oc
647
- .input(z.object({ id: uuidSchema }))
648
- .output(z.object({ success: z.boolean() })),
649
- getPayslip: oc
650
- .input(z.object({ payrollRunId: uuidSchema, staffId: uuidSchema }))
651
- .output(z.object({ url: z.string() })),
652
- },
653
- };
654
-
655
- // ─── Transport Router ─────────────────────────────────────────────────────────
656
-
657
- export const transportContract = {
658
- vehicles: {
659
- list: oc
660
- .input(z.object({ branchId: uuidSchema }))
661
- .output(z.array(vehicleSchema)),
662
- create: oc
663
- .input(
664
- z.object({
665
- branchId: uuidSchema,
666
- plateNo: z.string().min(1),
667
- type: vehicleTypeEnum,
668
- ownershipType: vehicleOwnershipEnum,
669
- make: z.string().optional(),
670
- model: z.string().optional(),
671
- capacity: z.number().int().positive().optional(),
672
- insuranceExpiry: z.string().optional(),
673
- inspectionDate: z.string().optional(),
674
- driverUserId: uuidSchema.optional(),
675
- }),
676
- )
677
- .output(vehicleSchema),
678
- updateStatus: oc
679
- .input(z.object({ id: uuidSchema, status: vehicleStatusEnum }))
680
- .output(vehicleSchema),
681
- },
682
-
683
- routes: {
684
- list: oc
685
- .input(z.object({ branchId: uuidSchema }))
686
- .output(z.array(routeSchema)),
687
- create: oc
688
- .input(
689
- z.object({
690
- branchId: uuidSchema,
691
- name: z.string().min(1),
692
- description: z.string().optional(),
693
- stops: z.array(
694
- z.object({
695
- stopName: z.string().min(1),
696
- stopOrder: z.number().int(),
697
- estimatedTime: z.string().optional(),
698
- }),
699
- ).optional(),
700
- }),
701
- )
702
- .output(routeSchema),
703
- toggle: oc
704
- .input(z.object({ id: uuidSchema, isActive: z.boolean() }))
705
- .output(z.object({ success: z.boolean() })),
706
- },
707
-
708
- trips: {
709
- list: oc
710
- .input(
711
- z.object({
712
- branchId: uuidSchema,
713
- date: z.string().optional(),
714
- vehicleId: uuidSchema.optional(),
715
- }),
716
- )
717
- .output(z.array(tripLogSchema)),
718
- get: oc
719
- .input(z.object({ tripId: uuidSchema }))
720
- .output(
721
- tripLogSchema.extend({
722
- boardings: z.array(
723
- z.object({
724
- studentId: uuidSchema,
725
- studentName: z.string(),
726
- status: busRegisterStatusEnum,
727
- recordedAt: z.string().datetime(),
728
- }),
729
- ),
730
- }),
731
- ),
732
- start: oc.input(tripLogInputSchema).output(z.object({ id: uuidSchema })),
733
- recordBoardings: oc
734
- .input(
735
- z.object({
736
- tripId: uuidSchema,
737
- entries: z.array(busRegisterEntrySchema),
738
- }),
739
- )
740
- .output(z.object({ saved: z.number() })),
741
- complete: oc
742
- .input(
743
- z.object({ tripId: uuidSchema, arrivalTime: z.string().datetime() }),
744
- )
745
- .output(z.object({ success: z.boolean() })),
746
- reportIncident: oc
747
- .input(
748
- z.object({
749
- tripId: uuidSchema,
750
- description: z.string(),
751
- severity: incidentSeverityEnum,
752
- location: z.string().optional(),
753
- }),
754
- )
755
- .output(z.object({ id: uuidSchema })),
756
- },
757
-
758
- maintenance: {
759
- log: oc
760
- .input(
761
- z.object({
762
- vehicleId: uuidSchema,
763
- issue: z.string(),
764
- severity: z.enum(["routine", "urgent", "critical"]),
765
- }),
766
- )
767
- .output(z.object({ id: uuidSchema })),
768
- list: oc
769
- .input(z.object({ vehicleId: uuidSchema }))
770
- .output(z.array(z.any())),
771
- resolve: oc
772
- .input(
773
- z.object({
774
- logId: uuidSchema,
775
- notes: z.string().optional(),
776
- cost: z.number().optional(),
777
- }),
778
- )
779
- .output(z.object({ success: z.boolean() })),
780
- },
781
- };
782
-
783
- // ─── Assets Router ────────────────────────────────────────────────────────────
784
-
785
- export const assetsContract = {
786
- list: oc
787
- .input(
788
- paginationSchema.extend({
789
- branchId: uuidSchema,
790
- category: assetCategoryEnum.optional(),
791
- search: z.string().optional(),
792
- }),
793
- )
794
- .output(z.object({ data: z.array(z.any()), total: z.number() })),
795
- create: oc
796
- .input(
797
- z.object({
798
- name: z.string(),
799
- category: assetCategoryEnum,
800
- serialNo: z.string().optional(),
801
- purchaseDate: z.string(),
802
- purchaseCost: z.number(),
803
- supplier: z.string().optional(),
804
- warrantyExpiry: z.string().optional(),
805
- condition: assetConditionEnum,
806
- depreciationRate: z.number().min(0).max(100),
807
- }),
808
- )
809
- .output(z.object({ id: uuidSchema })),
810
- assign: oc
811
- .input(
812
- z.object({
813
- assetId: uuidSchema,
814
- staffId: uuidSchema.optional(),
815
- room: z.string().optional(),
816
- }),
817
- )
818
- .output(z.object({ success: z.boolean() })),
819
- ticket: {
820
- create: oc
821
- .input(
822
- z.object({
823
- assetId: uuidSchema,
824
- description: z.string(),
825
- severity: z.string(),
826
- }),
827
- )
828
- .output(z.object({ id: uuidSchema })),
829
- update: oc
830
- .input(
831
- z.object({
832
- ticketId: uuidSchema,
833
- status: maintenanceTicketStatusEnum,
834
- notes: z.string().optional(),
835
- cost: z.number().optional(),
836
- }),
837
- )
838
- .output(z.object({ success: z.boolean() })),
839
- },
840
- };
841
-
842
- // ─── Library Router ───────────────────────────────────────────────────────────
843
-
844
- export const libraryContract = {
845
- books: {
846
- list: oc
847
- .input(
848
- paginationSchema.extend({
849
- branchId: uuidSchema,
850
- search: z.string().optional(),
851
- available: z.boolean().optional(),
852
- }),
853
- )
854
- .output(z.object({ data: z.array(z.any()), total: z.number() })),
855
- create: oc
856
- .input(
857
- z.object({
858
- isbn: z.string(),
859
- title: z.string(),
860
- author: z.string(),
861
- publisher: z.string().optional(),
862
- year: z.number().optional(),
863
- subjectArea: z.string().optional(),
864
- copiesTotal: z.number().min(1),
865
- shelfLocation: z.string().optional(),
866
- }),
867
- )
868
- .output(z.object({ id: uuidSchema })),
869
- },
870
- borrowings: {
871
- checkout: oc
872
- .input(
873
- z.object({
874
- bookId: uuidSchema,
875
- borrowerType: z.enum(["student", "staff"]),
876
- borrowerId: uuidSchema,
877
- dueDate: z.string(),
878
- }),
879
- )
880
- .output(z.object({ id: uuidSchema })),
881
- return: oc
882
- .input(
883
- z.object({ borrowingId: uuidSchema, condition: z.string().optional() }),
884
- )
885
- .output(z.object({ fine: z.number() })),
886
- listOverdue: oc
887
- .input(z.object({ branchId: uuidSchema }))
888
- .output(z.array(z.any())),
889
- },
890
- };
891
-
892
- // ─── Discipline Router ────────────────────────────────────────────────────────
893
-
894
- export const disciplineContract = {
895
- incidents: {
896
- log: oc
897
- .input(
898
- z.object({
899
- studentId: uuidSchema,
900
- incidentType: z.string(),
901
- description: z.string(),
902
- severity: incidentSeverityEnum,
903
- incidentDate: z.string(),
904
- requiresCounsellor: z.boolean().default(false),
905
- }),
906
- )
907
- .output(z.object({ id: uuidSchema })),
908
- list: oc
909
- .input(
910
- paginationSchema.extend({
911
- branchId: uuidSchema,
912
- studentId: uuidSchema.optional(),
913
- status: incidentStatusEnum.optional(),
914
- }),
915
- )
916
- .output(z.object({ data: z.array(z.any()), total: z.number() })),
917
- rule: oc
918
- .input(
919
- z.object({
920
- incidentId: uuidSchema,
921
- ruling: disciplineRulingEnum,
922
- suspensionDays: z.number().optional(),
923
- notes: z.string(),
924
- }),
925
- )
926
- .output(z.object({ success: z.boolean() })),
927
- },
928
- };
929
-
930
- // ─── Communications Router ────────────────────────────────────────────────────
931
-
932
- export const commsContract = {
933
- notifications: {
934
- list: oc
935
- .input(paginationSchema.extend({ unreadOnly: z.boolean().optional() }))
936
- .output(z.object({ data: z.array(z.any()), unreadCount: z.number() })),
937
- markRead: oc
938
- .input(z.object({ ids: z.array(uuidSchema) }))
939
- .output(z.object({ updated: z.number() })),
940
- },
941
- messages: {
942
- list: oc.input(paginationSchema).output(z.array(z.any())),
943
- send: oc
944
- .input(
945
- z.object({
946
- recipientId: uuidSchema,
947
- subject: z.string(),
948
- body: z.string(),
949
- parentId: uuidSchema.optional(),
950
- }),
951
- )
952
- .output(z.object({ id: uuidSchema })),
953
- },
954
- sms: {
955
- send: oc
956
- .input(sendBulkSmsInputSchema)
957
- .output(z.object({ queued: z.number() })),
958
- },
959
- notices: {
960
- list: oc
961
- .input(z.object({ branchId: uuidSchema, active: z.boolean().optional() }))
962
- .output(z.array(z.any())),
963
- create: oc
964
- .input(
965
- z.object({
966
- title: z.string(),
967
- body: z.string(),
968
- audienceType: z.string(),
969
- audienceIds: z.array(uuidSchema).optional(),
970
- publishAt: z.string().optional(),
971
- expiresAt: z.string().optional(),
972
- }),
973
- )
974
- .output(z.object({ id: uuidSchema })),
975
- },
976
- };
977
-
978
- // ─── Admin Router ─────────────────────────────────────────────────────────────
979
-
980
- export const adminContract = {
981
- auditLogs: {
982
- list: oc
983
- .input(
984
- paginationSchema.extend({
985
- branchId: uuidSchema.optional(),
986
- userId: uuidSchema.optional(),
987
- action: z.string().optional(),
988
- from: z.string().optional(),
989
- to: z.string().optional(),
990
- }),
991
- )
992
- .output(z.object({ data: z.array(z.any()), total: z.number() })),
993
- },
994
- users: {
995
- create: oc
996
- .input(
997
- z.object({
998
- firstName: z.string(),
999
- middleName: z.string().optional(),
1000
- lastName: z.string(),
1001
- email: z.string().email(),
1002
- phone: z.string().optional(),
1003
- roleId: uuidSchema,
1004
- branchId: uuidSchema,
1005
- temporaryPassword: z.string().min(8),
1006
- }),
1007
- )
1008
- .output(z.object({ id: uuidSchema })),
1009
- suspend: oc
1010
- .input(z.object({ userId: uuidSchema, reason: z.string() }))
1011
- .output(z.object({ success: z.boolean() })),
1012
- },
1013
- roles: {
1014
- list: oc
1015
- .input(z.object({ branchId: uuidSchema }))
1016
- .output(
1017
- z.array(
1018
- z.object({
1019
- id: uuidSchema,
1020
- name: z.string(),
1021
- isSystem: z.boolean(),
1022
- permissionCount: z.number(),
1023
- }),
1024
- ),
1025
- ),
1026
- create: oc
1027
- .input(
1028
- z.object({
1029
- name: z.string(),
1030
- description: z.string().optional(),
1031
- permissionIds: z.array(uuidSchema),
1032
- }),
1033
- )
1034
- .output(z.object({ id: uuidSchema })),
1035
- },
1036
- };
1037
-
1038
- // ─── Storage Router ───────────────────────────────────────────────────────────
1039
-
1040
- export const storageContract = {
1041
- presignUpload: oc
1042
- .input(
1043
- z.object({
1044
- fileName: z.string(),
1045
- mimeType: z.string(),
1046
- category: z.enum([
1047
- "student_photo",
1048
- "staff_photo",
1049
- "document",
1050
- "payslip",
1051
- "report_card",
1052
- "asset_photo",
1053
- "notice_attachment",
1054
- ]),
1055
- resourceId: uuidSchema.optional(),
1056
- }),
1057
- )
1058
- .output(
1059
- z.object({
1060
- uploadUrl: z.string(),
1061
- fileKey: z.string(),
1062
- expiresIn: z.number(),
1063
- }),
1064
- ),
1065
-
1066
- confirmUpload: oc
1067
- .input(
1068
- z.object({
1069
- fileKey: z.string(),
1070
- resourceId: uuidSchema,
1071
- resourceType: z.string(),
1072
- }),
1073
- )
1074
- .output(z.object({ publicUrl: z.string() })),
1075
- };
1076
-
1077
- // ─── Root Contract (flat router type for client inference) ───────────────────
1078
-
1079
- export const appContract = {
1080
- auth: authContract,
1081
- branches: branchesContract,
1082
- students: studentsContract,
1083
- academics: academicsContract,
1084
- finance: financeContract,
1085
- hr: hrContract,
1086
- payroll: payrollContract,
1087
- transport: transportContract,
1088
- assets: assetsContract,
1089
- library: libraryContract,
1090
- discipline: disciplineContract,
1091
- comms: commsContract,
1092
- admin: adminContract,
1093
- storage: storageContract,
1094
- };
1095
-
1096
- export type AppContract = typeof appContract;