gymmonk-schema 0.49.0 → 0.50.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.
@@ -99,6 +99,35 @@ export declare const membershipRequestQuerySchema: z.ZodObject<{
99
99
  }>>;
100
100
  }, z.core.$strip>;
101
101
  export type MembershipRequestQuery = z.infer<typeof membershipRequestQuerySchema>;
102
+ /**
103
+ * Where ONE request stands — the answer a notification card redraws itself from.
104
+ *
105
+ * ── Why this exists as its own reading ──────────────────────────────────────
106
+ * A join request can be answered from the notification feed, from the requests
107
+ * inbox, or by a different member of staff on a different phone. Whichever
108
+ * happens, every OTHER surface showing that request is now stale — and the one
109
+ * showing Accept / Decline buttons is the dangerous kind of stale, because the
110
+ * buttons still look live and the second tap fails.
111
+ *
112
+ * A card that tracks its own decision in component state only knows about taps
113
+ * made in THAT session: reload the feed, or navigate away and back, and the
114
+ * buttons return on a request that was settled days ago. This is the
115
+ * server-side truth that stops that, so a settled request reads as settled
116
+ * everywhere, forever, no matter who settled it.
117
+ *
118
+ * Deliberately just the decision. The notification already carries the member's
119
+ * name and avatar, so this must not become a second, thinner way to read a
120
+ * person's details.
121
+ */
122
+ export declare const membershipRequestStatusResultSchema: z.ZodObject<{
123
+ status: z.ZodEnum<{
124
+ pending: "pending";
125
+ rejected: "rejected";
126
+ approved: "approved";
127
+ }>;
128
+ decidedAt: z.ZodNullable<z.ZodISODateTime>;
129
+ }, z.core.$strip>;
130
+ export type MembershipRequestStatusResult = z.infer<typeof membershipRequestStatusResultSchema>;
102
131
  /**
103
132
  * What the member's own surfaces need: their standing at the gym they picked.
104
133
  * `null` when they have never requested one.
@@ -1 +1 @@
1
- {"version":3,"file":"membership-request.d.ts","sourceRoot":"","sources":["../src/membership-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,eAAO,MAAM,6BAA6B;;;;EAA8C,CAAC;AACzF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAIpF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;iBAYlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;GAGG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;iBAKtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAIhF;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC;;iBAE5C,CAAC;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAE5F,0EAA0E;AAC1E,eAAO,MAAM,iCAAiC;;iBAE5C,CAAC;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAE5F,eAAO,MAAM,4BAA4B;;;;;;;;iBAEvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF;;;GAGG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;kBAEzB,CAAC;AACd,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
1
+ {"version":3,"file":"membership-request.d.ts","sourceRoot":"","sources":["../src/membership-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,eAAO,MAAM,6BAA6B;;;;EAA8C,CAAC;AACzF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAIpF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;iBAYlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;GAGG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;iBAKtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAIhF;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC;;iBAE5C,CAAC;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAE5F,0EAA0E;AAC1E,eAAO,MAAM,iCAAiC;;iBAE5C,CAAC;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAE5F,eAAO,MAAM,4BAA4B;;;;;;;;iBAEvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,mCAAmC;;;;;;;iBAI9C,CAAC;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC,CAAC;AAEhG;;;GAGG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;kBAEzB,CAAC;AACd,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
@@ -75,6 +75,31 @@ export const decideMembershipRequestBodySchema = z.object({
75
75
  export const membershipRequestQuerySchema = paginationSchema.extend({
76
76
  status: membershipRequestStatusSchema.optional(),
77
77
  });
78
+ /**
79
+ * Where ONE request stands — the answer a notification card redraws itself from.
80
+ *
81
+ * ── Why this exists as its own reading ──────────────────────────────────────
82
+ * A join request can be answered from the notification feed, from the requests
83
+ * inbox, or by a different member of staff on a different phone. Whichever
84
+ * happens, every OTHER surface showing that request is now stale — and the one
85
+ * showing Accept / Decline buttons is the dangerous kind of stale, because the
86
+ * buttons still look live and the second tap fails.
87
+ *
88
+ * A card that tracks its own decision in component state only knows about taps
89
+ * made in THAT session: reload the feed, or navigate away and back, and the
90
+ * buttons return on a request that was settled days ago. This is the
91
+ * server-side truth that stops that, so a settled request reads as settled
92
+ * everywhere, forever, no matter who settled it.
93
+ *
94
+ * Deliberately just the decision. The notification already carries the member's
95
+ * name and avatar, so this must not become a second, thinner way to read a
96
+ * person's details.
97
+ */
98
+ export const membershipRequestStatusResultSchema = z.object({
99
+ status: membershipRequestStatusSchema,
100
+ /** When it was answered. Null while still pending. */
101
+ decidedAt: isoDateTimeSchema.nullable(),
102
+ });
78
103
  /**
79
104
  * What the member's own surfaces need: their standing at the gym they picked.
80
105
  * `null` when they have never requested one.
@@ -1 +1 @@
1
- {"version":3,"file":"membership-request.js","sourceRoot":"","sources":["../src/membership-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,gFAAgF;AAEhF,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC;AAGzF,gFAAgF;AAEhF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,EAAE,EAAE,cAAc;IAClB,MAAM,EAAE,cAAc;IACtB,QAAQ,EAAE,cAAc;IACxB,MAAM,EAAE,6BAA6B;IACrC,WAAW,EAAE,iBAAiB;IAC9B,0BAA0B;IAC1B,SAAS,EAAE,iBAAiB,CAAC,QAAQ,EAAE;IACvC,mEAAmE;IACnE,SAAS,EAAE,cAAc,CAAC,QAAQ,EAAE;IACpC,0DAA0D;IAC1D,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAC;AAGH;;;GAGG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,uBAAuB,CAAC,MAAM,CAAC;IACxE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC;AAGH,gFAAgF;AAEhF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC,MAAM,CAAC;IACxD,OAAO,EAAE,gBAAgB;CAC1B,CAAC,CAAC;AAGH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC,MAAM,CAAC;IACxD,IAAI,EAAE,qBAAqB,CAAC,GAAG,CAAC;CACjC,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,4BAA4B,GAAG,gBAAgB,CAAC,MAAM,CAAC;IAClE,MAAM,EAAE,6BAA6B,CAAC,QAAQ,EAAE;CACjD,CAAC,CAAC;AAGH;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,uBAAuB;KAC7D,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;KACvD,QAAQ,EAAE,CAAC","sourcesContent":["/**\n * gymmonk-schema — Membership join requests\n * ==========================================\n * A member asking to join a gym, and the gym's decision on it.\n *\n * WHY THIS EXISTS\n * A member picks a gym during onboarding, but picking is a CLAIM, not\n * membership. Until someone at the gym (owner, manager or front desk) accepts,\n * the person is not a member of that center and cannot check in. Without this\n * step anyone could self-serve into any gym's roster and start marking\n * attendance there.\n *\n * LIFECYCLE\n * member selects gym → `pending`\n * gym accepts → `approved` (member may now check in)\n * gym declines → `rejected` (member may request again, or elsewhere)\n * member cancels their plan → the approval is spent; a new request is needed\n *\n * A gym-CREATED member (the invite/claim path) never produces a request: the\n * gym made the record, so it has already decided.\n *\n * This is a LOG, not a flag: one row per request, so a cancel-and-rejoin cycle\n * leaves a trail and a rejection is not silently overwritten by the next\n * attempt. The member's CURRENT standing is the latest row for that center.\n *\n * @module gymmonk-schema/membership-request\n */\n\nimport { z } from 'zod';\nimport { codeLookupSchema } from './codes.js';\nimport {\n isoDateTimeSchema,\n objectIdSchema,\n optionalTrimmedString,\n paginationSchema,\n} from './common.js';\n\n// ─── Status ──────────────────────────────────────────────────────────────────\n\nexport const membershipRequestStatusSchema = z.enum(['pending', 'approved', 'rejected']);\nexport type MembershipRequestStatus = z.infer<typeof membershipRequestStatusSchema>;\n\n// ─── Entity ──────────────────────────────────────────────────────────────────\n\nexport const membershipRequestSchema = z.object({\n id: objectIdSchema,\n userId: objectIdSchema,\n centerId: objectIdSchema,\n status: membershipRequestStatusSchema,\n requestedAt: isoDateTimeSchema,\n /** Null while pending. */\n decidedAt: isoDateTimeSchema.nullable(),\n /** The staff user who accepted or declined. Null while pending. */\n decidedBy: objectIdSchema.nullable(),\n /** Optional reason shown to the member on a rejection. */\n note: z.string().nullable(),\n});\nexport type MembershipRequest = z.infer<typeof membershipRequestSchema>;\n\n/**\n * A row in the gym's requests inbox: the request plus enough of the person to\n * decide without opening each one.\n */\nexport const membershipRequestItemSchema = membershipRequestSchema.extend({\n name: z.string(),\n email: z.string(),\n phone: z.string().nullable(),\n avatarUrl: z.string().nullable(),\n});\nexport type MembershipRequestItem = z.infer<typeof membershipRequestItemSchema>;\n\n// ─── Requests ────────────────────────────────────────────────────────────────\n\n/**\n * The member asks to join. Identified by the gym's PUBLIC code (`IRN-1-7K2M`)\n * — that is what the member typed or picked during onboarding, and it keeps the\n * center's Mongo id out of a member-facing request.\n *\n * `codeLookupSchema` rather than `centerCodeSchema`: this normalises what the\n * member typed and lets the LOOKUP answer, so a code for a gym that has not\n * registered is refused with \"no gym found with that id\" instead of a regex\n * telling them they mistyped when they did not.\n */\nexport const createMembershipRequestBodySchema = z.object({\n gymCode: codeLookupSchema,\n});\nexport type CreateMembershipRequestBody = z.infer<typeof createMembershipRequestBodySchema>;\n\n/** Accept or decline. The note is surfaced to the member on a decline. */\nexport const decideMembershipRequestBodySchema = z.object({\n note: optionalTrimmedString(300),\n});\nexport type DecideMembershipRequestBody = z.infer<typeof decideMembershipRequestBodySchema>;\n\nexport const membershipRequestQuerySchema = paginationSchema.extend({\n status: membershipRequestStatusSchema.optional(),\n});\nexport type MembershipRequestQuery = z.infer<typeof membershipRequestQuerySchema>;\n\n/**\n * What the member's own surfaces need: their standing at the gym they picked.\n * `null` when they have never requested one.\n */\nexport const myMembershipRequestSchema = membershipRequestSchema\n .extend({ centerName: z.string(), gymCode: z.string() })\n .nullable();\nexport type MyMembershipRequest = z.infer<typeof myMembershipRequestSchema>;\n"]}
1
+ {"version":3,"file":"membership-request.js","sourceRoot":"","sources":["../src/membership-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,gFAAgF;AAEhF,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC;AAGzF,gFAAgF;AAEhF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,EAAE,EAAE,cAAc;IAClB,MAAM,EAAE,cAAc;IACtB,QAAQ,EAAE,cAAc;IACxB,MAAM,EAAE,6BAA6B;IACrC,WAAW,EAAE,iBAAiB;IAC9B,0BAA0B;IAC1B,SAAS,EAAE,iBAAiB,CAAC,QAAQ,EAAE;IACvC,mEAAmE;IACnE,SAAS,EAAE,cAAc,CAAC,QAAQ,EAAE;IACpC,0DAA0D;IAC1D,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAC;AAGH;;;GAGG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,uBAAuB,CAAC,MAAM,CAAC;IACxE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC;AAGH,gFAAgF;AAEhF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC,MAAM,CAAC;IACxD,OAAO,EAAE,gBAAgB;CAC1B,CAAC,CAAC;AAGH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC,MAAM,CAAC;IACxD,IAAI,EAAE,qBAAqB,CAAC,GAAG,CAAC;CACjC,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,4BAA4B,GAAG,gBAAgB,CAAC,MAAM,CAAC;IAClE,MAAM,EAAE,6BAA6B,CAAC,QAAQ,EAAE;CACjD,CAAC,CAAC;AAGH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1D,MAAM,EAAE,6BAA6B;IACrC,sDAAsD;IACtD,SAAS,EAAE,iBAAiB,CAAC,QAAQ,EAAE;CACxC,CAAC,CAAC;AAGH;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,uBAAuB;KAC7D,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;KACvD,QAAQ,EAAE,CAAC","sourcesContent":["/**\n * gymmonk-schema — Membership join requests\n * ==========================================\n * A member asking to join a gym, and the gym's decision on it.\n *\n * WHY THIS EXISTS\n * A member picks a gym during onboarding, but picking is a CLAIM, not\n * membership. Until someone at the gym (owner, manager or front desk) accepts,\n * the person is not a member of that center and cannot check in. Without this\n * step anyone could self-serve into any gym's roster and start marking\n * attendance there.\n *\n * LIFECYCLE\n * member selects gym → `pending`\n * gym accepts → `approved` (member may now check in)\n * gym declines → `rejected` (member may request again, or elsewhere)\n * member cancels their plan → the approval is spent; a new request is needed\n *\n * A gym-CREATED member (the invite/claim path) never produces a request: the\n * gym made the record, so it has already decided.\n *\n * This is a LOG, not a flag: one row per request, so a cancel-and-rejoin cycle\n * leaves a trail and a rejection is not silently overwritten by the next\n * attempt. The member's CURRENT standing is the latest row for that center.\n *\n * @module gymmonk-schema/membership-request\n */\n\nimport { z } from 'zod';\nimport { codeLookupSchema } from './codes.js';\nimport {\n isoDateTimeSchema,\n objectIdSchema,\n optionalTrimmedString,\n paginationSchema,\n} from './common.js';\n\n// ─── Status ──────────────────────────────────────────────────────────────────\n\nexport const membershipRequestStatusSchema = z.enum(['pending', 'approved', 'rejected']);\nexport type MembershipRequestStatus = z.infer<typeof membershipRequestStatusSchema>;\n\n// ─── Entity ──────────────────────────────────────────────────────────────────\n\nexport const membershipRequestSchema = z.object({\n id: objectIdSchema,\n userId: objectIdSchema,\n centerId: objectIdSchema,\n status: membershipRequestStatusSchema,\n requestedAt: isoDateTimeSchema,\n /** Null while pending. */\n decidedAt: isoDateTimeSchema.nullable(),\n /** The staff user who accepted or declined. Null while pending. */\n decidedBy: objectIdSchema.nullable(),\n /** Optional reason shown to the member on a rejection. */\n note: z.string().nullable(),\n});\nexport type MembershipRequest = z.infer<typeof membershipRequestSchema>;\n\n/**\n * A row in the gym's requests inbox: the request plus enough of the person to\n * decide without opening each one.\n */\nexport const membershipRequestItemSchema = membershipRequestSchema.extend({\n name: z.string(),\n email: z.string(),\n phone: z.string().nullable(),\n avatarUrl: z.string().nullable(),\n});\nexport type MembershipRequestItem = z.infer<typeof membershipRequestItemSchema>;\n\n// ─── Requests ────────────────────────────────────────────────────────────────\n\n/**\n * The member asks to join. Identified by the gym's PUBLIC code (`IRN-1-7K2M`)\n * — that is what the member typed or picked during onboarding, and it keeps the\n * center's Mongo id out of a member-facing request.\n *\n * `codeLookupSchema` rather than `centerCodeSchema`: this normalises what the\n * member typed and lets the LOOKUP answer, so a code for a gym that has not\n * registered is refused with \"no gym found with that id\" instead of a regex\n * telling them they mistyped when they did not.\n */\nexport const createMembershipRequestBodySchema = z.object({\n gymCode: codeLookupSchema,\n});\nexport type CreateMembershipRequestBody = z.infer<typeof createMembershipRequestBodySchema>;\n\n/** Accept or decline. The note is surfaced to the member on a decline. */\nexport const decideMembershipRequestBodySchema = z.object({\n note: optionalTrimmedString(300),\n});\nexport type DecideMembershipRequestBody = z.infer<typeof decideMembershipRequestBodySchema>;\n\nexport const membershipRequestQuerySchema = paginationSchema.extend({\n status: membershipRequestStatusSchema.optional(),\n});\nexport type MembershipRequestQuery = z.infer<typeof membershipRequestQuerySchema>;\n\n/**\n * Where ONE request stands — the answer a notification card redraws itself from.\n *\n * ── Why this exists as its own reading ──────────────────────────────────────\n * A join request can be answered from the notification feed, from the requests\n * inbox, or by a different member of staff on a different phone. Whichever\n * happens, every OTHER surface showing that request is now stale — and the one\n * showing Accept / Decline buttons is the dangerous kind of stale, because the\n * buttons still look live and the second tap fails.\n *\n * A card that tracks its own decision in component state only knows about taps\n * made in THAT session: reload the feed, or navigate away and back, and the\n * buttons return on a request that was settled days ago. This is the\n * server-side truth that stops that, so a settled request reads as settled\n * everywhere, forever, no matter who settled it.\n *\n * Deliberately just the decision. The notification already carries the member's\n * name and avatar, so this must not become a second, thinner way to read a\n * person's details.\n */\nexport const membershipRequestStatusResultSchema = z.object({\n status: membershipRequestStatusSchema,\n /** When it was answered. Null while still pending. */\n decidedAt: isoDateTimeSchema.nullable(),\n});\nexport type MembershipRequestStatusResult = z.infer<typeof membershipRequestStatusResultSchema>;\n\n/**\n * What the member's own surfaces need: their standing at the gym they picked.\n * `null` when they have never requested one.\n */\nexport const myMembershipRequestSchema = membershipRequestSchema\n .extend({ centerName: z.string(), gymCode: z.string() })\n .nullable();\nexport type MyMembershipRequest = z.infer<typeof myMembershipRequestSchema>;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gymmonk-schema",
3
- "version": "0.49.0",
3
+ "version": "0.50.0",
4
4
  "description": "Shared Zod schemas, enums and domain types for GymMonk (fitness SaaS) — single source of truth (SSOT) consumed by gymmonk-backend and gymmonk-web-client.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",