@updraft-solutions/mcrit-sdk 0.9.0 → 0.11.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.
Files changed (44) hide show
  1. package/README.md +11 -25
  2. package/dist/{chunk-3WKOQDFN.cjs → chunk-RN7XMRGC.cjs} +37 -25
  3. package/dist/{chunk-NYDIYVBI.js → chunk-XEKAC57V.js} +37 -25
  4. package/dist/crew/index.d.cts +1 -1
  5. package/dist/crew/index.d.ts +1 -1
  6. package/dist/index.cjs +5 -3
  7. package/dist/index.d.cts +3 -3
  8. package/dist/index.d.ts +3 -3
  9. package/dist/index.js +8 -6
  10. package/dist/media/index.d.cts +2 -2
  11. package/dist/media/index.d.ts +2 -2
  12. package/dist/money/index.d.cts +5 -5
  13. package/dist/money/index.d.ts +5 -5
  14. package/dist/sanitize/index.cjs +53 -0
  15. package/dist/sanitize/index.d.cts +50 -0
  16. package/dist/sanitize/index.d.ts +50 -0
  17. package/dist/sanitize/index.js +53 -0
  18. package/dist/schemas/index.cjs +4 -2
  19. package/dist/schemas/index.d.cts +16 -17
  20. package/dist/schemas/index.d.ts +16 -17
  21. package/dist/schemas/index.js +7 -5
  22. package/dist/{training-nljZCLW5.d.cts → training-CuiDqGAo.d.cts} +944 -858
  23. package/dist/{training-nljZCLW5.d.ts → training-CuiDqGAo.d.ts} +944 -858
  24. package/dist/types/index.cjs +1 -1
  25. package/dist/types/index.d.cts +153 -2
  26. package/dist/types/index.d.ts +153 -2
  27. package/dist/types/index.js +1 -1
  28. package/package.json +14 -2
  29. package/src/crew/index.ts +1 -1
  30. package/src/index.ts +18 -0
  31. package/src/media/index.ts +2 -2
  32. package/src/money/index.ts +4 -4
  33. package/src/sanitize/index.ts +92 -0
  34. package/src/schemas/common.ts +1 -1
  35. package/src/schemas/equipment.ts +1 -2
  36. package/src/schemas/form-template.ts +1 -1
  37. package/src/schemas/models.ts +46 -30
  38. package/src/schemas/training.ts +2 -2
  39. package/src/types/compliance.ts +1 -1
  40. package/src/types/index.ts +1 -0
  41. package/src/types/models.ts +18 -0
  42. package/src/types/operating-hours.ts +163 -0
  43. /package/dist/{chunk-2WJHTRIE.js → chunk-UV5JLKAL.js} +0 -0
  44. /package/dist/{chunk-DCEOET63.cjs → chunk-YVC6Q3YU.cjs} +0 -0
@@ -9,7 +9,7 @@ import {
9
9
  zContactDTO,
10
10
  zNullableBool,
11
11
  zNetGross,
12
- zPhpMoneyObject,
12
+ zMoneyObject,
13
13
  } from "./common";
14
14
  import { equipmentSchema } from "./equipment";
15
15
  import { courseCheckSyllabusSchema } from "./training";
@@ -75,8 +75,8 @@ export const documentType = z.object({
75
75
 
76
76
  export const priceSchema = z.object({
77
77
  id: z.number(),
78
- gross_total: zPhpMoneyObject,
79
- net_total: zPhpMoneyObject,
78
+ gross_total: zMoneyObject,
79
+ net_total: zMoneyObject,
80
80
  tax_percentage: z.number(),
81
81
  valid_from: z.string(),
82
82
  created_at: z.string(),
@@ -446,7 +446,7 @@ export const bookingWaypointSchema = z.object({
446
446
  });
447
447
 
448
448
  /**
449
- * Spatie activity-log row (ActivityResource) — audit trail entries loaded on
449
+ * Activity-log row (ActivityResource) — audit trail entries loaded on
450
450
  * booking/invoice/event show payloads (`activities` relation).
451
451
  */
452
452
  export const activitySchema = z.object({
@@ -736,7 +736,7 @@ export const allowedDocumentTypeSchema = z.object({
736
736
  });
737
737
 
738
738
  // Billing recipient (formerly "Address"; the `addresses` table was renamed
739
- // to `billing_profiles` — see BillingProfileResource.php). A membership/user
739
+ // to `billing_profiles` — see BillingProfileResource). A membership/user
740
740
  // can own several profiles (e.g. private vs. "Firma"); invoices, flight logs
741
741
  // and expense items each point at one via `billing_profile_id`.
742
742
  export const billingProfileSchema = z.object({
@@ -821,8 +821,8 @@ export const flightLogSchema = z.object({
821
821
  arr_airport: airportSchema.optional(),
822
822
  from: z.string().nullable().optional(),
823
823
  to: z.string().nullable().optional(),
824
- costs: zPhpMoneyObject.nullable().optional(),
825
- charge: zPhpMoneyObject.nullable().optional(),
824
+ costs: zMoneyObject.nullable().optional(),
825
+ charge: zMoneyObject.nullable().optional(),
826
826
  remarks: z.string().nullable().optional(),
827
827
  created_at: z.string(),
828
828
  updated_at: z.string(),
@@ -840,7 +840,7 @@ export const invoiceSchema = z.object({
840
840
  title: z.string(),
841
841
  membership_id: z.number(),
842
842
  membership: baseMembershipSchema.optional(),
843
- // InvoiceResource.php always emits `billing_profile_id` (non-optional key,
843
+ // InvoiceResource always emits `billing_profile_id` (non-optional key,
844
844
  // nullable FK value). `billingProfile` is a `whenLoaded('billingProfile')`
845
845
  // relation, so it stays optional. (Fix round 1: as of the original Task 16
846
846
  // commit, InvoiceResource hadn't gained `billing_profile_id` yet — it was
@@ -856,10 +856,10 @@ export const invoiceSchema = z.object({
856
856
  due_date: z.string().nullable().optional(),
857
857
  status: z.string().nullable().optional(),
858
858
  paid_status: z.string().nullable().optional(),
859
- total: zPhpMoneyObject.nullable().optional(),
860
- subtotal: zPhpMoneyObject.nullable().optional(),
861
- tax_amount: zPhpMoneyObject.nullable().optional(),
862
- discount_amount: zPhpMoneyObject.nullable().optional(),
859
+ total: zMoneyObject.nullable().optional(),
860
+ subtotal: zMoneyObject.nullable().optional(),
861
+ tax_amount: zMoneyObject.nullable().optional(),
862
+ discount_amount: zMoneyObject.nullable().optional(),
863
863
  paid_at: z.string().nullable().optional(),
864
864
  released_at: z.string().nullable().optional(),
865
865
  sent: z.number().default(0),
@@ -892,8 +892,8 @@ export const invoiceSchema = z.object({
892
892
  issuer_vat_id: z.string().nullable().optional(),
893
893
  issuer_register_court: z.string().nullable().optional(),
894
894
  issuer_register_number: z.string().nullable().optional(),
895
- gross_total: zPhpMoneyObject.nullable().optional(),
896
- net_total: zPhpMoneyObject.nullable().optional(),
895
+ gross_total: zMoneyObject.nullable().optional(),
896
+ net_total: zMoneyObject.nullable().optional(),
897
897
  // InvoiceResource media envelope: only the generated PDF.
898
898
  media: z
899
899
  .object({
@@ -945,7 +945,7 @@ export const expenseSchema = z.object({
945
945
  membership: baseMembershipSchema.optional(),
946
946
  title: z.string(),
947
947
  description: z.string().nullable().optional(),
948
- amount: zPhpMoneyObject,
948
+ amount: zMoneyObject,
949
949
  status: z.string().nullable().optional(),
950
950
  expense_date: z.string().nullable().optional(),
951
951
  invoice_id: z.number().nullable().optional(),
@@ -960,8 +960,7 @@ export const expenseSchema = z.object({
960
960
  .optional(),
961
961
  });
962
962
 
963
- // Company-scoped tag (shared pool across taggable models — see
964
- // m-crit-api docs/features/tags.md).
963
+ // Company-scoped tag (shared pool across taggable models).
965
964
  export const tagSchema = z.object({
966
965
  type: z.literal("tag").optional(),
967
966
  id: z.number(),
@@ -979,7 +978,7 @@ export const courseSchema = z.object({
979
978
  start_date: z.string().nullable().optional(),
980
979
  end_date: z.string().nullable().optional(),
981
980
  max_participants: z.number().nullable().optional(),
982
- price: zPhpMoneyObject.nullable().optional(),
981
+ price: zMoneyObject.nullable().optional(),
983
982
  instructor_id: z.number().nullable().optional(),
984
983
  instructor: userSchema.optional(),
985
984
  aircraft_required: z.boolean().default(false),
@@ -1016,7 +1015,7 @@ export const courseSchema = z.object({
1016
1015
 
1017
1016
  /**
1018
1017
  * Materialized, admin-only metrics for an enrollment — recomputed server-side
1019
- * (see m-crit-api course_enrollment_metrics). `flags` is an extensible bag of
1018
+ * (see the course_enrollment_metrics snapshot). `flags` is an extensible bag of
1020
1019
  * computed booleans (e.g. `has_solo`).
1021
1020
  */
1022
1021
  export const courseEnrollmentMetricsSchema = z.object({
@@ -1044,8 +1043,7 @@ export const courseEnrollmentSchema = z.object({
1044
1043
  comments: z.string().nullable().optional(),
1045
1044
  initial_payment_generated: z.boolean().optional(),
1046
1045
  initial_payment_removable: z.boolean().optional(),
1047
- // Element-driven training progress (see m-crit-api
1048
- // docs/features/training-tracking.md).
1046
+ // Element-driven training progress.
1049
1047
  tracked_training_elements_count: z.number().optional(),
1050
1048
  progress_source: z.enum(["training_elements", "milestones"]).optional(),
1051
1049
  metrics: courseEnrollmentMetricsSchema.optional(),
@@ -1151,7 +1149,7 @@ export const landingSchema = z.object({
1151
1149
  airport: airportSchema.optional(),
1152
1150
  landing_fee_id: z.number().nullable().optional(),
1153
1151
  landing_time: z.string(),
1154
- price: zPhpMoneyObject.nullable().optional(),
1152
+ price: zMoneyObject.nullable().optional(),
1155
1153
  created_at: z.string(),
1156
1154
  updated_at: z.string(),
1157
1155
  });
@@ -1209,24 +1207,42 @@ export const settingSchema = z.object({
1209
1207
  updated_at: z.string(),
1210
1208
  });
1211
1209
 
1210
+ /**
1211
+ * The model a post is *about* (its optional subject). Scope is always the
1212
+ * company; the subject only adds a second listing place (the model's own
1213
+ * page). `label` is display-ready (registration / course name).
1214
+ */
1215
+ export const postSubjectSchema = z.object({
1216
+ type: z.enum(["airplane", "course"]),
1217
+ id: z.number(),
1218
+ label: z.string(),
1219
+ });
1220
+
1212
1221
  export const postSchema = z.object({
1213
1222
  type: z.literal("post"),
1214
1223
  id: z.number(),
1215
1224
  company_id: z.number(),
1216
1225
  postable_type: z.string().nullable().optional(),
1217
1226
  postable_id: z.number().nullable().optional(),
1227
+ postable: postSubjectSchema.nullable().optional(),
1218
1228
  author_id: z.number(),
1219
1229
  author: userSchema.optional(),
1220
1230
  title: z.string().nullable().optional(),
1221
1231
  content: z.string(),
1222
1232
  sticky: z.boolean().default(false),
1223
1233
  public: z.boolean().default(false),
1234
+ comments_disabled: z.boolean().default(false),
1235
+ /** Present when the endpoint counts comments (listings + show). */
1236
+ comments_count: z.number().nullable().optional(),
1237
+ tags: z.array(tagSchema).optional(),
1224
1238
  media: mediaCollection.optional(),
1225
1239
  created_at: z.string(),
1226
1240
  updated_at: z.string(),
1227
1241
  can: z
1228
1242
  .object({
1229
1243
  view: zNullableBool,
1244
+ viewComments: zNullableBool,
1245
+ createComments: zNullableBool,
1230
1246
  update: zNullableBool,
1231
1247
  delete: zNullableBool,
1232
1248
  setFlags: zNullableBool,
@@ -1321,8 +1337,8 @@ export const prePaidPackageSchema = z.object({
1321
1337
  company: companySchema.optional(),
1322
1338
  name: z.string(),
1323
1339
  description: z.string().nullable().optional(),
1324
- balance: zPhpMoneyObject,
1325
- initial_amount: zPhpMoneyObject,
1340
+ balance: zMoneyObject,
1341
+ initial_amount: zMoneyObject,
1326
1342
  discount_percentage: z.number().nullable().optional(),
1327
1343
  valid_until: z.string().nullable().optional(),
1328
1344
  created_at: z.string(),
@@ -1339,7 +1355,7 @@ export const prePaidPackageSchema = z.object({
1339
1355
  // A billing line-item (flight charge, landing fee, membership fee, refund,
1340
1356
  // …) attached to a flight log / booking / membership via the polymorphic
1341
1357
  // (model_type, model_id) pair, and to an invoice once released. NOT a
1342
- // catalog entry — rebuilt field-by-field against ExpenseItemResource.php;
1358
+ // catalog entry — rebuilt field-by-field against ExpenseItemResource;
1343
1359
  // the previous shape (name/category/active) modeled a company catalog row,
1344
1360
  // which ExpenseItem has never had.
1345
1361
  export const prePaidPackageAssignmentSchema = z.object({
@@ -1347,7 +1363,7 @@ export const prePaidPackageAssignmentSchema = z.object({
1347
1363
  flight_log_id: z.number().nullable().optional(),
1348
1364
  expense_item_id: z.number().nullable().optional(),
1349
1365
  pre_paid_package_id: z.number().nullable().optional(),
1350
- gross_total: zPhpMoneyObject.nullable().optional(),
1366
+ gross_total: zMoneyObject.nullable().optional(),
1351
1367
  created_at: z.string().nullable().optional(),
1352
1368
  updated_at: z.string().nullable().optional(),
1353
1369
  });
@@ -1382,10 +1398,10 @@ export const expenseItemSchema = z.object({
1382
1398
  membership_id: z.number(),
1383
1399
  billing_profile_id: z.number().nullable(),
1384
1400
 
1385
- gross_total_before_vouchers: zPhpMoneyObject,
1386
- gross_total: zPhpMoneyObject,
1387
- net_total: zPhpMoneyObject,
1388
- tax_total: zPhpMoneyObject,
1401
+ gross_total_before_vouchers: zMoneyObject,
1402
+ gross_total: zMoneyObject,
1403
+ net_total: zMoneyObject,
1404
+ tax_total: zMoneyObject,
1389
1405
  // number_format(…, 2) string, not a number.
1390
1406
  tax_percentage: z.string(),
1391
1407
  billable: z.boolean(),
@@ -359,7 +359,7 @@ export type TrainingRecordEntryInput = z.infer<
359
359
  typeof trainingRecordEntryInputSchema
360
360
  >;
361
361
 
362
- // Mirrors the generic `SignatureResource` (App\Http\Resources\Form) — the
362
+ // Mirrors the generic `SignatureResource` — the
363
363
  // same polymorphic `Signature` model backs both form submissions and
364
364
  // training records. Kept self-contained here (not imported from
365
365
  // @schemas/FormTemplateSchema) to match this file's own alias root and the
@@ -504,7 +504,7 @@ export type CreateSyllabusFromTemplateForm = z.infer<
504
504
  typeof CreateSyllabusFromTemplateFormSchema
505
505
  >;
506
506
 
507
- // Mirrors `CreateSyllabusFromTemplate::run()`'s return shape (m-crit-api),
507
+ // Mirrors `CreateSyllabusFromTemplate::run()`'s return shape,
508
508
  // as wrapped by `SyllabusController::fromTemplate`.
509
509
  export const createSyllabusFromTemplateResultSchema = z.object({
510
510
  catalog: z.object({
@@ -7,7 +7,7 @@ export interface ComplianceRequirement {
7
7
  logical_group: string;
8
8
  is_satisfied: boolean;
9
9
  // True when the member has already signed the satisfying submission and
10
- // only the company's counter-signature is outstanding (e.g. the INflight
10
+ // only the company's counter-signature is outstanding (e.g. the
11
11
  // Chartervertrag). The requirement is still unsatisfied, but the member
12
12
  // isn't the blocker — consumers refine this into an `awaiting_countersign`
13
13
  // display state.
@@ -6,3 +6,4 @@ export * from "./background-task";
6
6
  export * from "./ai";
7
7
  export * from "./roles";
8
8
  export * from "./landing-fee";
9
+ export * from "./operating-hours";
@@ -128,12 +128,28 @@ export type AirplaneIssueStatus = z.infer<
128
128
  export type Event = z.infer<typeof ModelSchemas.eventSchema>;
129
129
  export type Ticket = z.infer<typeof ModelSchemas.ticketSchema>;
130
130
  export type Post = z.infer<typeof ModelSchemas.postSchema>;
131
+ export type PostSubject = z.infer<typeof ModelSchemas.postSubjectSchema>;
132
+
133
+ /**
134
+ * Full-replacement tag sync entry — `id` marks an existing tag (wins over
135
+ * name matching), fresh tags carry only name (+ optional color) and are
136
+ * created server-side on save.
137
+ */
138
+ export interface TagSyncEntry {
139
+ id?: number | null;
140
+ name: string;
141
+ color?: string | null;
142
+ }
131
143
 
132
144
  export interface CreatePostInput {
133
145
  title?: string | null;
134
146
  content: string;
135
147
  sticky?: boolean;
136
148
  public?: boolean;
149
+ comments_disabled?: boolean;
150
+ tags?: TagSyncEntry[];
151
+ /** Action flag, not persisted: notify company members on publish. */
152
+ notify?: boolean;
137
153
  }
138
154
 
139
155
  export interface UpdatePostInput {
@@ -141,6 +157,8 @@ export interface UpdatePostInput {
141
157
  content: string;
142
158
  sticky?: boolean;
143
159
  public?: boolean;
160
+ comments_disabled?: boolean;
161
+ tags?: TagSyncEntry[];
144
162
  }
145
163
 
146
164
  export interface CreateAirplaneIssueInput {
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Operating hours — an organization defines several independent groups (its
3
+ * own office hours, a military field it flies from, the tower, an FBO). Each
4
+ * group is self-contained (own IANA timezone + optional airport) and is
5
+ * materialised server-side into merged, half-open UTC windows; status is
6
+ * answered per group as open / partial / closed / unknown.
7
+ */
8
+
9
+ export type OperatingHourKind =
10
+ | "company" | "airport" | "atc" | "fbo" | "military"
11
+ | "fuel" | "customs" | "handling" | "maintenance" | "other";
12
+
13
+ export type TimeAnchor = "fixed" | "sunrise" | "sunset";
14
+
15
+ export type OperatingStatusValue = "open" | "partial" | "closed" | "unknown";
16
+
17
+ /**
18
+ * One open block. Each bound is a fixed local time, or sunrise/sunset with a
19
+ * signed offset and an optional cap ("SR-30 aber nicht vor 07:00"). An end at
20
+ * or before its start crosses midnight.
21
+ */
22
+ export type OperatingHourInterval = {
23
+ id?: number;
24
+ sort: number;
25
+ start_kind: TimeAnchor;
26
+ start_time: string | null;
27
+ start_offset_minutes: number | null;
28
+ start_not_before: string | null;
29
+ end_kind: TimeAnchor;
30
+ end_time: string | null;
31
+ end_offset_minutes: number | null;
32
+ end_not_after: string | null;
33
+ };
34
+
35
+ /** One weekday. ISO: 1 = Monday .. 7 = Sunday. */
36
+ export type OperatingHourDay = {
37
+ id?: number;
38
+ schedule_id?: number;
39
+ day_of_week: number;
40
+ is_closed: boolean;
41
+ is_24_hours: boolean;
42
+ notes: string | null;
43
+ intervals: OperatingHourInterval[];
44
+ };
45
+
46
+ /** A hand-entered override for a date range. No recurrence, by design. */
47
+ export type OperatingHourException = {
48
+ id: number;
49
+ schedule_id: number;
50
+ starts_on: string;
51
+ ends_on: string;
52
+ is_closed: boolean;
53
+ is_24_hours: boolean;
54
+ reason: string;
55
+ intervals: OperatingHourInterval[];
56
+ };
57
+
58
+ /**
59
+ * What the client composes before an exception exists — no id, no owner yet.
60
+ * The read type above is what comes back; keeping the two apart lets the
61
+ * dialog's `createItem` seed a blank without lying about an id.
62
+ */
63
+ export type OperatingHourExceptionInput = Omit<
64
+ OperatingHourException,
65
+ "id" | "schedule_id"
66
+ >;
67
+
68
+ /** A concrete, merged, half-open UTC window. Derived — read-only. */
69
+ export type OperatingHourWindow = {
70
+ starts_at: string;
71
+ ends_at: string;
72
+ };
73
+
74
+ /**
75
+ * A group. Self-contained: carries its own timezone and airport, resolved
76
+ * from nowhere else. `materialized_from`/`materialized_through` bound the
77
+ * range in which "no window" genuinely means closed rather than unknown.
78
+ */
79
+ export type OperatingHourSchedule = {
80
+ id: number;
81
+ company_id: number;
82
+ airport_id: number | null;
83
+ airport?: { id: number; ident: string; name: string } | null;
84
+ kind: OperatingHourKind;
85
+ name: string;
86
+ description: string | null;
87
+ timezone: string;
88
+ valid_from: string;
89
+ valid_until: string | null;
90
+ is_active: boolean;
91
+ sort_order: number;
92
+ source: string | null;
93
+ verified_at: string | null;
94
+ materialized_from: string | null;
95
+ materialized_through: string | null;
96
+ days: OperatingHourDay[];
97
+ exceptions: OperatingHourException[];
98
+ created_at?: string;
99
+ updated_at?: string;
100
+ };
101
+
102
+ /** The fields a client may send when creating a group. */
103
+ export type OperatingHourScheduleInput = Partial<
104
+ Omit<
105
+ OperatingHourSchedule,
106
+ | "id"
107
+ | "company_id"
108
+ | "airport"
109
+ | "materialized_from"
110
+ | "materialized_through"
111
+ | "days"
112
+ | "exceptions"
113
+ | "created_at"
114
+ | "updated_at"
115
+ >
116
+ >;
117
+
118
+ /**
119
+ * What the saved rules resolve to on ONE named calendar date — used for the
120
+ * solstice hints. Carries its own date and status precisely so the extremes
121
+ * can never be merged into a weekday bucket alongside the current week, and
122
+ * so an `unknown` date is reported as unknown rather than as closed.
123
+ */
124
+ export type OperatingHourResolvedDate = {
125
+ /** `Y-m-d` in the group's timezone. */
126
+ date: string;
127
+ status: OperatingStatusValue;
128
+ windows: OperatingHourWindow[];
129
+ };
130
+
131
+ export type OperatingHourStatusEntry = {
132
+ schedule_id: number;
133
+ kind: OperatingHourKind;
134
+ name: string;
135
+ timezone: string;
136
+ status: OperatingStatusValue;
137
+ windows: OperatingHourWindow[];
138
+ };
139
+
140
+ export type OperatingHoursStatusResponse = {
141
+ from: string;
142
+ to: string;
143
+ data: OperatingHourStatusEntry[];
144
+ };
145
+
146
+ export type OperatingHoursNextOpenEntry = {
147
+ schedule_id: number;
148
+ kind: OperatingHourKind;
149
+ name: string;
150
+ next_opening: string | null;
151
+ };
152
+
153
+ export type OperatingHoursNextOpenResponse = {
154
+ after: string;
155
+ data: OperatingHoursNextOpenEntry[];
156
+ };
157
+
158
+ export type OperatingHoursMeta = {
159
+ kinds: OperatingHourKind[];
160
+ anchors: TimeAnchor[];
161
+ statuses: OperatingStatusValue[];
162
+ max_status_range_days: number;
163
+ };
File without changes
File without changes