@updraft-solutions/mcrit-sdk 0.14.16 → 0.14.18

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.
@@ -89,7 +89,11 @@ export type ImpersonationState = z.infer<typeof impersonationStateSchema>;
89
89
  export const userSchema = z.object({
90
90
  type: z.literal("user"),
91
91
  id: z.number(),
92
- name: z.string(),
92
+ // name/firstname/lastname are gate-guarded server-side exactly like
93
+ // email/mobile below (a `viewName` policy decision, driven by the member's
94
+ // own `name.visibility` setting) and go null TOGETHER when denied. Render
95
+ // a placeholder via a display-name helper — never a bare `user.name`.
96
+ name: z.string().nullable(),
93
97
  firstname: z.string().nullable().optional(),
94
98
  lastname: z.string().nullable().optional(),
95
99
  // email/mobile are gate-guarded server-side (viewMail/viewMobile) and come
@@ -7,12 +7,17 @@ export enum FaqStatus {
7
7
  REJECTED = "rejected",
8
8
  }
9
9
 
10
- // User schema for submitter/reviewer
10
+ // User schema for submitter/reviewer.
11
+ //
12
+ // `name` and `email` are both gate-guarded server-side (`UserPolicy::viewName`
13
+ // / `viewMail`) and come back null for a viewer the member hid them from —
14
+ // a company member without `membership.viewMail` sees the submitter's name
15
+ // but no address. Render a placeholder, never a bare `.email`.
11
16
  const UserBasicSchema = z
12
17
  .object({
13
18
  id: z.number(),
14
- name: z.string(),
15
- email: z.string().email(),
19
+ name: z.string().nullable(),
20
+ email: z.string().email().nullable(),
16
21
  })
17
22
  .nullable();
18
23
 
@@ -149,8 +149,19 @@ export const fboFlightSchema = z.object({
149
149
  landings: z.number().nullable(),
150
150
  remarks: z.string().nullable(),
151
151
 
152
- // Null on an assigned company's booking rendered as a movement (see `source`).
153
- contact: zContactDTO.nullable(),
152
+ /**
153
+ * The filer's contact block. Own-row tier since the 2026-08 hardening: the
154
+ * key is OMITTED for a reader who is neither the movement's author, a root
155
+ * decider, nor in `assigned_user_ids` — a tenant colleague browsing a
156
+ * movement they did not file no longer receives it. Still explicitly `null`
157
+ * on an assigned company's booking rendered as a movement (see `source`),
158
+ * so BOTH `undefined` and `null` occur and consumers must use `contact?.…`.
159
+ *
160
+ * A client that backfills an empty block to bind a form to must key that on
161
+ * the KEY BEING PRESENT — `UpdateFlightRequest` accepts `contact` from any
162
+ * updater, so sending a seeded blank would erase the author's data.
163
+ */
164
+ contact: zContactDTO.nullable().optional(),
154
165
  ground_services: fboGroundServicesSchema.nullable(),
155
166
  acceptance_status: fboAcceptanceStatusSchema,
156
167
 
@@ -27,7 +27,13 @@ export const membershipIdentitySchema = z.object({
27
27
  user_id: z.number(),
28
28
  user: userSchema.optional(),
29
29
  company_id: z.number(),
30
- name: z.string(),
30
+ // The member's display name, lifted off the user row — and gate-guarded on
31
+ // the way, exactly like `userSchema.name`: `MembershipResource` and
32
+ // `MembershipIdentityResource` both run it through `UserPolicy::viewName`
33
+ // (driven by the member's own `name.visibility` setting, default `*`) and
34
+ // send `null` when denied. Render a placeholder, never a bare
35
+ // `membership.name`.
36
+ name: z.string().nullable(),
31
37
  });
32
38
 
33
39
  /**
@@ -310,6 +316,28 @@ export const airplanePermissionSchema = z.object({
310
316
  .optional(),
311
317
  });
312
318
 
319
+ export const teachingPermissionSchema = z.object({
320
+ type: z.literal("teaching_permission"),
321
+ id: z.number(),
322
+ course_id: z.number(),
323
+ course: z.object({ id: z.number(), name: z.string() }).passthrough().optional(),
324
+ membership_id: z.number(),
325
+ membership: nestedMembershipSchema.optional(),
326
+ status: z.enum(["pending", "approved"]),
327
+ is_active: z.boolean(),
328
+ expires_at: z.string().nullable().optional(),
329
+ notes: z.string().nullable().optional(),
330
+ conditions: z.string().nullable().optional(),
331
+ requested_by: userSchema.nullable().optional(),
332
+ approved_by: userSchema.nullable().optional(),
333
+ approved_at: z.string().nullable().optional(),
334
+ created_at: z.string(),
335
+ updated_at: z.string(),
336
+ can: z
337
+ .object({ view: zNullableBool, update: zNullableBool, delete: zNullableBool, approve: zNullableBool })
338
+ .optional(),
339
+ });
340
+
313
341
  export const landingFeeSchema = z.object({
314
342
  id: z.number(),
315
343
  airplane_id: z.number(),
@@ -12,6 +12,12 @@ export interface ComplianceRequirement {
12
12
  // isn't the blocker — consumers refine this into an `awaiting_countersign`
13
13
  // display state.
14
14
  awaiting_countersignature?: boolean;
15
+ // Whether the displayed submission is visible to the requesting company:
16
+ // it resolves to that company (company template / membership-issued, e.g.
17
+ // the Chartervertrag) or the member explicitly shared their user-owned
18
+ // submission. Stamped by the backend for User targets; absent for other
19
+ // target types.
20
+ shared?: boolean;
15
21
  // The submission to display for this requirement: the satisfying (final,
16
22
  // non-expired) one when present, otherwise the latest expired /
17
23
  // awaiting-signatures submission so the UI can explain *why* an unmet
@@ -22,6 +22,9 @@ export type ExtendedAirplane = z.infer<
22
22
  export type AirplanePermission = z.infer<
23
23
  typeof ModelSchemas.airplanePermissionSchema
24
24
  >;
25
+ export type TeachingPermission = z.infer<
26
+ typeof ModelSchemas.teachingPermissionSchema
27
+ >;
25
28
  export type AirplaneShareInvite = z.infer<
26
29
  typeof ModelSchemas.airplaneShareInviteSchema
27
30
  >;