gymmonk-schema 0.54.0 → 0.55.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.
@@ -98,6 +98,50 @@ export declare const assignMembershipBodySchema: z.ZodObject<{
98
98
  }, z.core.$strip>>;
99
99
  }, z.core.$strip>;
100
100
  export type AssignMembershipBody = z.infer<typeof assignMembershipBodySchema>;
101
+ /**
102
+ * The member attaching their transfer screenshot to a request already open.
103
+ *
104
+ * Separate from `assignMembershipBodySchema.paymentProofUrl`, which attaches
105
+ * one AT request time, because the two answer different moments and only one of
106
+ * them is the common case. A member almost never has the screenshot at the
107
+ * instant they pick a plan: they choose the plan, go and pay, and come back.
108
+ * Worse, a plan can be requested by somebody who cannot attach anything at all
109
+ * — the owner accepting a join request opens a pending membership on the
110
+ * member's behalf, and that member's first sight of it is a fee they are asked
111
+ * to prove they have paid.
112
+ *
113
+ * So the proof is a follow-up act on an existing request rather than a field on
114
+ * the request itself, and it is the only way the member's own screens offer it.
115
+ *
116
+ * URL, not bytes: the file goes straight to storage from the browser and only
117
+ * its address is sent here. The server still refuses any address that is not
118
+ * ours — see `ERROR_MESSAGE.paymentProofInvalid`.
119
+ */
120
+ export declare const attachPaymentProofBodySchema: z.ZodObject<{
121
+ paymentProofUrl: z.ZodString;
122
+ }, z.core.$strip>;
123
+ export type AttachPaymentProofBody = z.infer<typeof attachPaymentProofBodySchema>;
124
+ /**
125
+ * What the member is told about their screenshot while the gym has not yet
126
+ * marked the payment done.
127
+ *
128
+ * Here rather than in the web client because these two sentences ARE the rule,
129
+ * not decoration on it: which one shows is decided purely by whether
130
+ * `paymentProofUrl` is set on a pending membership, and every surface that asks
131
+ * that question has to answer it with the same words. Two of them already do
132
+ * (Manage Membership and the request-sent screen), and they had drifted before
133
+ * — one said "No screenshot attached. Show your gym the payment when you next
134
+ * visit." while the other offered an upload box.
135
+ *
136
+ * There is deliberately NO third entry for "the gym marked it done". That state
137
+ * shows nothing at all, and a string here would invite somebody to render it.
138
+ */
139
+ export declare const PAYMENT_PROOF_PROMPT: {
140
+ /** Nothing attached yet, and the gym is still waiting on the money. */
141
+ readonly none: "Have you paid the membership fees? If yes, please upload the screenshot. Your fees are not marked as paid by the gym owner yet.";
142
+ /** Attached, and still not confirmed. The next move belongs to the gym. */
143
+ readonly attached: "You have already uploaded the fees screenshot, but it is not marked as paid yet. Please ask your gym owner.";
144
+ };
101
145
  /**
102
146
  * The gym marking a pending membership's payment as done, which is the single
103
147
  * act that activates it.
@@ -1 +1 @@
1
- {"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../src/membership.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAahC,eAAO,MAAM,mBAAmB;;;;EAAkC,CAAC;AACnE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,mBAAmB;;;;EAA2C,CAAC;AAC5E,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;iBAQxB,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAIpD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoE3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAI1D;;;GAGG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;iBAiBrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAI9E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kCAAkC;;;;;;;;;iBAiB7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE9F;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB;;;;iBAI/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAQrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,6DAA6D;AAC7D,eAAO,MAAM,iCAAiC;;iBAE5C,CAAC;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAI5F,eAAO,MAAM,mBAAmB;;;;;EAAuC,CAAC;AACxE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,yBAAyB;;;;;;;;;iBASlC,CAAC;AACL,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAI5E,8EAA8E;AAC9E,eAAO,MAAM,0BAA0B;;;iBAGrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAI9E,eAAO,MAAM,wBAAwB;;;;;;;;iBAInC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
1
+ {"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../src/membership.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAahC,eAAO,MAAM,mBAAmB;;;;EAAkC,CAAC;AACnE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,mBAAmB;;;;EAA2C,CAAC;AAC5E,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;iBAQxB,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAIpD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoE3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAI1D;;;GAGG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;iBAiBrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAI9E;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,4BAA4B;;iBAEvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB;IAC/B,uEAAuE;;IAEvE,2EAA2E;;CAGnE,CAAC;AAIX;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kCAAkC;;;;;;;;;iBAiB7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE9F;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB;;;;iBAI/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAQrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,6DAA6D;AAC7D,eAAO,MAAM,iCAAiC;;iBAE5C,CAAC;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAI5F,eAAO,MAAM,mBAAmB;;;;;EAAuC,CAAC;AACxE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,yBAAyB;;;;;;;;;iBASlC,CAAC;AACL,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAI5E,8EAA8E;AAC9E,eAAO,MAAM,0BAA0B;;;iBAGrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAI9E,eAAO,MAAM,wBAAwB;;;;;;;;iBAInC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
@@ -125,6 +125,50 @@ export const assignMembershipBodySchema = z.object({
125
125
  })
126
126
  .optional(),
127
127
  });
128
+ // ——— Attach the screenshot AFTER the request ——————————————
129
+ /**
130
+ * The member attaching their transfer screenshot to a request already open.
131
+ *
132
+ * Separate from `assignMembershipBodySchema.paymentProofUrl`, which attaches
133
+ * one AT request time, because the two answer different moments and only one of
134
+ * them is the common case. A member almost never has the screenshot at the
135
+ * instant they pick a plan: they choose the plan, go and pay, and come back.
136
+ * Worse, a plan can be requested by somebody who cannot attach anything at all
137
+ * — the owner accepting a join request opens a pending membership on the
138
+ * member's behalf, and that member's first sight of it is a fee they are asked
139
+ * to prove they have paid.
140
+ *
141
+ * So the proof is a follow-up act on an existing request rather than a field on
142
+ * the request itself, and it is the only way the member's own screens offer it.
143
+ *
144
+ * URL, not bytes: the file goes straight to storage from the browser and only
145
+ * its address is sent here. The server still refuses any address that is not
146
+ * ours — see `ERROR_MESSAGE.paymentProofInvalid`.
147
+ */
148
+ export const attachPaymentProofBodySchema = z.object({
149
+ paymentProofUrl: z.string().trim().min(1).max(2000),
150
+ });
151
+ /**
152
+ * What the member is told about their screenshot while the gym has not yet
153
+ * marked the payment done.
154
+ *
155
+ * Here rather than in the web client because these two sentences ARE the rule,
156
+ * not decoration on it: which one shows is decided purely by whether
157
+ * `paymentProofUrl` is set on a pending membership, and every surface that asks
158
+ * that question has to answer it with the same words. Two of them already do
159
+ * (Manage Membership and the request-sent screen), and they had drifted before
160
+ * — one said "No screenshot attached. Show your gym the payment when you next
161
+ * visit." while the other offered an upload box.
162
+ *
163
+ * There is deliberately NO third entry for "the gym marked it done". That state
164
+ * shows nothing at all, and a string here would invite somebody to render it.
165
+ */
166
+ export const PAYMENT_PROOF_PROMPT = {
167
+ /** Nothing attached yet, and the gym is still waiting on the money. */
168
+ none: 'Have you paid the membership fees? If yes, please upload the screenshot. Your fees are not marked as paid by the gym owner yet.',
169
+ /** Attached, and still not confirmed. The next move belongs to the gym. */
170
+ attached: 'You have already uploaded the fees screenshot, but it is not marked as paid yet. Please ask your gym owner.',
171
+ };
128
172
  // ——— Confirm / reject the payment ————————————————————————
129
173
  /**
130
174
  * The gym marking a pending membership's payment as done, which is the single
@@ -1 +1 @@
1
- {"version":3,"file":"membership.js","sourceRoot":"","sources":["../src/membership.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — Membership & payments\n * =======================================\n * A member's live membership at a center: which plan/tenure, its lifecycle\n * status and dates, pause windows, and the payment ledger. v1 is single-center,\n * so one active membership per user.\n *\n * The \"expiring\" state the UI shows is DERIVED from `endDate` at the edge; the\n * stored `status` is the true lifecycle state.\n *\n * @module gymmonk-schema/membership\n */\n\nimport { z } from 'zod';\n\n/*\n * Membership STATUS moved to its own module — see `membership-status.ts` for\n * why. Re-exported here so the many call sites that reach for the stored enum\n * alongside the membership DTO do not all have to change import paths, and so\n * there is still exactly one definition behind both spellings.\n */\nexport {\n effectiveMembershipStatus,\n isMembershipLive,\n type MembershipStatus,\n membershipStatusSchema,\n} from './membership-status.js';\n\nimport {\n isoDateSchema,\n isoDateTimeSchema,\n objectIdSchema,\n optionalTrimmedString,\n priceInrSchema,\n} from './common.js';\nimport { membershipStatusSchema } from './membership-status.js';\n\n// ─── Payments ────────────────────────────────────────────────────────────────\n\nexport const paymentMethodSchema = z.enum(['upi', 'cash', 'card']);\nexport type PaymentMethod = z.infer<typeof paymentMethodSchema>;\n\nexport const paymentStatusSchema = z.enum(['success', 'failed', 'pending']);\nexport type PaymentStatus = z.infer<typeof paymentStatusSchema>;\n\nexport const paymentSchema = z.object({\n id: objectIdSchema,\n membershipId: objectIdSchema,\n amountInr: priceInrSchema,\n method: paymentMethodSchema,\n status: paymentStatusSchema,\n paidAt: isoDateTimeSchema,\n note: z.string().nullable(),\n});\nexport type Payment = z.infer<typeof paymentSchema>;\n\n// ─── Entity (API response) ───────────────────────────────────────────────────\n\nexport const membershipSchema = z.object({\n id: objectIdSchema,\n /**\n * The enrolment this plan was sold against.\n *\n * The link that makes a plan belong to a stint at a gym rather than to a\n * person. It replaces a `(centerId, memberCode)` pair that had to be matched\n * by hand in three places, and it is what lets a plan be sold to somebody the\n * desk has only just written down — an enrolment exists from that moment,\n * whereas an account may not exist for weeks.\n */\n enrolmentId: objectIdSchema,\n /**\n * The account, denormalised from the enrolment for member-facing queries.\n * NULL until they claim, for the same reason it is null on the enrolment.\n */\n userId: objectIdSchema.nullable(),\n centerId: objectIdSchema,\n planId: objectIdSchema,\n planName: z.string(),\n /**\n * Which variant of the plan was bought, and how it read at the time.\n *\n * A plain string, not a fixed tenure enum: variant ids are minted per plan\n * now, so a gym can sell a 10-day pass. The LABEL is a snapshot for the same\n * reason a payment records what it was for — the owner may rename or delete\n * the variant later, and a membership must still be able to say what was\n * sold.\n */\n variantId: z.string(),\n variantLabel: z.string(),\n priceInr: priceInrSchema,\n status: membershipStatusSchema,\n /**\n * The term, PROVISIONAL while the membership is pending.\n *\n * Dated from the member's JOINING DATE, not from the day either party\n * pressed a button. Terms are a cycle — somebody who joined on the 4th runs\n * 4 Jul→4 Aug, 4 Aug→4 Sep — so a gym confirming a week late records the\n * month that was owed rather than starting a fresh one from today. Dating\n * them from the confirmation, which is what this used to do, walked every\n * member's renewal day forward by however long the desk took to look.\n *\n * Written at request time so every screen that reads them keeps working, and\n * recomputed on confirmation in case the cycle moved in between.\n */\n startDate: isoDateSchema,\n endDate: isoDateSchema,\n pausedFrom: isoDateSchema.nullable(),\n pausedTo: isoDateSchema.nullable(),\n /**\n * The screenshot the member attached, if they attached one.\n *\n * Optional on purpose. A member paying cash at the desk has nothing to\n * screenshot, and refusing to accept their request until they produce one\n * would lock the commonest case out of the flow entirely.\n */\n paymentProofUrl: z.string().nullable(),\n /** When the plan was chosen. Distinct from `createdAt` only in intent. */\n requestedAt: isoDateTimeSchema,\n /** When the gym marked the payment done or turned it down. */\n decidedAt: isoDateTimeSchema.nullable(),\n /** Who decided. Null while pending. */\n decidedBy: objectIdSchema.nullable(),\n /** Why it was turned down, in the gym's words. */\n decisionNote: z.string().nullable(),\n createdAt: isoDateTimeSchema,\n updatedAt: isoDateTimeSchema,\n});\nexport type Membership = z.infer<typeof membershipSchema>;\n\n// ─── Assign / renew / upgrade ────────────────────────────────────────────────\n\n/**\n * Assign a plan to a member (new membership, renewal, or upgrade). Records the\n * charge in one shot; omit `payment` for an unpaid/invited assignment.\n */\nexport const assignMembershipBodySchema = z.object({\n planId: objectIdSchema,\n variantId: z.string().trim().min(1),\n /** Explicit start; defaults to today server-side when omitted. */\n startDate: isoDateSchema.optional(),\n /**\n * A screenshot of the transfer, uploaded to storage first and passed as its\n * URL. Optional — see `paymentProofUrl` on the entity.\n */\n paymentProofUrl: z.string().trim().max(2000).optional(),\n payment: z\n .object({\n amountInr: priceInrSchema,\n method: paymentMethodSchema,\n status: paymentStatusSchema.default('success'),\n })\n .optional(),\n});\nexport type AssignMembershipBody = z.infer<typeof assignMembershipBodySchema>;\n\n// ——— Confirm / reject the payment ————————————————————————\n\n/**\n * The gym marking a pending membership's payment as done, which is the single\n * act that activates it.\n *\n * It carries the LEDGER entry rather than being a bare approval, because those\n * are the same event: the owner is asserting that this much money arrived by\n * this route. Splitting them would let a membership go live with nothing\n * written down, which is exactly the hole this feature exists to close.\n *\n * The amount is not forced to equal the plan price. A gym that takes half now\n * and half on Friday still wants the member training, and the balance is\n * already tracked against the term.\n */\nexport const confirmMembershipPaymentBodySchema = z.object({\n amountInr: priceInrSchema,\n method: paymentMethodSchema,\n note: optionalTrimmedString(240),\n /**\n * The term the owner BELIEVES they are paying for, echoed back from what the\n * screen showed them.\n *\n * The server derives the term itself and never trusts this as input — it is\n * a guard, not a parameter. The owner is told \"this covers 3 Jul to 3 Aug\"\n * and then presses a button; if a second manager confirms a term in the\n * seconds between, the derived term moves on and this one no longer matches.\n * Without the echo that owner silently pays for a DIFFERENT month than the\n * one they read, and the gym's ledger disagrees with the conversation that\n * produced it. Optional so a client that shows no term still works.\n */\n expectedTermStart: isoDateSchema.optional(),\n});\nexport type ConfirmMembershipPaymentBody = z.infer<typeof confirmMembershipPaymentBodySchema>;\n\n/**\n * One billing term, dated from the member's JOINING DATE.\n *\n * Terms are a derived cycle, not free-form dates the desk types in: a member\n * who joined on the 4th is billed 4 Jul→4 Aug, 4 Aug→4 Sep, and so on, each\n * one the length of the variant they bought. Nobody picks a start date,\n * because every gym already answers \"which month is this for?\" the same way,\n * and letting two people type it produces two answers.\n *\n * `isOverdue` is the term having already ended — the owner marking a month\n * they forgot at the time. That is a normal, expected act rather than an\n * error, so it is surfaced as a fact about the term rather than a warning.\n */\nexport const membershipTermSchema = z.object({\n startDate: isoDateSchema,\n endDate: isoDateSchema,\n isOverdue: z.boolean(),\n});\nexport type MembershipTerm = z.infer<typeof membershipTermSchema>;\n\n/**\n * One row of the gym's confirmation queue.\n *\n * The membership plus just enough of the person to act on it. The owner is\n * deciding about a HUMAN — \"did Ravi's transfer land\" — and a list of plan\n * names and amounts with no names attached is not something anybody can\n * answer. Kept to four fields rather than embedding the whole roster row,\n * because this list is read on every visit to the members screen.\n */\nexport const pendingMembershipRowSchema = z.object({\n membership: membershipSchema,\n member: z.object({\n enrolmentId: objectIdSchema,\n gymMemberCode: z.string(),\n name: z.string(),\n avatarUrl: z.string().nullable(),\n }),\n});\nexport type PendingMembershipRow = z.infer<typeof pendingMembershipRowSchema>;\n\n/** Turning a request down. The reason reaches the member. */\nexport const rejectMembershipPaymentBodySchema = z.object({\n reason: optionalTrimmedString(240),\n});\nexport type RejectMembershipPaymentBody = z.infer<typeof rejectMembershipPaymentBodySchema>;\n\n// ─── Pause ───────────────────────────────────────────────────────────────────\n\nexport const pauseDurationSchema = z.enum(['1w', '2w', '1m', 'custom']);\nexport type PauseDuration = z.infer<typeof pauseDurationSchema>;\n\nexport const pauseMembershipBodySchema = z\n .object({\n duration: pauseDurationSchema,\n customFrom: isoDateSchema.optional(),\n customTo: isoDateSchema.optional(),\n })\n .refine((v) => v.duration !== 'custom' || (v.customFrom && v.customTo), {\n message: 'Pick both pause dates',\n path: ['customTo'],\n });\nexport type PauseMembershipBody = z.infer<typeof pauseMembershipBodySchema>;\n\n// ─── Cancel ──────────────────────────────────────────────────────────────────\n\n/** Cancellation requires echoing the member code as a safety confirmation. */\nexport const cancelMembershipBodySchema = z.object({\n confirmationCode: z.string().trim().min(1),\n reason: optionalTrimmedString(240),\n});\nexport type CancelMembershipBody = z.infer<typeof cancelMembershipBodySchema>;\n\n// ─── Collect an offline payment ──────────────────────────────────────────────\n\nexport const collectPaymentBodySchema = z.object({\n amountInr: priceInrSchema,\n method: paymentMethodSchema,\n note: optionalTrimmedString(240),\n});\nexport type CollectPaymentBody = z.infer<typeof collectPaymentBodySchema>;\n"]}
1
+ {"version":3,"file":"membership.js","sourceRoot":"","sources":["../src/membership.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — Membership & payments\n * =======================================\n * A member's live membership at a center: which plan/tenure, its lifecycle\n * status and dates, pause windows, and the payment ledger. v1 is single-center,\n * so one active membership per user.\n *\n * The \"expiring\" state the UI shows is DERIVED from `endDate` at the edge; the\n * stored `status` is the true lifecycle state.\n *\n * @module gymmonk-schema/membership\n */\n\nimport { z } from 'zod';\n\n/*\n * Membership STATUS moved to its own module — see `membership-status.ts` for\n * why. Re-exported here so the many call sites that reach for the stored enum\n * alongside the membership DTO do not all have to change import paths, and so\n * there is still exactly one definition behind both spellings.\n */\nexport {\n effectiveMembershipStatus,\n isMembershipLive,\n type MembershipStatus,\n membershipStatusSchema,\n} from './membership-status.js';\n\nimport {\n isoDateSchema,\n isoDateTimeSchema,\n objectIdSchema,\n optionalTrimmedString,\n priceInrSchema,\n} from './common.js';\nimport { membershipStatusSchema } from './membership-status.js';\n\n// ─── Payments ────────────────────────────────────────────────────────────────\n\nexport const paymentMethodSchema = z.enum(['upi', 'cash', 'card']);\nexport type PaymentMethod = z.infer<typeof paymentMethodSchema>;\n\nexport const paymentStatusSchema = z.enum(['success', 'failed', 'pending']);\nexport type PaymentStatus = z.infer<typeof paymentStatusSchema>;\n\nexport const paymentSchema = z.object({\n id: objectIdSchema,\n membershipId: objectIdSchema,\n amountInr: priceInrSchema,\n method: paymentMethodSchema,\n status: paymentStatusSchema,\n paidAt: isoDateTimeSchema,\n note: z.string().nullable(),\n});\nexport type Payment = z.infer<typeof paymentSchema>;\n\n// ─── Entity (API response) ───────────────────────────────────────────────────\n\nexport const membershipSchema = z.object({\n id: objectIdSchema,\n /**\n * The enrolment this plan was sold against.\n *\n * The link that makes a plan belong to a stint at a gym rather than to a\n * person. It replaces a `(centerId, memberCode)` pair that had to be matched\n * by hand in three places, and it is what lets a plan be sold to somebody the\n * desk has only just written down — an enrolment exists from that moment,\n * whereas an account may not exist for weeks.\n */\n enrolmentId: objectIdSchema,\n /**\n * The account, denormalised from the enrolment for member-facing queries.\n * NULL until they claim, for the same reason it is null on the enrolment.\n */\n userId: objectIdSchema.nullable(),\n centerId: objectIdSchema,\n planId: objectIdSchema,\n planName: z.string(),\n /**\n * Which variant of the plan was bought, and how it read at the time.\n *\n * A plain string, not a fixed tenure enum: variant ids are minted per plan\n * now, so a gym can sell a 10-day pass. The LABEL is a snapshot for the same\n * reason a payment records what it was for — the owner may rename or delete\n * the variant later, and a membership must still be able to say what was\n * sold.\n */\n variantId: z.string(),\n variantLabel: z.string(),\n priceInr: priceInrSchema,\n status: membershipStatusSchema,\n /**\n * The term, PROVISIONAL while the membership is pending.\n *\n * Dated from the member's JOINING DATE, not from the day either party\n * pressed a button. Terms are a cycle — somebody who joined on the 4th runs\n * 4 Jul→4 Aug, 4 Aug→4 Sep — so a gym confirming a week late records the\n * month that was owed rather than starting a fresh one from today. Dating\n * them from the confirmation, which is what this used to do, walked every\n * member's renewal day forward by however long the desk took to look.\n *\n * Written at request time so every screen that reads them keeps working, and\n * recomputed on confirmation in case the cycle moved in between.\n */\n startDate: isoDateSchema,\n endDate: isoDateSchema,\n pausedFrom: isoDateSchema.nullable(),\n pausedTo: isoDateSchema.nullable(),\n /**\n * The screenshot the member attached, if they attached one.\n *\n * Optional on purpose. A member paying cash at the desk has nothing to\n * screenshot, and refusing to accept their request until they produce one\n * would lock the commonest case out of the flow entirely.\n */\n paymentProofUrl: z.string().nullable(),\n /** When the plan was chosen. Distinct from `createdAt` only in intent. */\n requestedAt: isoDateTimeSchema,\n /** When the gym marked the payment done or turned it down. */\n decidedAt: isoDateTimeSchema.nullable(),\n /** Who decided. Null while pending. */\n decidedBy: objectIdSchema.nullable(),\n /** Why it was turned down, in the gym's words. */\n decisionNote: z.string().nullable(),\n createdAt: isoDateTimeSchema,\n updatedAt: isoDateTimeSchema,\n});\nexport type Membership = z.infer<typeof membershipSchema>;\n\n// ─── Assign / renew / upgrade ────────────────────────────────────────────────\n\n/**\n * Assign a plan to a member (new membership, renewal, or upgrade). Records the\n * charge in one shot; omit `payment` for an unpaid/invited assignment.\n */\nexport const assignMembershipBodySchema = z.object({\n planId: objectIdSchema,\n variantId: z.string().trim().min(1),\n /** Explicit start; defaults to today server-side when omitted. */\n startDate: isoDateSchema.optional(),\n /**\n * A screenshot of the transfer, uploaded to storage first and passed as its\n * URL. Optional — see `paymentProofUrl` on the entity.\n */\n paymentProofUrl: z.string().trim().max(2000).optional(),\n payment: z\n .object({\n amountInr: priceInrSchema,\n method: paymentMethodSchema,\n status: paymentStatusSchema.default('success'),\n })\n .optional(),\n});\nexport type AssignMembershipBody = z.infer<typeof assignMembershipBodySchema>;\n\n// ——— Attach the screenshot AFTER the request ——————————————\n\n/**\n * The member attaching their transfer screenshot to a request already open.\n *\n * Separate from `assignMembershipBodySchema.paymentProofUrl`, which attaches\n * one AT request time, because the two answer different moments and only one of\n * them is the common case. A member almost never has the screenshot at the\n * instant they pick a plan: they choose the plan, go and pay, and come back.\n * Worse, a plan can be requested by somebody who cannot attach anything at all\n * — the owner accepting a join request opens a pending membership on the\n * member's behalf, and that member's first sight of it is a fee they are asked\n * to prove they have paid.\n *\n * So the proof is a follow-up act on an existing request rather than a field on\n * the request itself, and it is the only way the member's own screens offer it.\n *\n * URL, not bytes: the file goes straight to storage from the browser and only\n * its address is sent here. The server still refuses any address that is not\n * ours — see `ERROR_MESSAGE.paymentProofInvalid`.\n */\nexport const attachPaymentProofBodySchema = z.object({\n paymentProofUrl: z.string().trim().min(1).max(2000),\n});\nexport type AttachPaymentProofBody = z.infer<typeof attachPaymentProofBodySchema>;\n\n/**\n * What the member is told about their screenshot while the gym has not yet\n * marked the payment done.\n *\n * Here rather than in the web client because these two sentences ARE the rule,\n * not decoration on it: which one shows is decided purely by whether\n * `paymentProofUrl` is set on a pending membership, and every surface that asks\n * that question has to answer it with the same words. Two of them already do\n * (Manage Membership and the request-sent screen), and they had drifted before\n * — one said \"No screenshot attached. Show your gym the payment when you next\n * visit.\" while the other offered an upload box.\n *\n * There is deliberately NO third entry for \"the gym marked it done\". That state\n * shows nothing at all, and a string here would invite somebody to render it.\n */\nexport const PAYMENT_PROOF_PROMPT = {\n /** Nothing attached yet, and the gym is still waiting on the money. */\n none: 'Have you paid the membership fees? If yes, please upload the screenshot. Your fees are not marked as paid by the gym owner yet.',\n /** Attached, and still not confirmed. The next move belongs to the gym. */\n attached:\n 'You have already uploaded the fees screenshot, but it is not marked as paid yet. Please ask your gym owner.',\n} as const;\n\n// ——— Confirm / reject the payment ————————————————————————\n\n/**\n * The gym marking a pending membership's payment as done, which is the single\n * act that activates it.\n *\n * It carries the LEDGER entry rather than being a bare approval, because those\n * are the same event: the owner is asserting that this much money arrived by\n * this route. Splitting them would let a membership go live with nothing\n * written down, which is exactly the hole this feature exists to close.\n *\n * The amount is not forced to equal the plan price. A gym that takes half now\n * and half on Friday still wants the member training, and the balance is\n * already tracked against the term.\n */\nexport const confirmMembershipPaymentBodySchema = z.object({\n amountInr: priceInrSchema,\n method: paymentMethodSchema,\n note: optionalTrimmedString(240),\n /**\n * The term the owner BELIEVES they are paying for, echoed back from what the\n * screen showed them.\n *\n * The server derives the term itself and never trusts this as input — it is\n * a guard, not a parameter. The owner is told \"this covers 3 Jul to 3 Aug\"\n * and then presses a button; if a second manager confirms a term in the\n * seconds between, the derived term moves on and this one no longer matches.\n * Without the echo that owner silently pays for a DIFFERENT month than the\n * one they read, and the gym's ledger disagrees with the conversation that\n * produced it. Optional so a client that shows no term still works.\n */\n expectedTermStart: isoDateSchema.optional(),\n});\nexport type ConfirmMembershipPaymentBody = z.infer<typeof confirmMembershipPaymentBodySchema>;\n\n/**\n * One billing term, dated from the member's JOINING DATE.\n *\n * Terms are a derived cycle, not free-form dates the desk types in: a member\n * who joined on the 4th is billed 4 Jul→4 Aug, 4 Aug→4 Sep, and so on, each\n * one the length of the variant they bought. Nobody picks a start date,\n * because every gym already answers \"which month is this for?\" the same way,\n * and letting two people type it produces two answers.\n *\n * `isOverdue` is the term having already ended — the owner marking a month\n * they forgot at the time. That is a normal, expected act rather than an\n * error, so it is surfaced as a fact about the term rather than a warning.\n */\nexport const membershipTermSchema = z.object({\n startDate: isoDateSchema,\n endDate: isoDateSchema,\n isOverdue: z.boolean(),\n});\nexport type MembershipTerm = z.infer<typeof membershipTermSchema>;\n\n/**\n * One row of the gym's confirmation queue.\n *\n * The membership plus just enough of the person to act on it. The owner is\n * deciding about a HUMAN — \"did Ravi's transfer land\" — and a list of plan\n * names and amounts with no names attached is not something anybody can\n * answer. Kept to four fields rather than embedding the whole roster row,\n * because this list is read on every visit to the members screen.\n */\nexport const pendingMembershipRowSchema = z.object({\n membership: membershipSchema,\n member: z.object({\n enrolmentId: objectIdSchema,\n gymMemberCode: z.string(),\n name: z.string(),\n avatarUrl: z.string().nullable(),\n }),\n});\nexport type PendingMembershipRow = z.infer<typeof pendingMembershipRowSchema>;\n\n/** Turning a request down. The reason reaches the member. */\nexport const rejectMembershipPaymentBodySchema = z.object({\n reason: optionalTrimmedString(240),\n});\nexport type RejectMembershipPaymentBody = z.infer<typeof rejectMembershipPaymentBodySchema>;\n\n// ─── Pause ───────────────────────────────────────────────────────────────────\n\nexport const pauseDurationSchema = z.enum(['1w', '2w', '1m', 'custom']);\nexport type PauseDuration = z.infer<typeof pauseDurationSchema>;\n\nexport const pauseMembershipBodySchema = z\n .object({\n duration: pauseDurationSchema,\n customFrom: isoDateSchema.optional(),\n customTo: isoDateSchema.optional(),\n })\n .refine((v) => v.duration !== 'custom' || (v.customFrom && v.customTo), {\n message: 'Pick both pause dates',\n path: ['customTo'],\n });\nexport type PauseMembershipBody = z.infer<typeof pauseMembershipBodySchema>;\n\n// ─── Cancel ──────────────────────────────────────────────────────────────────\n\n/** Cancellation requires echoing the member code as a safety confirmation. */\nexport const cancelMembershipBodySchema = z.object({\n confirmationCode: z.string().trim().min(1),\n reason: optionalTrimmedString(240),\n});\nexport type CancelMembershipBody = z.infer<typeof cancelMembershipBodySchema>;\n\n// ─── Collect an offline payment ──────────────────────────────────────────────\n\nexport const collectPaymentBodySchema = z.object({\n amountInr: priceInrSchema,\n method: paymentMethodSchema,\n note: optionalTrimmedString(240),\n});\nexport type CollectPaymentBody = z.infer<typeof collectPaymentBodySchema>;\n"]}
@@ -223,6 +223,16 @@ export declare const SUCCESS_MESSAGE: {
223
223
  readonly title: "Request withdrawn";
224
224
  readonly description: "Pick another plan whenever you are ready.";
225
225
  };
226
+ /**
227
+ * Says the gym can SEE it, not that the plan has started. The member has just
228
+ * done the only thing left to them and the temptation is to congratulate them
229
+ * with something that sounds like confirmation; the next move is still the
230
+ * gym's, and the screen they are looking at says so.
231
+ */
232
+ readonly membershipProofAttached: {
233
+ readonly title: "Screenshot sent";
234
+ readonly description: "Your gym can see it now.";
235
+ };
226
236
  readonly membershipPaymentConfirmed: {
227
237
  readonly title: "Payment confirmed";
228
238
  readonly description: "The plan is now active for this member.";
@@ -446,6 +456,8 @@ export declare const ERROR_MESSAGE: {
446
456
  readonly membershipWithdrawFailed: "Could not withdraw that request. Please try again.";
447
457
  /** The URL did not come from our own storage — a broken or hostile client. */
448
458
  readonly paymentProofInvalid: "That payment screenshot could not be attached. Upload it again.";
459
+ /** The upload itself worked; saving it against the request did not. */
460
+ readonly paymentProofAttachFailed: "Could not send that screenshot. Please try again.";
449
461
  readonly workoutPlanRequired: "Choose a plan to assign";
450
462
  readonly attendanceMarkFailed: "Could not save that attendance. Please try again.";
451
463
  readonly memberStatusFailed: "Could not change that member's status. Please try again.";
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH;;;;GAIG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;IAsBrB;;;;;;;;;;;;;OAaG;;;CAKK,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAIrE,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;;;IAU1B,sEAAsE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8DtE;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkH6C,CAAC;AAEnD,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,eAAe,CAAC;AAE7D,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB;iCACV,MAAM,KAAG,aAAa;gCAIvB,MAAM,KAAG,aAAa;sCAIhB,MAAM,KAAG,aAAa;uCAIrB,MAAM,KAAG,aAAa;uCAGtB,MAAM,KAAG,aAAa;uCAItB,MAAM,KAAG,aAAa;CAIxC,CAAC;AAIX;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;IAiBxB;;;;;OAKG;;IAGH;;;;;;;OAOG;;IAGH;;;;;;OAMG;;IAGH,4EAA4E;;IAE5E,+EAA+E;;IAE/E,gFAAgF;;IAGhF,iDAAiD;;;IAKjD,kDAAkD;;IAElD;;;;OAIG;;IAEH;;;;OAIG;;IAEH,qEAAqE;;IAErE,2EAA2E;;IAE3E,wEAAwE;;;;;;;IASxE,gEAAgE;;;;;;;;;;;IAehE,2EAA2E;;;IAI3E;;;OAGG;;;IAIH;;;;OAIG;;IAGH;;;;OAIG;;IAEH;;;;;OAKG;;IAGH;;;OAGG;;IAGH,mEAAmE;;;;;IAKnE,8EAA8E;;;;;;;;;;;;;IAgB9E;;;OAGG;;;;IAMH,wEAAwE;;IAExE,uEAAuE;;IAEvE,yEAAyE;;IAEzE,8EAA8E;;IAE9E,0DAA0D;;;IAG1D;;;;OAIG;;IAIH,4EAA4E;;IAG5E,6DAA6D;;IAE7D,kDAAkD;;IAElD,+DAA+D;;IAE/D,sEAAsE;;;IAItE,kEAAkE;;;;IAIlE,uEAAuE;;IAEvE,8EAA8E;;;IAI9E;;;;;;;;;;;;;OAaG;;;;;IAMH;;;;;;OAMG;;;;;;;;;;;;CAkBsC,CAAC;AAE5C,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,aAAa,CAAC;AAEzD,iDAAiD;AACjD,eAAO,MAAM,iBAAiB;8BACX,MAAM,KAAG,MAAM;IAChC;;;;;;;;OAQG;gDACgC,MAAM,KAAG,MAAM;IAElD,2DAA2D;sCAClC,MAAM,KAAG,MAAM;CAEhC,CAAC"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH;;;;GAIG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;IAsBrB;;;;;;;;;;;;;OAaG;;;CAKK,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAIrE,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;;;IAU1B,sEAAsE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8DtE;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAwCH;;;;;OAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+E6C,CAAC;AAEnD,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,eAAe,CAAC;AAE7D,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB;iCACV,MAAM,KAAG,aAAa;gCAIvB,MAAM,KAAG,aAAa;sCAIhB,MAAM,KAAG,aAAa;uCAIrB,MAAM,KAAG,aAAa;uCAGtB,MAAM,KAAG,aAAa;uCAItB,MAAM,KAAG,aAAa;CAIxC,CAAC;AAIX;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;IAiBxB;;;;;OAKG;;IAGH;;;;;;;OAOG;;IAGH;;;;;;OAMG;;IAGH,4EAA4E;;IAE5E,+EAA+E;;IAE/E,gFAAgF;;IAGhF,iDAAiD;;;IAKjD,kDAAkD;;IAElD;;;;OAIG;;IAEH;;;;OAIG;;IAEH,qEAAqE;;IAErE,2EAA2E;;IAE3E,wEAAwE;;;;;;;IASxE,gEAAgE;;;;;;;;;;;IAehE,2EAA2E;;;IAI3E;;;OAGG;;;IAIH;;;;OAIG;;IAGH;;;;OAIG;;IAEH;;;;;OAKG;;IAGH;;;OAGG;;IAGH,mEAAmE;;;;;IAKnE,8EAA8E;;IAE9E,uEAAuE;;;;;;;;;;;;;IAgBvE;;;OAGG;;;;IAMH,wEAAwE;;IAExE,uEAAuE;;IAEvE,yEAAyE;;IAEzE,8EAA8E;;IAE9E,0DAA0D;;;IAG1D;;;;OAIG;;IAIH,4EAA4E;;IAG5E,6DAA6D;;IAE7D,kDAAkD;;IAElD,+DAA+D;;IAE/D,sEAAsE;;;IAItE,kEAAkE;;;;IAIlE,uEAAuE;;IAEvE,8EAA8E;;;IAI9E;;;;;;;;;;;;;OAaG;;;;;IAMH;;;;;;OAMG;;;;;;;;;;;;CAkBsC,CAAC;AAE5C,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,aAAa,CAAC;AAEzD,iDAAiD;AACjD,eAAO,MAAM,iBAAiB;8BACX,MAAM,KAAG,MAAM;IAChC;;;;;;;;OAQG;gDACgC,MAAM,KAAG,MAAM;IAElD,2DAA2D;sCAClC,MAAM,KAAG,MAAM;CAEhC,CAAC"}
package/dist/messages.js CHANGED
@@ -169,6 +169,16 @@ export const SUCCESS_MESSAGE = {
169
169
  title: 'Request withdrawn',
170
170
  description: 'Pick another plan whenever you are ready.',
171
171
  },
172
+ /**
173
+ * Says the gym can SEE it, not that the plan has started. The member has just
174
+ * done the only thing left to them and the temptation is to congratulate them
175
+ * with something that sounds like confirmation; the next move is still the
176
+ * gym's, and the screen they are looking at says so.
177
+ */
178
+ membershipProofAttached: {
179
+ title: 'Screenshot sent',
180
+ description: 'Your gym can see it now.',
181
+ },
172
182
  membershipPaymentConfirmed: {
173
183
  title: 'Payment confirmed',
174
184
  description: 'The plan is now active for this member.',
@@ -398,6 +408,8 @@ export const ERROR_MESSAGE = {
398
408
  membershipWithdrawFailed: 'Could not withdraw that request. Please try again.',
399
409
  /** The URL did not come from our own storage — a broken or hostile client. */
400
410
  paymentProofInvalid: 'That payment screenshot could not be attached. Upload it again.',
411
+ /** The upload itself worked; saving it against the request did not. */
412
+ paymentProofAttachFailed: 'Could not send that screenshot. Please try again.',
401
413
  // Training
402
414
  workoutPlanRequired: 'Choose a plan to assign',
403
415
  // Roster (owner console)
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,UAAU;IACV,cAAc,EAAE,gBAAgB;IAChC,gBAAgB,EAAE,kBAAkB;IACpC,SAAS,EAAE,WAAW;IAEtB,OAAO;IACP,aAAa,EAAE,eAAe;IAC9B,aAAa,EAAE,eAAe;IAC9B,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,WAAW;IACtB,mBAAmB,EAAE,qBAAqB;IAE1C,OAAO;IACP,cAAc,EAAE,gBAAgB;IAChC,mBAAmB,EAAE,qBAAqB;IAC1C,YAAY,EAAE,cAAc;IAE5B,gBAAgB;IAChB,YAAY,EAAE,cAAc;IAC5B,mBAAmB,EAAE,qBAAqB;IAE1C;;;;;;;;;;;;;OAaG;IACH,mBAAmB,EAAE,qBAAqB;IAE1C,oCAAoC;IACpC,mBAAmB,EAAE,qBAAqB;CAClC,CAAC;AAYX,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,aAAa;IACb,wBAAwB,EAAE;QACxB,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,qBAAqB;KACnC;IACD,uBAAuB,EAAE;QACvB,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,qBAAqB;KACnC;IACD,sEAAsE;IACtE,mBAAmB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;IAEvC,qBAAqB;IACrB,cAAc,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE;IAC5C,mBAAmB,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;IAEtD,gBAAgB;IAChB,WAAW,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE;IACtC,aAAa,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE;IAC1C,qBAAqB,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE;IAC3D,qBAAqB,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE;IAC3D,cAAc,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE;IAE9C,2BAA2B;IAC3B,iBAAiB,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE;IAClD,iBAAiB,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE;IAClD,mBAAmB,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;IACtD,wBAAwB,EAAE;QACxB,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EAAE,kCAAkC;KAChD;IAED,0BAA0B;IAC1B,mBAAmB,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;IAEtD,WAAW;IACX,kBAAkB,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;IACrD,kBAAkB,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE;IACnD,kBAAkB,EAAE;QAClB,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EAAE,0CAA0C;KACxD;IACD,cAAc,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE;IAC5C,cAAc,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE;IAC1C,cAAc,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE;IAC5C,mBAAmB,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE;IAEvD,0DAA0D;IAC1D,gBAAgB,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE;IACjD,gBAAgB,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE;IAC/C,gBAAgB,EAAE;QAChB,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EAAE,2CAA2C;KACzD;IACD,iBAAiB,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE;IACnD,gBAAgB,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE;IACjD,WAAW,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE;IACpC,WAAW,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE;IACpC,WAAW,EAAE;QACX,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,iDAAiD;KAC/D;IAED,yBAAyB;IACzB,gBAAgB,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE;IAC/C,oBAAoB,EAAE;QACpB,KAAK,EAAE,wBAAwB;QAC/B,WAAW,EAAE,8BAA8B;KAC5C;IACD,kBAAkB,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE;IACnD,aAAa,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE;IAC1C;;;;OAIG;IACH,kBAAkB,EAAE;QAClB,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,2CAA2C;KACzD;IACD,sBAAsB,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE;IACtD,yBAAyB,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;IAC5D,gBAAgB,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE;IAE/C,WAAW;IACX,SAAS,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,2BAA2B,EAAE;IAC5E,UAAU,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE;IACnE,gBAAgB,EAAE;QAChB,KAAK,EAAE,kBAAkB;QACzB,WAAW,EAAE,sCAAsC;KACpD;IAED,2BAA2B;IAC3B,qBAAqB,EAAE;QACrB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,iCAAiC;KAC/C;IACD,yBAAyB,EAAE;QACzB,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,oCAAoC;KAClD;IACD,yBAAyB,EAAE;QACzB,KAAK,EAAE,kBAAkB;QACzB,WAAW,EAAE,2BAA2B;KACzC;IAED,kCAAkC;IAClC,mBAAmB,EAAE;QACnB,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,+CAA+C;KAC7D;IACD,0BAA0B,EAAE;QAC1B,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,2CAA2C;KACzD;IACD,0BAA0B,EAAE;QAC1B,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,yCAAyC;KACvD;IACD,yBAAyB,EAAE;QACzB,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EAAE,iDAAiD;KAC/D;IAED,qCAAqC;IACrC,aAAa,EAAE;QACb,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,2CAA2C;KACzD;IACD,WAAW,EAAE;QACX,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,uCAAuC;KACrD;IACD,WAAW,EAAE;QACX,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,0BAA0B;KACxC;IAED,aAAa;IACb,iBAAiB,EAAE;QACjB,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,mCAAmC;KACjD;IACD,kBAAkB,EAAE;QAClB,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EAAE,6BAA6B;KAC3C;IACD,gBAAgB,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE;IAChD,qBAAqB,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE;IAC1D,sBAAsB,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;IAChD,mBAAmB,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;IACtD,qBAAqB,EAAE;QACrB,KAAK,EAAE,wBAAwB;QAC/B,WAAW,EAAE,sBAAsB;KACpC;IAED,oBAAoB;IACpB,kBAAkB,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE;IACpD,oBAAoB,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE;IACxD,oBAAoB,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE;IAEhD,6BAA6B;IAC7B,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE;IAC7B,SAAS,EAAE,EAAE,KAAK,EAAE,0BAA0B,EAAE;IAChD,iBAAiB,EAAE;QACjB,KAAK,EAAE,gCAAgC;QACvC,WAAW,EAAE,mBAAmB;KACjC;IAED,+EAA+E;IAC/E,oBAAoB,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE;IACzD,qBAAqB,EAAE;QACrB,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,6BAA6B;KAC3C;IACD,sBAAsB,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE;IACvD,mBAAmB,EAAE;QACnB,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,oDAAoD;KAClE;IACD,oBAAoB,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE;IACnD,kBAAkB,EAAE;QAClB,KAAK,EAAE,aAAa;QACpB,WAAW,EAAE,oDAAoD;KAClE;IACD,mBAAmB,EAAE;QACnB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,kEAAkE;KAChF;CAC+C,CAAC;AAInD,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,WAAW,EAAE,CAAC,IAAY,EAAiB,EAAE,CAAC,CAAC;QAC7C,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,yBAAyB,IAAI,GAAG;KAC9C,CAAC;IACF,UAAU,EAAE,CAAC,IAAY,EAAiB,EAAE,CAAC,CAAC;QAC5C,KAAK,EAAE,aAAa;QACpB,WAAW,EAAE,yBAAyB,IAAI,GAAG;KAC9C,CAAC;IACF,aAAa,EAAE,CAAC,OAAe,EAAiB,EAAE,CAAC,CAAC;QAClD,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,6BAA6B,OAAO,GAAG;KACrD,CAAC;IACF,YAAY,EAAE,CAAC,SAAiB,EAAiB,EAAE,CAAC,CAAC;QACnD,KAAK,EAAE,eAAe,SAAS,EAAE;KAClC,CAAC;IACF,cAAc,EAAE,CAAC,OAAe,EAAiB,EAAE,CAAC,CAAC;QACnD,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,GAAG,OAAO,0BAA0B;KAClD,CAAC;IACF,cAAc,EAAE,CAAC,OAAe,EAAiB,EAAE,CAAC,CAAC;QACnD,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,gBAAgB,OAAO,qBAAqB;KAC1D,CAAC;CACM,CAAC;AAEX,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,sBAAsB;IACtB,OAAO,EAAE,yCAAyC;IAClD,OAAO,EAAE,qDAAqD;IAC9D,OAAO,EAAE,uCAAuC;IAChD,cAAc,EAAE,6CAA6C;IAE7D,yBAAyB;IACzB,WAAW,EAAE,sCAAsC;IAEnD,aAAa;IACb,YAAY,EAAE,2BAA2B;IACzC,qBAAqB,EAAE,iDAAiD;IACxE,sBAAsB,EAAE,mDAAmD;IAE3E,8EAA8E;IAC9E,qBAAqB,EAAE,mDAAmD;IAC1E;;;;;OAKG;IACH,8BAA8B,EAC5B,gFAAgF;IAClF;;;;;;;OAOG;IACH,4BAA4B,EAC1B,yFAAyF;IAC3F;;;;;;OAMG;IACH,mBAAmB,EACjB,+FAA+F;IACjG,4EAA4E;IAC5E,oBAAoB,EAAE,oDAAoD;IAC1E,+EAA+E;IAC/E,2BAA2B,EAAE,0DAA0D;IACvF,gFAAgF;IAChF,mBAAmB,EACjB,oFAAoF;IACtF,iDAAiD;IACjD,uBAAuB,EAAE,yCAAyC;IAClE,oBAAoB,EAAE,8BAA8B;IAEpD,+EAA+E;IAC/E,kDAAkD;IAClD,oBAAoB,EAAE,kCAAkC;IACxD;;;;OAIG;IACH,uBAAuB,EAAE,yCAAyC;IAClE;;;;OAIG;IACH,sBAAsB,EAAE,iDAAiD;IACzE,qEAAqE;IACrE,4BAA4B,EAAE,0CAA0C;IACxE,2EAA2E;IAC3E,0BAA0B,EAAE,wCAAwC;IACpE,wEAAwE;IACxE,wBAAwB,EAAE,gDAAgD;IAE1E,8BAA8B;IAC9B,qBAAqB,EAAE,uCAAuC;IAC9D,mBAAmB,EAAE,0DAA0D;IAC/E,aAAa,EAAE,mEAAmE;IAClF,2BAA2B,EAAE,sCAAsC;IACnE,wBAAwB,EAAE,kCAAkC;IAC5D,gEAAgE;IAChE,2BAA2B,EAAE,wDAAwD;IACrF,qBAAqB,EAAE,4CAA4C;IACnE,gBAAgB,EAAE,0DAA0D;IAC5E,mBAAmB,EAAE,6DAA6D;IAClF,wBAAwB,EAAE,iEAAiE;IAC3F,uBAAuB,EAAE,oEAAoE;IAE7F,oBAAoB;IACpB,sBAAsB,EAAE,mCAAmC;IAC3D,yBAAyB,EAAE,sCAAsC;IAEjE,aAAa;IACb,eAAe,EAAE,uCAAuC;IACxD,sBAAsB,EAAE,sCAAsC;IAC9D,2EAA2E;IAC3E,sBAAsB,EACpB,iFAAiF;IACnF,eAAe,EAAE,yEAAyE;IAC1F;;;OAGG;IACH,yBAAyB,EACvB,gFAAgF;IAClF,0BAA0B,EAAE,4CAA4C;IACxE;;;;OAIG;IACH,qBAAqB,EACnB,mFAAmF;IACrF;;;;OAIG;IACH,mBAAmB,EAAE,yCAAyC;IAC9D;;;;;OAKG;IACH,mBAAmB,EACjB,4HAA4H;IAC9H;;;OAGG;IACH,2BAA2B,EACzB,mFAAmF;IACrF,mEAAmE;IACnE,wBAAwB,EAAE,4CAA4C;IACtE,uBAAuB,EAAE,mDAAmD;IAC5E,sBAAsB,EAAE,qDAAqD;IAC7E,wBAAwB,EAAE,oDAAoD;IAC9E,8EAA8E;IAC9E,mBAAmB,EAAE,iEAAiE;IAEtF,WAAW;IACX,mBAAmB,EAAE,yBAAyB;IAC9C,yBAAyB;IACzB,oBAAoB,EAAE,mDAAmD;IACzE,kBAAkB,EAAE,0DAA0D;IAC9E,kBAAkB,EAAE,iDAAiD;IACrE,sBAAsB,EAAE,+CAA+C;IACvE,iBAAiB,EAAE,oDAAoD;IACvE,kBAAkB,EAAE,qDAAqD;IACzE,oBAAoB,EAAE,kDAAkD;IACxE,qBAAqB,EAAE,4BAA4B;IACnD,kBAAkB,EAAE,uBAAuB;IAC3C,eAAe,EAAE,6CAA6C;IAC9D;;;OAGG;IACH,gBAAgB,EAAE,+CAA+C;IAEjE,WAAW;IACX,aAAa,EAAE,2CAA2C;IAC1D,cAAc,EAAE,4CAA4C;IAC5D,wEAAwE;IACxE,eAAe,EAAE,gEAAgE;IACjF,uEAAuE;IACvE,UAAU,EAAE,qCAAqC;IACjD,yEAAyE;IACzE,SAAS,EAAE,iEAAiE;IAC5E,8EAA8E;IAC9E,qBAAqB,EAAE,8CAA8C;IACrE,0DAA0D;IAC1D,kBAAkB,EAAE,mDAAmD;IACvE,qBAAqB,EAAE,gDAAgD;IACvE;;;;OAIG;IACH,gBAAgB,EAAE,6CAA6C;IAE/D,8EAA8E;IAC9E,4EAA4E;IAC5E,qBAAqB,EACnB,iFAAiF;IACnF,6DAA6D;IAC7D,mBAAmB,EAAE,0EAA0E;IAC/F,kDAAkD;IAClD,wBAAwB,EAAE,mDAAmD;IAC7E,+DAA+D;IAC/D,8BAA8B,EAAE,uCAAuC;IACvE,sEAAsE;IACtE,oCAAoC,EAClC,6EAA6E;IAC/E,yBAAyB,EAAE,qCAAqC;IAChE,kEAAkE;IAClE,+BAA+B,EAAE,4CAA4C;IAC7E,uBAAuB,EAAE,gDAAgD;IACzE,cAAc,EAAE,oDAAoD;IACpE,uEAAuE;IACvE,WAAW,EAAE,sDAAsD;IACnE,8EAA8E;IAC9E,sBAAsB,EAAE,sCAAsC;IAC9D,kBAAkB,EAAE,6DAA6D;IAEjF;;;;;;;;;;;;;OAaG;IACH,YAAY,EACV,4FAA4F;IAC9F,iBAAiB,EAAE,oEAAoE;IACvF,cAAc,EAAE,6CAA6C;IAC7D,YAAY,EAAE,gEAAgE;IAC9E;;;;;;OAMG;IACH,cAAc,EAAE,sEAAsE;IAEtF,aAAa;IACb,sBAAsB,EAAE,yCAAyC;IACjE,sBAAsB,EAAE,oCAAoC;IAE5D,6BAA6B;IAC7B,aAAa,EAAE,gBAAgB;IAC/B,gBAAgB,EAAE,6BAA6B;IAC/C,aAAa,EAAE,mCAAmC;IAClD,YAAY,EAAE,gCAAgC;IAC9C,mBAAmB,EAAE,0CAA0C;IAC/D,eAAe,EAAE,qCAAqC;IAEtD,UAAU;IACV,iBAAiB,EAAE,gDAAgD;IACnE,kBAAkB,EAAE,iDAAiD;CAC5B,CAAC;AAI5C,iDAAiD;AACjD,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,SAAS,EAAE,CAAC,GAAW,EAAU,EAAE,CAAC,qBAAqB,GAAG,SAAS;IACrE;;;;;;;;OAQG;IACH,qBAAqB,EAAE,CAAC,SAAiB,EAAU,EAAE,CACnD,mCAAmC,SAAS,yCAAyC;IACvF,2DAA2D;IAC3D,aAAa,EAAE,CAAC,OAAe,EAAU,EAAE,CACzC,mCAAmC,OAAO,UAAU,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG;CACvE,CAAC","sourcesContent":["/**\n * gymmonk-schema — API result messages\n * ====================================\n * Single source of truth for every user-facing SUCCESS and ERROR message the\n * product shows, plus the API error codes both sides key off.\n *\n * Why it lives here: the schema package is already the SSOT for data shapes,\n * types and validation. Result copy is part of that contract — the backend\n * raises a message, the client shows it, and neither should invent its own\n * wording. Both repos import from here, so copy changes land in one place and\n * can never drift between server and client.\n *\n * Usage:\n * backend → `throw new BadRequestError(ERROR_MESSAGE.centerAddressRequired)`\n * frontend → `showSuccess(...toast(SUCCESS_MESSAGE.profileUpdated))`\n *\n * @module gymmonk-schema/messages\n */\n\n// ─── API error codes ─────────────────────────────────────────────────────────\n\n/**\n * Machine-readable error codes returned in the API envelope\n * (`{ success: false, error: { code, message } }`). The client can branch on a\n * code; the message is what a human reads.\n */\nexport const ERROR_CODE = {\n // General\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n NOT_FOUND: 'NOT_FOUND',\n\n // Auth\n INVALID_TOKEN: 'INVALID_TOKEN',\n TOKEN_EXPIRED: 'TOKEN_EXPIRED',\n UNAUTHORIZED: 'UNAUTHORIZED',\n FORBIDDEN: 'FORBIDDEN',\n SESSION_INVALIDATED: 'SESSION_INVALIDATED',\n\n // User\n USER_NOT_FOUND: 'USER_NOT_FOUND',\n USER_ALREADY_EXISTS: 'USER_ALREADY_EXISTS',\n INVALID_ROLE: 'INVALID_ROLE',\n\n // Rate limiting\n RATE_LIMITED: 'RATE_LIMITED',\n RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED',\n\n /**\n * The caller MAY do this — their subscription simply does not stretch to it.\n *\n * Deliberately NOT `FORBIDDEN`, and the distinction is load-bearing on the\n * client. A role denial means \"this is not yours\"; the answer is to go away.\n * A plan denial means \"this costs money\"; the answer is an upgrade prompt with\n * the price on it. A client that cannot tell them apart either shows an\n * upgrade sheet to a trainer who will never be allowed to buy one, or shows a\n * bare 403 to an owner who would happily have paid.\n *\n * Always accompanied by `details`: `{ feature, currentPlan, requiredPlan,\n * current?, limit? }` — enough for the prompt to name the feature, the tier\n * that unlocks it, and how full the current allowance is.\n */\n PLAN_LIMIT_EXCEEDED: 'PLAN_LIMIT_EXCEEDED',\n\n // Upstream / dependency unavailable\n SERVICE_UNAVAILABLE: 'SERVICE_UNAVAILABLE',\n} as const;\n\nexport type ErrorCode = (typeof ERROR_CODE)[keyof typeof ERROR_CODE];\n\n// ─── Message shape ───────────────────────────────────────────────────────────\n\n/** A result message: a headline and an optional quieter second line. */\nexport interface ResultMessage {\n title: string;\n description?: string;\n}\n\n// ─── Success messages ────────────────────────────────────────────────────────\n\n/**\n * Confirmation shown after a write succeeds, keyed by action. Interpolating\n * entries are functions so the caller supplies the value without re-wording.\n */\nexport const SUCCESS_MESSAGE = {\n // Onboarding\n memberOnboardingComplete: {\n title: \"You're all set\",\n description: 'Welcome to GymMonk.',\n },\n ownerOnboardingComplete: {\n title: 'Your gym is ready',\n description: 'Welcome to GymMonk.',\n },\n /** Each wizard step is persisted on its own, so each one confirms. */\n onboardingStepSaved: { title: 'Saved' },\n\n // Identity & profile\n profileUpdated: { title: 'Profile updated' },\n organisationUpdated: { title: 'Organisation updated' },\n\n // Gym structure\n centerAdded: { title: 'Center added' },\n centerUpdated: { title: 'Center updated' },\n membershipPlanCreated: { title: 'Membership plan created' },\n membershipPlanUpdated: { title: 'Membership plan updated' },\n equipmentSaved: { title: 'Equipment updated' },\n\n // Membership (member-side)\n membershipUpdated: { title: 'Membership updated' },\n membershipRenewed: { title: 'Membership renewed' },\n membershipCancelled: { title: 'Membership cancelled' },\n membershipPauseRequested: {\n title: 'Pause request sent',\n description: 'Your gym will confirm the pause.',\n },\n\n // Owner SaaS subscription\n subscriptionUpdated: { title: 'Subscription updated' },\n\n // Training\n workoutPlanCreated: { title: 'Workout plan created' },\n workoutPlanUpdated: { title: 'Workout plan saved' },\n workoutPlanDeleted: {\n title: 'Workout plan deleted',\n description: 'Members on this plan no longer have one.',\n },\n workoutCreated: { title: 'Workout created' },\n workoutUpdated: { title: 'Workout saved' },\n workoutDeleted: { title: 'Workout removed' },\n workoutPlanAssigned: { title: 'Workout plan assigned' },\n\n // Diet — the counterparts of the training messages above.\n dietChartCreated: { title: 'Diet chart created' },\n dietChartUpdated: { title: 'Diet chart saved' },\n dietChartDeleted: {\n title: 'Diet chart deleted',\n description: 'Members on this chart no longer have one.',\n },\n dietChartAssigned: { title: 'Diet chart assigned' },\n dietChartCleared: { title: 'Diet chart removed' },\n foodCreated: { title: 'Food added' },\n foodUpdated: { title: 'Food saved' },\n foodDeleted: {\n title: 'Food removed',\n description: 'It no longer appears in any chart that used it.',\n },\n\n // Roster (owner console)\n attendanceMarked: { title: 'Attendance saved' },\n memberMarkedInactive: {\n title: 'Member marked inactive',\n description: 'They can no longer check in.',\n },\n memberMarkedActive: { title: 'Member reactivated' },\n memberDeleted: { title: 'Member removed' },\n /**\n * Says what actually happened. Assigning a plan no longer starts it —\n * nothing does until the payment is marked done — so a bare \"Plan\n * updated\" would send the owner away believing the member could train.\n */\n memberPlanAssigned: {\n title: 'Plan assigned',\n description: 'It starts once you mark the payment done.',\n },\n memberMembershipPaused: { title: 'Membership paused' },\n memberMembershipCancelled: { title: 'Membership cancelled' },\n paymentCollected: { title: 'Payment recorded' },\n\n // Sessions\n checkedIn: { title: 'Checked in', description: 'Your session has started.' },\n checkedOut: { title: 'Checked out', description: 'Session saved.' },\n checkInQrRotated: {\n title: 'New QR generated',\n description: 'Print it and replace the old poster.',\n },\n\n // Membership join requests\n membershipRequestSent: {\n title: 'Request sent',\n description: 'The gym will review it shortly.',\n },\n membershipRequestApproved: {\n title: 'Member approved',\n description: 'They can now check in at your gym.',\n },\n membershipRequestRejected: {\n title: 'Request declined',\n description: 'The member has been told.',\n },\n\n // Membership payment confirmation\n membershipRequested: {\n title: 'Plan requested',\n description: 'It starts once your gym confirms the payment.',\n },\n membershipRequestWithdrawn: {\n title: 'Request withdrawn',\n description: 'Pick another plan whenever you are ready.',\n },\n membershipPaymentConfirmed: {\n title: 'Payment confirmed',\n description: 'The plan is now active for this member.',\n },\n membershipPaymentRejected: {\n title: 'Payment turned down',\n description: 'The member has been told and can send it again.',\n },\n\n // Gym registration + platform review\n gymRegistered: {\n title: 'Gym submitted',\n description: 'Our team will review it and let you know.',\n },\n gymApproved: {\n title: 'Gym approved',\n description: 'It is now listed for members to find.',\n },\n gymRejected: {\n title: 'Gym rejected',\n description: 'The owner has been told.',\n },\n\n // Engagement\n feedbackSubmitted: {\n title: 'Feedback sent',\n description: 'Thank you for helping us improve.',\n },\n complaintSubmitted: {\n title: 'Complaint submitted',\n description: 'Your gym will look into it.',\n },\n announcementSent: { title: 'Announcement sent' },\n announcementScheduled: { title: 'Announcement scheduled' },\n announcementDraftSaved: { title: 'Draft saved' },\n announcementDeleted: { title: 'Announcement deleted' },\n announcementCancelled: {\n title: 'Announcement cancelled',\n description: 'It will not be sent.',\n },\n\n // Notification feed\n notificationHidden: { title: 'Notification hidden' },\n notificationRestored: { title: 'Notification restored' },\n allNotificationsRead: { title: 'All caught up' },\n\n // Security (role-switch PIN)\n mpinSet: { title: 'PIN set' },\n mpinReset: { title: 'Your PIN has been reset.' },\n mpinResetLinkSent: {\n title: \"We've emailed you a reset link\",\n description: 'Check your inbox.',\n },\n\n // ── Platform console (superadmin / platform manager) ────────────────────────\n platformRolesUpdated: { title: 'Platform roles updated' },\n platformUserSuspended: {\n title: 'Account suspended',\n description: 'They can no longer sign in.',\n },\n platformUserReinstated: { title: 'Account reinstated' },\n platformUserDeleted: {\n title: 'Account deleted',\n description: 'It is hidden everywhere but can still be restored.',\n },\n platformUserRestored: { title: 'Account restored' },\n platformGymDeleted: {\n title: 'Gym deleted',\n description: 'It is hidden everywhere but can still be restored.',\n },\n platformGymRestored: {\n title: 'Gym restored',\n description: 'It is back in the directory and its owner has the console again.',\n },\n} as const satisfies Record<string, ResultMessage>;\n\nexport type SuccessMessageKey = keyof typeof SUCCESS_MESSAGE;\n\n/** Success messages that name the thing they acted on. */\nexport const SUCCESS_MESSAGE_FOR = {\n memberAdded: (name: string): ResultMessage => ({\n title: 'Member added',\n description: `Share the invite with ${name}.`,\n }),\n staffAdded: (name: string): ResultMessage => ({\n title: 'Staff added',\n description: `Share the invite with ${name}.`,\n }),\n inviteClaimed: (gymName: string): ResultMessage => ({\n title: \"You're all set\",\n description: `Your account is linked to ${gymName}.`,\n }),\n roleSwitched: (roleLabel: string): ResultMessage => ({\n title: `Switched to ${roleLabel}`,\n }),\n gymApprovedFor: (gymName: string): ResultMessage => ({\n title: 'Gym approved',\n description: `${gymName} is now live on GymMonk.`,\n }),\n gymRejectedFor: (gymName: string): ResultMessage => ({\n title: 'Gym rejected',\n description: `The owner of ${gymName} has been told why.`,\n }),\n} as const;\n\n// ─── Error messages ──────────────────────────────────────────────────────────\n\n/**\n * Failure copy. Transport entries describe a request that never reached the\n * API; the rest are domain rules the backend enforces and the client can also\n * check up-front — same wording either way.\n */\nexport const ERROR_MESSAGE = {\n // Transport / generic\n generic: 'Something went wrong. Please try again.',\n network: 'Network error. Check your connection and try again.',\n timeout: 'That took too long. Please try again.',\n sessionExpired: 'Your session expired. Please sign in again.',\n\n // Client-side validation\n formInvalid: 'Please check the highlighted fields.',\n\n // Onboarding\n roleRequired: 'Choose a role to continue',\n ownerOnboardingFailed: 'We could not create your gym. Please try again.',\n memberOnboardingFailed: 'We could not save your details. Please try again.',\n\n // ── Gym registration (\"list your gym\") ─────────────────────────────────────\n gymRegistrationFailed: 'We could not register your gym. Please try again.',\n /**\n * The wizard could not read what the owner had already saved.\n *\n * It refuses to open on a blank form rather than risk the next Continue\n * overwriting a real draft, so this is the whole screen, not a toast.\n */\n gymRegistrationDraftLoadFailed:\n 'We could not open your saved gym details. Check your connection and try again.',\n /**\n * The step the wizard tried to save is implausibly large.\n *\n * A guard against a client sending something absurd rather than a limit the\n * real form approaches, but it is raised by the SERVER and shown verbatim in\n * the step-save toast — so the wording lives here with the rest of the copy,\n * not as a hand-written string at the throw site.\n */\n gymRegistrationDraftTooLarge:\n 'Those gym details are too large to save. Please shorten the descriptions and try again.',\n /**\n * \"Near me\" found nothing inside the radius.\n *\n * Deliberately not the same as `gymSearchNoResults`: this member searched\n * nowhere in particular, so telling them to refine their search would be\n * advice they cannot act on. What helps is a different place or a gym id.\n */\n gymSearchNoneNearby:\n 'No gyms within 50 km of you yet. Try a different location, or enter a gym id if you have one.',\n /** One organisation per account — registering a second is not supported. */\n gymAlreadyRegistered: 'You have already registered a gym on this account.',\n /** Registering a gym is something a MEMBER does, so the profile must exist. */\n gymRegistrationNeedsProfile: 'Finish setting up your profile before registering a gym.',\n /** Every owner action other than editing their own details, before approval. */\n gymAwaitingApproval:\n 'Your gym is waiting for approval. You can manage it once our team has reviewed it.',\n /** The reviewer turned the registration down. */\n gymRegistrationRejected: 'Your gym registration was not approved.',\n organisationNotFound: 'That gym could not be found.',\n\n // ── Platform console ────────────────────────────────────────────────────────\n /** The account is gone, or was already erased. */\n platformUserNotFound: 'That account could not be found.',\n /**\n * An admin acting on their OWN row. Suspending or deleting yourself takes away\n * the access needed to undo it, so the console refuses rather than letting one\n * mis-tap lock the platform's last superadmin out of it.\n */\n platformCannotActOnSelf: 'You cannot do that to your own account.',\n /**\n * Removing the last superadmin. `manage GymApproval` is held by nobody else,\n * so the gym review queue would become unreachable with no way back in short\n * of a database edit.\n */\n platformLastSuperadmin: 'The platform must keep at least one superadmin.',\n /** The typed confirmation did not match the record being deleted. */\n platformConfirmationMismatch: 'That did not match. Nothing was changed.',\n /** Two reviewers acting on the same registration; the second one loses. */\n organisationAlreadyDecided: 'Someone has already reviewed this gym.',\n /** A step failed to persist — the user stays put so nothing is lost. */\n onboardingStepSaveFailed: 'We could not save this step. Please try again.',\n\n // Gym structure prerequisites\n centerRequiredForPlan: 'Pick the center this plan belongs to.',\n planVariantRequired: 'Add at least one pricing variant before saving the plan.',\n planNameTaken: 'This center already has a plan with that name. Pick another name.',\n planVariantDurationRequired: 'Enter how long this variant runs for',\n planVariantPriceRequired: 'Enter the price for this variant',\n /** A \"discount\" that raises the price is a typo, every time. */\n planFinalPriceAboveOriginal: 'The final price cannot be more than the original price',\n centerAddressRequired: 'Add the center address before creating it.',\n noCenterForPlans: 'No gym center found. Add a center before creating plans.',\n noCenterForWorkouts: 'No gym center found. Add a center before creating workouts.',\n noCenterForAnnouncements: 'No gym center found. Add a center before sending announcements.',\n announcementAlreadySent: 'That announcement has already gone out, so it cannot be cancelled.',\n\n // Notification feed\n notificationHideFailed: 'Could not hide that notification.',\n notificationRestoreFailed: 'Could not restore that notification.',\n\n // Membership\n planUnavailable: 'That plan is not available right now.',\n membershipPlanRequired: 'Choose a membership plan to continue',\n /** The member was created, but the plan the owner picked did not stick. */\n initialPlanNotAssigned:\n 'Member added, but the plan could not be assigned. Assign it from their profile.',\n noPlansToAssign: 'No membership plans yet. Create one to assign it while adding a member.',\n /**\n * Not an error the member can act on by trying harder — it is the whole\n * point of the flow, so it reads as a status rather than a refusal.\n */\n membershipAwaitingPayment:\n 'Your gym is checking your payment. The plan starts as soon as they confirm it.',\n membershipNoPendingRequest: 'There is no plan waiting for confirmation.',\n /**\n * Pausing extends the term by the paused days, so pausing something that is\n * not running would push out a date nobody is training against — and pausing\n * an already-paused membership would extend it twice for one absence.\n */\n membershipNotPausable:\n 'Only a running membership can be paused. This one has already ended or is paused.',\n /**\n * The joining date records something that has already happened, so a future\n * one is not a preference to honour — it would date the member's whole\n * billing cycle from a day that has not arrived.\n */\n joiningDateInFuture: 'A joining date cannot be in the future.',\n /**\n * The term moved between the screen being drawn and the button being\n * pressed, which in practice means a second manager confirmed one first.\n * Says which month it now covers rather than a bare \"try again\", because the\n * owner's next question is always \"then what am I paying for?\".\n */\n membershipTermMoved:\n 'Someone just confirmed a payment for this member, so the period has moved on. Reopen their profile to see the current one.',\n /**\n * The owner reached for the roster toggle on a plan that has not been paid\n * for. Points at the action that WOULD start it rather than just refusing.\n */\n membershipPendingUseConfirm:\n 'This plan is waiting for payment confirmation. Mark the payment done to start it.',\n /** Two managers opening the same request; the second one loses. */\n membershipAlreadyDecided: 'Someone has already answered this payment.',\n membershipConfirmFailed: 'Could not confirm that payment. Please try again.',\n membershipRejectFailed: 'Could not turn down that payment. Please try again.',\n membershipWithdrawFailed: 'Could not withdraw that request. Please try again.',\n /** The URL did not come from our own storage — a broken or hostile client. */\n paymentProofInvalid: 'That payment screenshot could not be attached. Upload it again.',\n\n // Training\n workoutPlanRequired: 'Choose a plan to assign',\n // Roster (owner console)\n attendanceMarkFailed: 'Could not save that attendance. Please try again.',\n memberStatusFailed: \"Could not change that member's status. Please try again.\",\n memberDeleteFailed: 'Could not remove that member. Please try again.',\n memberPlanAssignFailed: 'Could not update that plan. Please try again.',\n memberPauseFailed: 'Could not pause that membership. Please try again.',\n memberCancelFailed: 'Could not cancel that membership. Please try again.',\n paymentCollectFailed: 'Could not record that payment. Please try again.',\n paymentAmountRequired: 'Enter the amount collected',\n pauseDatesRequired: 'Pick both pause dates',\n pauseDatesOrder: 'The end date has to be after the start date',\n /**\n * Attendance, workout plans and payments are all keyed on the central-auth\n * user, which an invited record does not have until the person claims it.\n */\n memberNotClaimed: 'This member has not claimed their invite yet.',\n\n // Sessions\n checkInFailed: 'Could not check you in. Please try again.',\n checkOutFailed: 'Could not check you out. Please try again.',\n /** The camera decoded a QR, but it is not a gymmonk check-in poster. */\n qrNotRecognised: 'That is not a gym check-in QR. Point at the poster at the gym.',\n /** A valid gymmonk QR, but for a gym the member does not belong to. */\n qrWrongGym: 'That QR belongs to a different gym.',\n /** The scanned code matches no center — a retired or mistyped poster. */\n qrUnknown: 'This QR is no longer valid. Ask the gym for the current poster.',\n /** Checking out with a poster from somewhere other than where you started. */\n qrWrongGymForCheckOut: 'Scan the QR at the gym where you checked in.',\n /** Manual entry only accepts a full, well-formed code. */\n checkInCodeInvalid: 'Enter the 16-character code printed under the QR.',\n checkInQrRotateFailed: 'Could not generate a new QR. Please try again.',\n /**\n * Ticking an exercise off records it against a live session, so there has to\n * be one. Shown when a member opens an exercise outside a workout and taps\n * Mark As Done.\n */\n noSessionToTrack: 'Check in at your gym to track this workout.',\n\n // ── Membership join requests ───────────────────────────────────────────────\n /** Scanned a valid poster, but the gym has not accepted this member yet. */\n membershipNotApproved:\n 'Your gym has not accepted your join request yet. You can check in once they do.',\n /** Accepted, but with no live plan — check-in needs both. */\n membershipNotActive: 'Your membership plan is not active. Renew it to start checking in again.',\n /** A second request while one is already open. */\n membershipRequestPending: 'You already have a request waiting with this gym.',\n /** Requesting a gym they are already an accepted member of. */\n membershipRequestAlreadyMember: 'You are already a member of this gym.',\n /** v1 memberships are single-center — leave the current gym first. */\n membershipRequestAlreadyAtAnotherGym:\n 'You already belong to a gym. Cancel that membership before joining another.',\n membershipRequestNotFound: 'That join request no longer exists.',\n /** Two staff acting on the same request; the second one loses. */\n membershipRequestAlreadyDecided: 'Someone has already answered this request.',\n membershipRequestFailed: 'Could not send your request. Please try again.',\n gymCodeUnknown: 'No gym found with that id. Check it with your gym.',\n /** Actions that only make sense once a gym has taken the member on. */\n noGymJoined: 'Join a gym first. You can find one from your Me tab.',\n /** Searching by name needs somewhere to look — see `gymSearchQuerySchema`. */\n gymSearchStateRequired: 'Pick a state to search gyms by name.',\n gymSearchNoResults: 'No gyms matched. Try a different area, or enter the gym id.',\n\n /**\n * Camera (QR scanning)\n *\n * ── THESE USED TO POINT AT A BOX THAT DOES NOT EXIST ──────────────────────\n * Two of them ended \"Enter the code printed under the QR\", and both halves of\n * that sentence were wrong: the scanner has no field to type a code into, and\n * the poster does not print the check-in code at all — it prints the gym's\n * PUBLIC join code, which cannot check anybody in. So a member whose camera\n * failed was sent looking for a box that is not there, to type a number that\n * is not printed.\n *\n * They now point at the thing that IS there: the check-in button under the\n * viewfinder, which needs no code at all.\n */\n cameraDenied:\n 'Camera access is blocked. Allow it in your browser settings, or check in without scanning.',\n cameraUnavailable: 'No camera found on this device. Check in without scanning instead.',\n cameraInsecure: 'Scanning needs a secure (https) connection.',\n cameraFailed: 'Could not start the camera. Check in without scanning instead.',\n /**\n * The member is not attached to a gym, on the codeless path.\n *\n * Its own sentence because that path reached for `qrUnknown` before it had\n * one, and that string opens \"This QR is no longer valid\" — advice about a\n * poster, given to somebody who never scanned one.\n */\n gymUnavailable: 'You are not checked in to a gym yet. Join one from the Me tab first.',\n\n // Engagement\n feedbackRatingRequired: 'Choose a rating to submit your feedback',\n complaintIssueRequired: 'Choose the issue you are reporting',\n\n // Security (role-switch PIN)\n mpinIncorrect: 'Incorrect PIN.',\n mpinVerifyFailed: \"Couldn't verify. Try again.\",\n mpinSetFailed: \"Couldn't set your PIN. Try again.\",\n mpinMismatch: \"Those didn't match. Try again.\",\n mpinResetLinkFailed: \"Couldn't send the reset link. Try again.\",\n mpinResetFailed: \"Couldn't reset your PIN. Try again.\",\n\n // Uploads\n imageUploadFailed: 'Could not upload that image. Please try again.',\n imageProcessFailed: 'Could not process that image. Please try again.',\n} as const satisfies Record<string, string>;\n\nexport type ErrorMessageKey = keyof typeof ERROR_MESSAGE;\n\n/** Error messages that name a limit or value. */\nexport const ERROR_MESSAGE_FOR = {\n maxPhotos: (max: number): string => `You can add up to ${max} photos`,\n /**\n * Refused at the door because the membership is paused and still inside its\n * window.\n *\n * Its own sentence because \"renew your plan\" is wrong advice here twice\n * over: nothing has lapsed, and paying again would not open the door a day\n * sooner. The resume date is the only thing this member wants to know, and\n * it is the reason this one takes a parameter at all.\n */\n membershipPausedUntil: (resumesOn: string): string =>\n `Your membership is paused until ${resumesOn}. You can check in again from that day.`,\n /** Role-switch PIN lockout, in whole minutes remaining. */\n mpinLockedOut: (minutes: number): string =>\n `Too many attempts. Try again in ${minutes} minute${minutes > 1 ? 's' : ''}.`,\n} as const;\n"]}
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — API result messages\n * ====================================\n * Single source of truth for every user-facing SUCCESS and ERROR message the\n * product shows, plus the API error codes both sides key off.\n *\n * Why it lives here: the schema package is already the SSOT for data shapes,\n * types and validation. Result copy is part of that contract — the backend\n * raises a message, the client shows it, and neither should invent its own\n * wording. Both repos import from here, so copy changes land in one place and\n * can never drift between server and client.\n *\n * Usage:\n * backend → `throw new BadRequestError(ERROR_MESSAGE.centerAddressRequired)`\n * frontend → `showSuccess(...toast(SUCCESS_MESSAGE.profileUpdated))`\n *\n * @module gymmonk-schema/messages\n */\n\n// ─── API error codes ─────────────────────────────────────────────────────────\n\n/**\n * Machine-readable error codes returned in the API envelope\n * (`{ success: false, error: { code, message } }`). The client can branch on a\n * code; the message is what a human reads.\n */\nexport const ERROR_CODE = {\n // General\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n NOT_FOUND: 'NOT_FOUND',\n\n // Auth\n INVALID_TOKEN: 'INVALID_TOKEN',\n TOKEN_EXPIRED: 'TOKEN_EXPIRED',\n UNAUTHORIZED: 'UNAUTHORIZED',\n FORBIDDEN: 'FORBIDDEN',\n SESSION_INVALIDATED: 'SESSION_INVALIDATED',\n\n // User\n USER_NOT_FOUND: 'USER_NOT_FOUND',\n USER_ALREADY_EXISTS: 'USER_ALREADY_EXISTS',\n INVALID_ROLE: 'INVALID_ROLE',\n\n // Rate limiting\n RATE_LIMITED: 'RATE_LIMITED',\n RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED',\n\n /**\n * The caller MAY do this — their subscription simply does not stretch to it.\n *\n * Deliberately NOT `FORBIDDEN`, and the distinction is load-bearing on the\n * client. A role denial means \"this is not yours\"; the answer is to go away.\n * A plan denial means \"this costs money\"; the answer is an upgrade prompt with\n * the price on it. A client that cannot tell them apart either shows an\n * upgrade sheet to a trainer who will never be allowed to buy one, or shows a\n * bare 403 to an owner who would happily have paid.\n *\n * Always accompanied by `details`: `{ feature, currentPlan, requiredPlan,\n * current?, limit? }` — enough for the prompt to name the feature, the tier\n * that unlocks it, and how full the current allowance is.\n */\n PLAN_LIMIT_EXCEEDED: 'PLAN_LIMIT_EXCEEDED',\n\n // Upstream / dependency unavailable\n SERVICE_UNAVAILABLE: 'SERVICE_UNAVAILABLE',\n} as const;\n\nexport type ErrorCode = (typeof ERROR_CODE)[keyof typeof ERROR_CODE];\n\n// ─── Message shape ───────────────────────────────────────────────────────────\n\n/** A result message: a headline and an optional quieter second line. */\nexport interface ResultMessage {\n title: string;\n description?: string;\n}\n\n// ─── Success messages ────────────────────────────────────────────────────────\n\n/**\n * Confirmation shown after a write succeeds, keyed by action. Interpolating\n * entries are functions so the caller supplies the value without re-wording.\n */\nexport const SUCCESS_MESSAGE = {\n // Onboarding\n memberOnboardingComplete: {\n title: \"You're all set\",\n description: 'Welcome to GymMonk.',\n },\n ownerOnboardingComplete: {\n title: 'Your gym is ready',\n description: 'Welcome to GymMonk.',\n },\n /** Each wizard step is persisted on its own, so each one confirms. */\n onboardingStepSaved: { title: 'Saved' },\n\n // Identity & profile\n profileUpdated: { title: 'Profile updated' },\n organisationUpdated: { title: 'Organisation updated' },\n\n // Gym structure\n centerAdded: { title: 'Center added' },\n centerUpdated: { title: 'Center updated' },\n membershipPlanCreated: { title: 'Membership plan created' },\n membershipPlanUpdated: { title: 'Membership plan updated' },\n equipmentSaved: { title: 'Equipment updated' },\n\n // Membership (member-side)\n membershipUpdated: { title: 'Membership updated' },\n membershipRenewed: { title: 'Membership renewed' },\n membershipCancelled: { title: 'Membership cancelled' },\n membershipPauseRequested: {\n title: 'Pause request sent',\n description: 'Your gym will confirm the pause.',\n },\n\n // Owner SaaS subscription\n subscriptionUpdated: { title: 'Subscription updated' },\n\n // Training\n workoutPlanCreated: { title: 'Workout plan created' },\n workoutPlanUpdated: { title: 'Workout plan saved' },\n workoutPlanDeleted: {\n title: 'Workout plan deleted',\n description: 'Members on this plan no longer have one.',\n },\n workoutCreated: { title: 'Workout created' },\n workoutUpdated: { title: 'Workout saved' },\n workoutDeleted: { title: 'Workout removed' },\n workoutPlanAssigned: { title: 'Workout plan assigned' },\n\n // Diet — the counterparts of the training messages above.\n dietChartCreated: { title: 'Diet chart created' },\n dietChartUpdated: { title: 'Diet chart saved' },\n dietChartDeleted: {\n title: 'Diet chart deleted',\n description: 'Members on this chart no longer have one.',\n },\n dietChartAssigned: { title: 'Diet chart assigned' },\n dietChartCleared: { title: 'Diet chart removed' },\n foodCreated: { title: 'Food added' },\n foodUpdated: { title: 'Food saved' },\n foodDeleted: {\n title: 'Food removed',\n description: 'It no longer appears in any chart that used it.',\n },\n\n // Roster (owner console)\n attendanceMarked: { title: 'Attendance saved' },\n memberMarkedInactive: {\n title: 'Member marked inactive',\n description: 'They can no longer check in.',\n },\n memberMarkedActive: { title: 'Member reactivated' },\n memberDeleted: { title: 'Member removed' },\n /**\n * Says what actually happened. Assigning a plan no longer starts it —\n * nothing does until the payment is marked done — so a bare \"Plan\n * updated\" would send the owner away believing the member could train.\n */\n memberPlanAssigned: {\n title: 'Plan assigned',\n description: 'It starts once you mark the payment done.',\n },\n memberMembershipPaused: { title: 'Membership paused' },\n memberMembershipCancelled: { title: 'Membership cancelled' },\n paymentCollected: { title: 'Payment recorded' },\n\n // Sessions\n checkedIn: { title: 'Checked in', description: 'Your session has started.' },\n checkedOut: { title: 'Checked out', description: 'Session saved.' },\n checkInQrRotated: {\n title: 'New QR generated',\n description: 'Print it and replace the old poster.',\n },\n\n // Membership join requests\n membershipRequestSent: {\n title: 'Request sent',\n description: 'The gym will review it shortly.',\n },\n membershipRequestApproved: {\n title: 'Member approved',\n description: 'They can now check in at your gym.',\n },\n membershipRequestRejected: {\n title: 'Request declined',\n description: 'The member has been told.',\n },\n\n // Membership payment confirmation\n membershipRequested: {\n title: 'Plan requested',\n description: 'It starts once your gym confirms the payment.',\n },\n membershipRequestWithdrawn: {\n title: 'Request withdrawn',\n description: 'Pick another plan whenever you are ready.',\n },\n /**\n * Says the gym can SEE it, not that the plan has started. The member has just\n * done the only thing left to them and the temptation is to congratulate them\n * with something that sounds like confirmation; the next move is still the\n * gym's, and the screen they are looking at says so.\n */\n membershipProofAttached: {\n title: 'Screenshot sent',\n description: 'Your gym can see it now.',\n },\n membershipPaymentConfirmed: {\n title: 'Payment confirmed',\n description: 'The plan is now active for this member.',\n },\n membershipPaymentRejected: {\n title: 'Payment turned down',\n description: 'The member has been told and can send it again.',\n },\n\n // Gym registration + platform review\n gymRegistered: {\n title: 'Gym submitted',\n description: 'Our team will review it and let you know.',\n },\n gymApproved: {\n title: 'Gym approved',\n description: 'It is now listed for members to find.',\n },\n gymRejected: {\n title: 'Gym rejected',\n description: 'The owner has been told.',\n },\n\n // Engagement\n feedbackSubmitted: {\n title: 'Feedback sent',\n description: 'Thank you for helping us improve.',\n },\n complaintSubmitted: {\n title: 'Complaint submitted',\n description: 'Your gym will look into it.',\n },\n announcementSent: { title: 'Announcement sent' },\n announcementScheduled: { title: 'Announcement scheduled' },\n announcementDraftSaved: { title: 'Draft saved' },\n announcementDeleted: { title: 'Announcement deleted' },\n announcementCancelled: {\n title: 'Announcement cancelled',\n description: 'It will not be sent.',\n },\n\n // Notification feed\n notificationHidden: { title: 'Notification hidden' },\n notificationRestored: { title: 'Notification restored' },\n allNotificationsRead: { title: 'All caught up' },\n\n // Security (role-switch PIN)\n mpinSet: { title: 'PIN set' },\n mpinReset: { title: 'Your PIN has been reset.' },\n mpinResetLinkSent: {\n title: \"We've emailed you a reset link\",\n description: 'Check your inbox.',\n },\n\n // ── Platform console (superadmin / platform manager) ────────────────────────\n platformRolesUpdated: { title: 'Platform roles updated' },\n platformUserSuspended: {\n title: 'Account suspended',\n description: 'They can no longer sign in.',\n },\n platformUserReinstated: { title: 'Account reinstated' },\n platformUserDeleted: {\n title: 'Account deleted',\n description: 'It is hidden everywhere but can still be restored.',\n },\n platformUserRestored: { title: 'Account restored' },\n platformGymDeleted: {\n title: 'Gym deleted',\n description: 'It is hidden everywhere but can still be restored.',\n },\n platformGymRestored: {\n title: 'Gym restored',\n description: 'It is back in the directory and its owner has the console again.',\n },\n} as const satisfies Record<string, ResultMessage>;\n\nexport type SuccessMessageKey = keyof typeof SUCCESS_MESSAGE;\n\n/** Success messages that name the thing they acted on. */\nexport const SUCCESS_MESSAGE_FOR = {\n memberAdded: (name: string): ResultMessage => ({\n title: 'Member added',\n description: `Share the invite with ${name}.`,\n }),\n staffAdded: (name: string): ResultMessage => ({\n title: 'Staff added',\n description: `Share the invite with ${name}.`,\n }),\n inviteClaimed: (gymName: string): ResultMessage => ({\n title: \"You're all set\",\n description: `Your account is linked to ${gymName}.`,\n }),\n roleSwitched: (roleLabel: string): ResultMessage => ({\n title: `Switched to ${roleLabel}`,\n }),\n gymApprovedFor: (gymName: string): ResultMessage => ({\n title: 'Gym approved',\n description: `${gymName} is now live on GymMonk.`,\n }),\n gymRejectedFor: (gymName: string): ResultMessage => ({\n title: 'Gym rejected',\n description: `The owner of ${gymName} has been told why.`,\n }),\n} as const;\n\n// ─── Error messages ──────────────────────────────────────────────────────────\n\n/**\n * Failure copy. Transport entries describe a request that never reached the\n * API; the rest are domain rules the backend enforces and the client can also\n * check up-front — same wording either way.\n */\nexport const ERROR_MESSAGE = {\n // Transport / generic\n generic: 'Something went wrong. Please try again.',\n network: 'Network error. Check your connection and try again.',\n timeout: 'That took too long. Please try again.',\n sessionExpired: 'Your session expired. Please sign in again.',\n\n // Client-side validation\n formInvalid: 'Please check the highlighted fields.',\n\n // Onboarding\n roleRequired: 'Choose a role to continue',\n ownerOnboardingFailed: 'We could not create your gym. Please try again.',\n memberOnboardingFailed: 'We could not save your details. Please try again.',\n\n // ── Gym registration (\"list your gym\") ─────────────────────────────────────\n gymRegistrationFailed: 'We could not register your gym. Please try again.',\n /**\n * The wizard could not read what the owner had already saved.\n *\n * It refuses to open on a blank form rather than risk the next Continue\n * overwriting a real draft, so this is the whole screen, not a toast.\n */\n gymRegistrationDraftLoadFailed:\n 'We could not open your saved gym details. Check your connection and try again.',\n /**\n * The step the wizard tried to save is implausibly large.\n *\n * A guard against a client sending something absurd rather than a limit the\n * real form approaches, but it is raised by the SERVER and shown verbatim in\n * the step-save toast — so the wording lives here with the rest of the copy,\n * not as a hand-written string at the throw site.\n */\n gymRegistrationDraftTooLarge:\n 'Those gym details are too large to save. Please shorten the descriptions and try again.',\n /**\n * \"Near me\" found nothing inside the radius.\n *\n * Deliberately not the same as `gymSearchNoResults`: this member searched\n * nowhere in particular, so telling them to refine their search would be\n * advice they cannot act on. What helps is a different place or a gym id.\n */\n gymSearchNoneNearby:\n 'No gyms within 50 km of you yet. Try a different location, or enter a gym id if you have one.',\n /** One organisation per account — registering a second is not supported. */\n gymAlreadyRegistered: 'You have already registered a gym on this account.',\n /** Registering a gym is something a MEMBER does, so the profile must exist. */\n gymRegistrationNeedsProfile: 'Finish setting up your profile before registering a gym.',\n /** Every owner action other than editing their own details, before approval. */\n gymAwaitingApproval:\n 'Your gym is waiting for approval. You can manage it once our team has reviewed it.',\n /** The reviewer turned the registration down. */\n gymRegistrationRejected: 'Your gym registration was not approved.',\n organisationNotFound: 'That gym could not be found.',\n\n // ── Platform console ────────────────────────────────────────────────────────\n /** The account is gone, or was already erased. */\n platformUserNotFound: 'That account could not be found.',\n /**\n * An admin acting on their OWN row. Suspending or deleting yourself takes away\n * the access needed to undo it, so the console refuses rather than letting one\n * mis-tap lock the platform's last superadmin out of it.\n */\n platformCannotActOnSelf: 'You cannot do that to your own account.',\n /**\n * Removing the last superadmin. `manage GymApproval` is held by nobody else,\n * so the gym review queue would become unreachable with no way back in short\n * of a database edit.\n */\n platformLastSuperadmin: 'The platform must keep at least one superadmin.',\n /** The typed confirmation did not match the record being deleted. */\n platformConfirmationMismatch: 'That did not match. Nothing was changed.',\n /** Two reviewers acting on the same registration; the second one loses. */\n organisationAlreadyDecided: 'Someone has already reviewed this gym.',\n /** A step failed to persist — the user stays put so nothing is lost. */\n onboardingStepSaveFailed: 'We could not save this step. Please try again.',\n\n // Gym structure prerequisites\n centerRequiredForPlan: 'Pick the center this plan belongs to.',\n planVariantRequired: 'Add at least one pricing variant before saving the plan.',\n planNameTaken: 'This center already has a plan with that name. Pick another name.',\n planVariantDurationRequired: 'Enter how long this variant runs for',\n planVariantPriceRequired: 'Enter the price for this variant',\n /** A \"discount\" that raises the price is a typo, every time. */\n planFinalPriceAboveOriginal: 'The final price cannot be more than the original price',\n centerAddressRequired: 'Add the center address before creating it.',\n noCenterForPlans: 'No gym center found. Add a center before creating plans.',\n noCenterForWorkouts: 'No gym center found. Add a center before creating workouts.',\n noCenterForAnnouncements: 'No gym center found. Add a center before sending announcements.',\n announcementAlreadySent: 'That announcement has already gone out, so it cannot be cancelled.',\n\n // Notification feed\n notificationHideFailed: 'Could not hide that notification.',\n notificationRestoreFailed: 'Could not restore that notification.',\n\n // Membership\n planUnavailable: 'That plan is not available right now.',\n membershipPlanRequired: 'Choose a membership plan to continue',\n /** The member was created, but the plan the owner picked did not stick. */\n initialPlanNotAssigned:\n 'Member added, but the plan could not be assigned. Assign it from their profile.',\n noPlansToAssign: 'No membership plans yet. Create one to assign it while adding a member.',\n /**\n * Not an error the member can act on by trying harder — it is the whole\n * point of the flow, so it reads as a status rather than a refusal.\n */\n membershipAwaitingPayment:\n 'Your gym is checking your payment. The plan starts as soon as they confirm it.',\n membershipNoPendingRequest: 'There is no plan waiting for confirmation.',\n /**\n * Pausing extends the term by the paused days, so pausing something that is\n * not running would push out a date nobody is training against — and pausing\n * an already-paused membership would extend it twice for one absence.\n */\n membershipNotPausable:\n 'Only a running membership can be paused. This one has already ended or is paused.',\n /**\n * The joining date records something that has already happened, so a future\n * one is not a preference to honour — it would date the member's whole\n * billing cycle from a day that has not arrived.\n */\n joiningDateInFuture: 'A joining date cannot be in the future.',\n /**\n * The term moved between the screen being drawn and the button being\n * pressed, which in practice means a second manager confirmed one first.\n * Says which month it now covers rather than a bare \"try again\", because the\n * owner's next question is always \"then what am I paying for?\".\n */\n membershipTermMoved:\n 'Someone just confirmed a payment for this member, so the period has moved on. Reopen their profile to see the current one.',\n /**\n * The owner reached for the roster toggle on a plan that has not been paid\n * for. Points at the action that WOULD start it rather than just refusing.\n */\n membershipPendingUseConfirm:\n 'This plan is waiting for payment confirmation. Mark the payment done to start it.',\n /** Two managers opening the same request; the second one loses. */\n membershipAlreadyDecided: 'Someone has already answered this payment.',\n membershipConfirmFailed: 'Could not confirm that payment. Please try again.',\n membershipRejectFailed: 'Could not turn down that payment. Please try again.',\n membershipWithdrawFailed: 'Could not withdraw that request. Please try again.',\n /** The URL did not come from our own storage — a broken or hostile client. */\n paymentProofInvalid: 'That payment screenshot could not be attached. Upload it again.',\n /** The upload itself worked; saving it against the request did not. */\n paymentProofAttachFailed: 'Could not send that screenshot. Please try again.',\n\n // Training\n workoutPlanRequired: 'Choose a plan to assign',\n // Roster (owner console)\n attendanceMarkFailed: 'Could not save that attendance. Please try again.',\n memberStatusFailed: \"Could not change that member's status. Please try again.\",\n memberDeleteFailed: 'Could not remove that member. Please try again.',\n memberPlanAssignFailed: 'Could not update that plan. Please try again.',\n memberPauseFailed: 'Could not pause that membership. Please try again.',\n memberCancelFailed: 'Could not cancel that membership. Please try again.',\n paymentCollectFailed: 'Could not record that payment. Please try again.',\n paymentAmountRequired: 'Enter the amount collected',\n pauseDatesRequired: 'Pick both pause dates',\n pauseDatesOrder: 'The end date has to be after the start date',\n /**\n * Attendance, workout plans and payments are all keyed on the central-auth\n * user, which an invited record does not have until the person claims it.\n */\n memberNotClaimed: 'This member has not claimed their invite yet.',\n\n // Sessions\n checkInFailed: 'Could not check you in. Please try again.',\n checkOutFailed: 'Could not check you out. Please try again.',\n /** The camera decoded a QR, but it is not a gymmonk check-in poster. */\n qrNotRecognised: 'That is not a gym check-in QR. Point at the poster at the gym.',\n /** A valid gymmonk QR, but for a gym the member does not belong to. */\n qrWrongGym: 'That QR belongs to a different gym.',\n /** The scanned code matches no center — a retired or mistyped poster. */\n qrUnknown: 'This QR is no longer valid. Ask the gym for the current poster.',\n /** Checking out with a poster from somewhere other than where you started. */\n qrWrongGymForCheckOut: 'Scan the QR at the gym where you checked in.',\n /** Manual entry only accepts a full, well-formed code. */\n checkInCodeInvalid: 'Enter the 16-character code printed under the QR.',\n checkInQrRotateFailed: 'Could not generate a new QR. Please try again.',\n /**\n * Ticking an exercise off records it against a live session, so there has to\n * be one. Shown when a member opens an exercise outside a workout and taps\n * Mark As Done.\n */\n noSessionToTrack: 'Check in at your gym to track this workout.',\n\n // ── Membership join requests ───────────────────────────────────────────────\n /** Scanned a valid poster, but the gym has not accepted this member yet. */\n membershipNotApproved:\n 'Your gym has not accepted your join request yet. You can check in once they do.',\n /** Accepted, but with no live plan — check-in needs both. */\n membershipNotActive: 'Your membership plan is not active. Renew it to start checking in again.',\n /** A second request while one is already open. */\n membershipRequestPending: 'You already have a request waiting with this gym.',\n /** Requesting a gym they are already an accepted member of. */\n membershipRequestAlreadyMember: 'You are already a member of this gym.',\n /** v1 memberships are single-center — leave the current gym first. */\n membershipRequestAlreadyAtAnotherGym:\n 'You already belong to a gym. Cancel that membership before joining another.',\n membershipRequestNotFound: 'That join request no longer exists.',\n /** Two staff acting on the same request; the second one loses. */\n membershipRequestAlreadyDecided: 'Someone has already answered this request.',\n membershipRequestFailed: 'Could not send your request. Please try again.',\n gymCodeUnknown: 'No gym found with that id. Check it with your gym.',\n /** Actions that only make sense once a gym has taken the member on. */\n noGymJoined: 'Join a gym first. You can find one from your Me tab.',\n /** Searching by name needs somewhere to look — see `gymSearchQuerySchema`. */\n gymSearchStateRequired: 'Pick a state to search gyms by name.',\n gymSearchNoResults: 'No gyms matched. Try a different area, or enter the gym id.',\n\n /**\n * Camera (QR scanning)\n *\n * ── THESE USED TO POINT AT A BOX THAT DOES NOT EXIST ──────────────────────\n * Two of them ended \"Enter the code printed under the QR\", and both halves of\n * that sentence were wrong: the scanner has no field to type a code into, and\n * the poster does not print the check-in code at all — it prints the gym's\n * PUBLIC join code, which cannot check anybody in. So a member whose camera\n * failed was sent looking for a box that is not there, to type a number that\n * is not printed.\n *\n * They now point at the thing that IS there: the check-in button under the\n * viewfinder, which needs no code at all.\n */\n cameraDenied:\n 'Camera access is blocked. Allow it in your browser settings, or check in without scanning.',\n cameraUnavailable: 'No camera found on this device. Check in without scanning instead.',\n cameraInsecure: 'Scanning needs a secure (https) connection.',\n cameraFailed: 'Could not start the camera. Check in without scanning instead.',\n /**\n * The member is not attached to a gym, on the codeless path.\n *\n * Its own sentence because that path reached for `qrUnknown` before it had\n * one, and that string opens \"This QR is no longer valid\" — advice about a\n * poster, given to somebody who never scanned one.\n */\n gymUnavailable: 'You are not checked in to a gym yet. Join one from the Me tab first.',\n\n // Engagement\n feedbackRatingRequired: 'Choose a rating to submit your feedback',\n complaintIssueRequired: 'Choose the issue you are reporting',\n\n // Security (role-switch PIN)\n mpinIncorrect: 'Incorrect PIN.',\n mpinVerifyFailed: \"Couldn't verify. Try again.\",\n mpinSetFailed: \"Couldn't set your PIN. Try again.\",\n mpinMismatch: \"Those didn't match. Try again.\",\n mpinResetLinkFailed: \"Couldn't send the reset link. Try again.\",\n mpinResetFailed: \"Couldn't reset your PIN. Try again.\",\n\n // Uploads\n imageUploadFailed: 'Could not upload that image. Please try again.',\n imageProcessFailed: 'Could not process that image. Please try again.',\n} as const satisfies Record<string, string>;\n\nexport type ErrorMessageKey = keyof typeof ERROR_MESSAGE;\n\n/** Error messages that name a limit or value. */\nexport const ERROR_MESSAGE_FOR = {\n maxPhotos: (max: number): string => `You can add up to ${max} photos`,\n /**\n * Refused at the door because the membership is paused and still inside its\n * window.\n *\n * Its own sentence because \"renew your plan\" is wrong advice here twice\n * over: nothing has lapsed, and paying again would not open the door a day\n * sooner. The resume date is the only thing this member wants to know, and\n * it is the reason this one takes a parameter at all.\n */\n membershipPausedUntil: (resumesOn: string): string =>\n `Your membership is paused until ${resumesOn}. You can check in again from that day.`,\n /** Role-switch PIN lockout, in whole minutes remaining. */\n mpinLockedOut: (minutes: number): string =>\n `Too many attempts. Try again in ${minutes} minute${minutes > 1 ? 's' : ''}.`,\n} as const;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gymmonk-schema",
3
- "version": "0.54.0",
3
+ "version": "0.55.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",