gymmonk-schema 0.31.0 → 0.32.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.
- package/dist/center.d.ts +95 -14
- package/dist/center.d.ts.map +1 -1
- package/dist/center.js +58 -6
- package/dist/center.js.map +1 -1
- package/dist/gym-registration.d.ts +15 -3
- package/dist/gym-registration.d.ts.map +1 -1
- package/dist/onboarding-form.d.ts +246 -3
- package/dist/onboarding-form.d.ts.map +1 -1
- package/dist/onboarding-form.js +69 -4
- package/dist/onboarding-form.js.map +1 -1
- package/dist/onboarding.d.ts +15 -3
- package/dist/onboarding.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/center.d.ts
CHANGED
|
@@ -8,21 +8,66 @@
|
|
|
8
8
|
* @module gymmonk-schema/center
|
|
9
9
|
*/
|
|
10
10
|
import { z } from 'zod';
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* What a centre OFFERS, multi-select.
|
|
13
|
+
*
|
|
14
|
+
* ── The axis, and why `personal-training` is gone ───────────────────────────
|
|
15
|
+
* Every value here answers one question: what does somebody DO at this place.
|
|
16
|
+
* That makes them disciplines and training modalities, so an owner ticking
|
|
17
|
+
* three of them describes a real branch and a member reading them knows what
|
|
18
|
+
* they are walking into.
|
|
19
|
+
*
|
|
20
|
+
* `personal-training` answered a different question — HOW coaching is
|
|
21
|
+
* delivered, one-to-one instead of in a class — and nearly every gym in the
|
|
22
|
+
* list offers it. A value almost everyone can tick separates nobody from
|
|
23
|
+
* anybody, which is the opposite of what a type is for. Business model
|
|
24
|
+
* (big-box, boutique, 24-hour) is a third axis again, and one this product
|
|
25
|
+
* already answers better elsewhere: floor area and opening hours are recorded
|
|
26
|
+
* fields, not a guess from a chip.
|
|
27
|
+
*
|
|
28
|
+
* ── Where the list comes from ───────────────────────────────────────────────
|
|
29
|
+
* Cross-referenced rather than invented: Google Business Profile's fitness
|
|
30
|
+
* categories (what gyms actually file themselves under, and therefore what
|
|
31
|
+
* members search), ClassPass and Mindbody's activity taxonomies, and the
|
|
32
|
+
* formats the Indian market actually runs — cult.fit's S&C, HRX, boxing, dance
|
|
33
|
+
* fitness and yoga, plus the bodybuilding and martial-arts traditions that a
|
|
34
|
+
* list drawn only from US sources would miss entirely.
|
|
35
|
+
*
|
|
36
|
+
* Deliberately NOT included, so the next person does not re-litigate it:
|
|
37
|
+
* rock climbing, gymnastics and aerial/pole are real facility categories but
|
|
38
|
+
* not gyms in the sense this product manages; women-only and kids' fitness
|
|
39
|
+
* describe an AUDIENCE, not an activity; 24-hour and big-box describe hours
|
|
40
|
+
* and size, both of which are already their own fields.
|
|
41
|
+
*
|
|
42
|
+
* Values are stable slugs — the LABELS are the frontend's business, so
|
|
43
|
+
* rewording a chip never rewrites stored data.
|
|
44
|
+
*/
|
|
45
|
+
export declare const CENTER_TYPES: readonly ["gym", "strength", "crossfit", "functional", "bodybuilding", "powerlifting", "calisthenics", "cardio", "hiit", "spinning", "bootcamp", "aerobics", "sports-conditioning", "yoga", "pilates", "meditation", "zumba", "dance", "boxing", "kickboxing", "mma", "martial-arts", "swimming", "physiotherapy"];
|
|
13
46
|
export declare const centerTypeSchema: z.ZodEnum<{
|
|
14
47
|
cardio: "cardio";
|
|
15
48
|
strength: "strength";
|
|
16
49
|
yoga: "yoga";
|
|
17
50
|
crossfit: "crossfit";
|
|
18
51
|
gym: "gym";
|
|
52
|
+
functional: "functional";
|
|
53
|
+
bodybuilding: "bodybuilding";
|
|
54
|
+
powerlifting: "powerlifting";
|
|
55
|
+
calisthenics: "calisthenics";
|
|
56
|
+
hiit: "hiit";
|
|
57
|
+
spinning: "spinning";
|
|
58
|
+
bootcamp: "bootcamp";
|
|
59
|
+
aerobics: "aerobics";
|
|
60
|
+
"sports-conditioning": "sports-conditioning";
|
|
19
61
|
pilates: "pilates";
|
|
62
|
+
meditation: "meditation";
|
|
20
63
|
zumba: "zumba";
|
|
21
|
-
|
|
64
|
+
dance: "dance";
|
|
22
65
|
boxing: "boxing";
|
|
66
|
+
kickboxing: "kickboxing";
|
|
67
|
+
mma: "mma";
|
|
68
|
+
"martial-arts": "martial-arts";
|
|
23
69
|
swimming: "swimming";
|
|
24
|
-
|
|
25
|
-
"personal-training": "personal-training";
|
|
70
|
+
physiotherapy: "physiotherapy";
|
|
26
71
|
}>;
|
|
27
72
|
export type CenterType = z.infer<typeof centerTypeSchema>;
|
|
28
73
|
export declare const centerStatusSchema: z.ZodEnum<{
|
|
@@ -47,13 +92,25 @@ export declare const createCenterBodySchema: z.ZodObject<{
|
|
|
47
92
|
yoga: "yoga";
|
|
48
93
|
crossfit: "crossfit";
|
|
49
94
|
gym: "gym";
|
|
95
|
+
functional: "functional";
|
|
96
|
+
bodybuilding: "bodybuilding";
|
|
97
|
+
powerlifting: "powerlifting";
|
|
98
|
+
calisthenics: "calisthenics";
|
|
99
|
+
hiit: "hiit";
|
|
100
|
+
spinning: "spinning";
|
|
101
|
+
bootcamp: "bootcamp";
|
|
102
|
+
aerobics: "aerobics";
|
|
103
|
+
"sports-conditioning": "sports-conditioning";
|
|
50
104
|
pilates: "pilates";
|
|
105
|
+
meditation: "meditation";
|
|
51
106
|
zumba: "zumba";
|
|
52
|
-
|
|
107
|
+
dance: "dance";
|
|
53
108
|
boxing: "boxing";
|
|
109
|
+
kickboxing: "kickboxing";
|
|
110
|
+
mma: "mma";
|
|
111
|
+
"martial-arts": "martial-arts";
|
|
54
112
|
swimming: "swimming";
|
|
55
|
-
|
|
56
|
-
"personal-training": "personal-training";
|
|
113
|
+
physiotherapy: "physiotherapy";
|
|
57
114
|
}>>;
|
|
58
115
|
address: z.ZodObject<{
|
|
59
116
|
country: z.ZodObject<{
|
|
@@ -149,13 +206,25 @@ export declare const updateCenterBodySchema: z.ZodObject<{
|
|
|
149
206
|
yoga: "yoga";
|
|
150
207
|
crossfit: "crossfit";
|
|
151
208
|
gym: "gym";
|
|
209
|
+
functional: "functional";
|
|
210
|
+
bodybuilding: "bodybuilding";
|
|
211
|
+
powerlifting: "powerlifting";
|
|
212
|
+
calisthenics: "calisthenics";
|
|
213
|
+
hiit: "hiit";
|
|
214
|
+
spinning: "spinning";
|
|
215
|
+
bootcamp: "bootcamp";
|
|
216
|
+
aerobics: "aerobics";
|
|
217
|
+
"sports-conditioning": "sports-conditioning";
|
|
152
218
|
pilates: "pilates";
|
|
219
|
+
meditation: "meditation";
|
|
153
220
|
zumba: "zumba";
|
|
154
|
-
|
|
221
|
+
dance: "dance";
|
|
155
222
|
boxing: "boxing";
|
|
223
|
+
kickboxing: "kickboxing";
|
|
224
|
+
mma: "mma";
|
|
225
|
+
"martial-arts": "martial-arts";
|
|
156
226
|
swimming: "swimming";
|
|
157
|
-
|
|
158
|
-
"personal-training": "personal-training";
|
|
227
|
+
physiotherapy: "physiotherapy";
|
|
159
228
|
}>>>;
|
|
160
229
|
address: z.ZodOptional<z.ZodObject<{
|
|
161
230
|
country: z.ZodObject<{
|
|
@@ -265,13 +334,25 @@ export declare const centerSchema: z.ZodObject<{
|
|
|
265
334
|
yoga: "yoga";
|
|
266
335
|
crossfit: "crossfit";
|
|
267
336
|
gym: "gym";
|
|
337
|
+
functional: "functional";
|
|
338
|
+
bodybuilding: "bodybuilding";
|
|
339
|
+
powerlifting: "powerlifting";
|
|
340
|
+
calisthenics: "calisthenics";
|
|
341
|
+
hiit: "hiit";
|
|
342
|
+
spinning: "spinning";
|
|
343
|
+
bootcamp: "bootcamp";
|
|
344
|
+
aerobics: "aerobics";
|
|
345
|
+
"sports-conditioning": "sports-conditioning";
|
|
268
346
|
pilates: "pilates";
|
|
347
|
+
meditation: "meditation";
|
|
269
348
|
zumba: "zumba";
|
|
270
|
-
|
|
349
|
+
dance: "dance";
|
|
271
350
|
boxing: "boxing";
|
|
351
|
+
kickboxing: "kickboxing";
|
|
352
|
+
mma: "mma";
|
|
353
|
+
"martial-arts": "martial-arts";
|
|
272
354
|
swimming: "swimming";
|
|
273
|
-
|
|
274
|
-
"personal-training": "personal-training";
|
|
355
|
+
physiotherapy: "physiotherapy";
|
|
275
356
|
}>>;
|
|
276
357
|
address: z.ZodObject<{
|
|
277
358
|
country: z.ZodObject<{
|
package/dist/center.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"center.d.ts","sourceRoot":"","sources":["../src/center.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB
|
|
1
|
+
{"version":3,"file":"center.d.ts","sourceRoot":"","sources":["../src/center.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,YAAY,oTAgCf,CAAC;AACX,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;EAAuB,CAAC;AACrD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAI1D,eAAO,MAAM,kBAAkB;;;EAAiC,CAAC;AACjE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,aAAa,aAGwB,CAAC;AACnD,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAIpD,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqCjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAmC,CAAC;AACvE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAItE,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4CvB,CAAC;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAIlD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB;;;;iBAI5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAI5D,yEAAyE;AACzE,eAAO,MAAM,eAAe;;;;;;;;;iBAkB1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,eAAO,MAAM,oBAAoB;;;;;;;;iBAwB7B,CAAC;AACL,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;iBAIhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAKpE,eAAO,MAAM,qBAAqB;;;;iBAIhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|
package/dist/center.js
CHANGED
|
@@ -12,20 +12,72 @@ import { isoDateTimeSchema, objectIdSchema, optionalEmailSchema, optionalPhoneSc
|
|
|
12
12
|
import { mapLinkInputSchema, placePinSchema, timezoneSchema } from './location.js';
|
|
13
13
|
import { addressSchema, weeklyHoursSchema } from './shared.js';
|
|
14
14
|
// ─── Center type ─────────────────────────────────────────────────────────────
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* What a centre OFFERS, multi-select.
|
|
17
|
+
*
|
|
18
|
+
* ── The axis, and why `personal-training` is gone ───────────────────────────
|
|
19
|
+
* Every value here answers one question: what does somebody DO at this place.
|
|
20
|
+
* That makes them disciplines and training modalities, so an owner ticking
|
|
21
|
+
* three of them describes a real branch and a member reading them knows what
|
|
22
|
+
* they are walking into.
|
|
23
|
+
*
|
|
24
|
+
* `personal-training` answered a different question — HOW coaching is
|
|
25
|
+
* delivered, one-to-one instead of in a class — and nearly every gym in the
|
|
26
|
+
* list offers it. A value almost everyone can tick separates nobody from
|
|
27
|
+
* anybody, which is the opposite of what a type is for. Business model
|
|
28
|
+
* (big-box, boutique, 24-hour) is a third axis again, and one this product
|
|
29
|
+
* already answers better elsewhere: floor area and opening hours are recorded
|
|
30
|
+
* fields, not a guess from a chip.
|
|
31
|
+
*
|
|
32
|
+
* ── Where the list comes from ───────────────────────────────────────────────
|
|
33
|
+
* Cross-referenced rather than invented: Google Business Profile's fitness
|
|
34
|
+
* categories (what gyms actually file themselves under, and therefore what
|
|
35
|
+
* members search), ClassPass and Mindbody's activity taxonomies, and the
|
|
36
|
+
* formats the Indian market actually runs — cult.fit's S&C, HRX, boxing, dance
|
|
37
|
+
* fitness and yoga, plus the bodybuilding and martial-arts traditions that a
|
|
38
|
+
* list drawn only from US sources would miss entirely.
|
|
39
|
+
*
|
|
40
|
+
* Deliberately NOT included, so the next person does not re-litigate it:
|
|
41
|
+
* rock climbing, gymnastics and aerial/pole are real facility categories but
|
|
42
|
+
* not gyms in the sense this product manages; women-only and kids' fitness
|
|
43
|
+
* describe an AUDIENCE, not an activity; 24-hour and big-box describe hours
|
|
44
|
+
* and size, both of which are already their own fields.
|
|
45
|
+
*
|
|
46
|
+
* Values are stable slugs — the LABELS are the frontend's business, so
|
|
47
|
+
* rewording a chip never rewrites stored data.
|
|
48
|
+
*/
|
|
16
49
|
export const CENTER_TYPES = [
|
|
50
|
+
// General
|
|
17
51
|
'gym',
|
|
52
|
+
// Strength
|
|
53
|
+
'strength',
|
|
18
54
|
'crossfit',
|
|
55
|
+
'functional',
|
|
56
|
+
'bodybuilding',
|
|
57
|
+
'powerlifting',
|
|
58
|
+
'calisthenics',
|
|
59
|
+
// Cardio & group training
|
|
60
|
+
'cardio',
|
|
61
|
+
'hiit',
|
|
62
|
+
'spinning',
|
|
63
|
+
'bootcamp',
|
|
64
|
+
'aerobics',
|
|
65
|
+
'sports-conditioning',
|
|
66
|
+
// Mind & body
|
|
19
67
|
'yoga',
|
|
20
68
|
'pilates',
|
|
69
|
+
'meditation',
|
|
70
|
+
// Dance
|
|
21
71
|
'zumba',
|
|
22
|
-
'
|
|
23
|
-
|
|
24
|
-
'mma',
|
|
72
|
+
'dance',
|
|
73
|
+
// Combat
|
|
25
74
|
'boxing',
|
|
75
|
+
'kickboxing',
|
|
76
|
+
'mma',
|
|
77
|
+
'martial-arts',
|
|
78
|
+
// Facilities
|
|
26
79
|
'swimming',
|
|
27
|
-
'
|
|
28
|
-
'personal-training',
|
|
80
|
+
'physiotherapy',
|
|
29
81
|
];
|
|
30
82
|
export const centerTypeSchema = z.enum(CENTER_TYPES);
|
|
31
83
|
// ─── Status ──────────────────────────────────────────────────────────────────
|
package/dist/center.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"center.js","sourceRoot":"","sources":["../src/center.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — Center (branch)\n * ================================\n * A physical gym location belonging to an organisation. Carries the public gym\n * code members use to join, opening hours, equipment scope, and contact info.\n * v1 memberships are single-center, so a Center is the unit a member joins.\n *\n * @module gymmonk-schema/center\n */\n\nimport { z } from 'zod';\nimport {\n isoDateTimeSchema,\n objectIdSchema,\n optionalEmailSchema,\n optionalPhoneSchema,\n optionalTrimmedString,\n phoneSchema,\n} from './common.js';\nimport { mapLinkInputSchema, placePinSchema, timezoneSchema } from './location.js';\nimport { addressSchema, weeklyHoursSchema } from './shared.js';\n\n// ─── Center type ─────────────────────────────────────────────────────────────\n\n/** The activity types a center offers (multi-select). */\nexport const CENTER_TYPES = [\n 'gym',\n 'crossfit',\n 'yoga',\n 'pilates',\n 'zumba',\n 'cardio',\n 'strength',\n 'mma',\n 'boxing',\n 'swimming',\n 'aerobics',\n 'personal-training',\n] as const;\nexport const centerTypeSchema = z.enum(CENTER_TYPES);\nexport type CenterType = z.infer<typeof centerTypeSchema>;\n\n// ─── Status ──────────────────────────────────────────────────────────────────\n\nexport const centerStatusSchema = z.enum(['active', 'inactive']);\nexport type CenterStatus = z.infer<typeof centerStatusSchema>;\n\n/**\n * The public gym code a member enters at onboarding to join, `GYM-YYYY-NNN`.\n * Server-generated and unique; never accepted in a create body.\n */\nexport const gymCodeSchema = z\n .string()\n .trim()\n .regex(/^GYM-\\d{4}-\\d{3,}$/, 'Invalid gym code');\nexport type GymCode = z.infer<typeof gymCodeSchema>;\n\n// ─── Create / update bodies ──────────────────────────────────────────────────\n\nexport const createCenterBodySchema = z.object({\n name: z.string().trim().min(1, 'Center name is required').max(160),\n description: optionalTrimmedString(2000),\n types: z.array(centerTypeSchema).min(1, 'Pick at least one type'),\n address: addressSchema,\n /** Usable floor area in square feet. */\n floorAreaSqft: z.number().int().min(0).max(1_000_000).optional(),\n /**\n * How many people the branch holds at once.\n *\n * The denominator behind \"how full are we\" — the centre detail screen shows\n * it beside the active-member count, and crowd-load has no meaning without\n * it. Optional: a gym that has never measured it should not be blocked from\n * saving the rest of its details.\n */\n capacity: z.number().int().min(1).max(100_000).optional(),\n primaryPhone: phoneSchema,\n secondaryPhone: optionalPhoneSchema,\n email: optionalEmailSchema,\n website: optionalTrimmedString(200),\n hours: weeklyHoursSchema,\n /** Uploaded gallery image URLs. */\n photos: z.array(z.string().trim().max(2000)).max(20).default([]),\n /**\n * A pasted Google Maps link. The server reads the coordinates out of it; the\n * client never sends them.\n *\n * This is the pin attendance is measured against, and it is NOT the same as\n * `address.coords`: that is an area centroid resolved from master data, which\n * can be a whole district's midpoint and is useless for proximity.\n */\n ...mapLinkInputSchema.shape,\n /**\n * How close a member must be for a check-in to count, metres. Optional — the\n * server applies its own default when a gym has not chosen one.\n */\n checkInRadiusM: z.number().int().min(50).max(5000).optional(),\n});\nexport type CreateCenterBody = z.infer<typeof createCenterBodySchema>;\n\nexport const updateCenterBodySchema = createCenterBodySchema.partial();\nexport type UpdateCenterBody = z.infer<typeof updateCenterBodySchema>;\n\n// ─── Entity (API response) ───────────────────────────────────────────────────\n\nexport const centerSchema = z.object({\n id: objectIdSchema,\n organisationId: objectIdSchema,\n /** Public join code, `GYM-YYYY-NNN`. */\n code: gymCodeSchema,\n name: z.string(),\n description: z.string().nullable(),\n types: z.array(centerTypeSchema),\n address: addressSchema,\n floorAreaSqft: z.number().int().nullable(),\n capacity: z.number().int().nullable(),\n primaryPhone: z.string(),\n secondaryPhone: z.string().nullable(),\n email: z.string().nullable(),\n website: z.string().nullable(),\n hours: weeklyHoursSchema,\n photos: z.array(z.string()),\n status: centerStatusSchema,\n /** Denormalised active-member count for roster headers. */\n memberCount: z.number().int().nonnegative(),\n // The resolved pin. Absent until an owner pastes a Maps link, which is why\n // attendance is unenforceable rather than failing for a gym without one.\n ...placePinSchema.shape,\n checkInRadiusM: z.number().int().nullable().optional(),\n /**\n * The centre's own timezone, as an IANA identifier (\"Asia/Kolkata\").\n *\n * WHY THE CENTRE'S AND NOT THE VIEWER'S. Attendance is filed under a calendar\n * DAY, and a day is not timezone-free — whichever zone derives it defines when\n * it starts. Deriving it from whoever is looking would put one visit on\n * different days for the member and the owner, and would make an owner's daily\n * roll-up change depending on where they opened the app. A visit belongs to the\n * gym's operating day; only the display of times follows the reader.\n *\n * An IANA NAME, never a UTC offset. Offsets are a property of an instant, not\n * of a place: they move with DST, so a stored \"+05:30\" is already wrong for\n * half the year anywhere that observes it.\n *\n * Server-derived from the centre's pin, never accepted from a client — the\n * same rule the coordinates follow, and for the same reason.\n */\n timezone: timezoneSchema,\n createdAt: isoDateTimeSchema,\n updatedAt: isoDateTimeSchema,\n});\nexport type Center = z.infer<typeof centerSchema>;\n\n// ─── Center stats (owner detail header) ──────────────────────────────────────\n\n/**\n * The three-up header on a centre's detail screen.\n *\n * Computed, never stored — `activeMembers` counts live memberships and\n * `monthlyRevenueInr` sums this month's successful payments, so the card can\n * never drift from the ledger the way a denormalised total would. `capacity`\n * is the one stored value, and is null until the owner fills it in.\n */\nexport const centerStatsSchema = z.object({\n capacity: z.number().int().nullable(),\n activeMembers: z.number().int().nonnegative(),\n monthlyRevenueInr: z.number().nonnegative(),\n});\nexport type CenterStats = z.infer<typeof centerStatsSchema>;\n\n// ─── Gym directory (member \"find a gym\") ─────────────────────────────────────\n\n/** A lightweight public directory result for the \"find a gym\" screen. */\nexport const nearbyGymSchema = z.object({\n id: objectIdSchema,\n code: gymCodeSchema,\n name: z.string(),\n /** The narrowest place name we hold (locality → district → state). */\n area: z.string(),\n district: z.string().nullable(),\n state: z.string().nullable(),\n /**\n * Kilometres from the coordinates the caller supplied.\n *\n * NULL, not 0, when it cannot be computed — the caller sent no position, or\n * the gym's address carries no coordinates. Zero is a real distance (\"you are\n * standing in it\") and using it as the unknown value put every un-geocoded\n * gym at the top of a nearest-first list.\n */\n distanceKm: z.number().nonnegative().nullable(),\n memberCount: z.number().int().nonnegative().optional(),\n});\nexport type NearbyGym = z.infer<typeof nearbyGymSchema>;\n\n/**\n * The gym directory has THREE ways in, and they differ in how far they reach.\n *\n * `code` exact public gym id — matches anywhere in the country, because\n * a member holding the id already knows which gym they mean and\n * may well be joining one in another state.\n * `q` name search — deliberately narrowed to ONE STATE. Gym names\n * repeat heavily (\"Fitness First\"), so a nationwide name search\n * returns a list nobody can pick from.\n * `districtId` browse — the gyms around them, nearest first when they share\n * their position.\n *\n * `stateId` / `districtId` are master-area ids (`area.ts`), matched against the\n * address `ancestry` array every center stores.\n */\nexport const GYM_SEARCH_MAX_RESULTS = 20;\n\nexport const gymSearchQuerySchema = z\n .object({\n /** Exact public gym id, e.g. `GYM-2025-001`. Unscoped by geography. */\n code: optionalTrimmedString(40),\n /** Name fragment. Requires `stateId`. */\n q: optionalTrimmedString(120),\n stateId: optionalTrimmedString(40),\n districtId: optionalTrimmedString(40),\n lat: z.coerce.number().min(-90).max(90).optional(),\n lng: z.coerce.number().min(-180).max(180).optional(),\n limit: z.coerce\n .number()\n .int()\n .min(1)\n .max(GYM_SEARCH_MAX_RESULTS)\n .default(GYM_SEARCH_MAX_RESULTS),\n })\n .refine((v) => !v.q || Boolean(v.stateId), {\n message: 'Pick a state to search gyms by name',\n path: ['stateId'],\n })\n .refine((v) => Boolean(v.code || v.q || v.stateId || v.districtId), {\n message: 'Enter a gym id, or pick where to look',\n path: ['stateId'],\n });\nexport type GymSearchQuery = z.infer<typeof gymSearchQuerySchema>;\n\n/**\n * The directory response.\n *\n * `truncated` says the district holds more gyms than were returned, which is\n * what the screen turns into \"showing the 20 nearest — enter a gym id if yours\n * is not here\" rather than letting the member assume their gym is absent.\n */\nexport const gymSearchResultSchema = z.object({\n items: z.array(nearbyGymSchema),\n total: z.number().int().nonnegative(),\n truncated: z.boolean(),\n});\nexport type GymSearchResult = z.infer<typeof gymSearchResultSchema>;\n\n// ─── Retained: nearby-gyms query ─────────────────────────────────────────────\n// Kept for the gym-search route the owner console's repo still serves. See the\n// note in `onboarding.ts`.\nexport const nearbyGymsQuerySchema = z.object({\n lat: z.coerce.number().min(-90).max(90).optional(),\n lng: z.coerce.number().min(-180).max(180).optional(),\n q: optionalTrimmedString(120),\n});\nexport type NearbyGymsQuery = z.infer<typeof nearbyGymsQuerySchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"center.js","sourceRoot":"","sources":["../src/center.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — Center (branch)\n * ================================\n * A physical gym location belonging to an organisation. Carries the public gym\n * code members use to join, opening hours, equipment scope, and contact info.\n * v1 memberships are single-center, so a Center is the unit a member joins.\n *\n * @module gymmonk-schema/center\n */\n\nimport { z } from 'zod';\nimport {\n isoDateTimeSchema,\n objectIdSchema,\n optionalEmailSchema,\n optionalPhoneSchema,\n optionalTrimmedString,\n phoneSchema,\n} from './common.js';\nimport { mapLinkInputSchema, placePinSchema, timezoneSchema } from './location.js';\nimport { addressSchema, weeklyHoursSchema } from './shared.js';\n\n// ─── Center type ─────────────────────────────────────────────────────────────\n\n/**\n * What a centre OFFERS, multi-select.\n *\n * ── The axis, and why `personal-training` is gone ───────────────────────────\n * Every value here answers one question: what does somebody DO at this place.\n * That makes them disciplines and training modalities, so an owner ticking\n * three of them describes a real branch and a member reading them knows what\n * they are walking into.\n *\n * `personal-training` answered a different question — HOW coaching is\n * delivered, one-to-one instead of in a class — and nearly every gym in the\n * list offers it. A value almost everyone can tick separates nobody from\n * anybody, which is the opposite of what a type is for. Business model\n * (big-box, boutique, 24-hour) is a third axis again, and one this product\n * already answers better elsewhere: floor area and opening hours are recorded\n * fields, not a guess from a chip.\n *\n * ── Where the list comes from ───────────────────────────────────────────────\n * Cross-referenced rather than invented: Google Business Profile's fitness\n * categories (what gyms actually file themselves under, and therefore what\n * members search), ClassPass and Mindbody's activity taxonomies, and the\n * formats the Indian market actually runs — cult.fit's S&C, HRX, boxing, dance\n * fitness and yoga, plus the bodybuilding and martial-arts traditions that a\n * list drawn only from US sources would miss entirely.\n *\n * Deliberately NOT included, so the next person does not re-litigate it:\n * rock climbing, gymnastics and aerial/pole are real facility categories but\n * not gyms in the sense this product manages; women-only and kids' fitness\n * describe an AUDIENCE, not an activity; 24-hour and big-box describe hours\n * and size, both of which are already their own fields.\n *\n * Values are stable slugs — the LABELS are the frontend's business, so\n * rewording a chip never rewrites stored data.\n */\nexport const CENTER_TYPES = [\n // General\n 'gym',\n // Strength\n 'strength',\n 'crossfit',\n 'functional',\n 'bodybuilding',\n 'powerlifting',\n 'calisthenics',\n // Cardio & group training\n 'cardio',\n 'hiit',\n 'spinning',\n 'bootcamp',\n 'aerobics',\n 'sports-conditioning',\n // Mind & body\n 'yoga',\n 'pilates',\n 'meditation',\n // Dance\n 'zumba',\n 'dance',\n // Combat\n 'boxing',\n 'kickboxing',\n 'mma',\n 'martial-arts',\n // Facilities\n 'swimming',\n 'physiotherapy',\n] as const;\nexport const centerTypeSchema = z.enum(CENTER_TYPES);\nexport type CenterType = z.infer<typeof centerTypeSchema>;\n\n// ─── Status ──────────────────────────────────────────────────────────────────\n\nexport const centerStatusSchema = z.enum(['active', 'inactive']);\nexport type CenterStatus = z.infer<typeof centerStatusSchema>;\n\n/**\n * The public gym code a member enters at onboarding to join, `GYM-YYYY-NNN`.\n * Server-generated and unique; never accepted in a create body.\n */\nexport const gymCodeSchema = z\n .string()\n .trim()\n .regex(/^GYM-\\d{4}-\\d{3,}$/, 'Invalid gym code');\nexport type GymCode = z.infer<typeof gymCodeSchema>;\n\n// ─── Create / update bodies ──────────────────────────────────────────────────\n\nexport const createCenterBodySchema = z.object({\n name: z.string().trim().min(1, 'Center name is required').max(160),\n description: optionalTrimmedString(2000),\n types: z.array(centerTypeSchema).min(1, 'Pick at least one type'),\n address: addressSchema,\n /** Usable floor area in square feet. */\n floorAreaSqft: z.number().int().min(0).max(1_000_000).optional(),\n /**\n * How many people the branch holds at once.\n *\n * The denominator behind \"how full are we\" — the centre detail screen shows\n * it beside the active-member count, and crowd-load has no meaning without\n * it. Optional: a gym that has never measured it should not be blocked from\n * saving the rest of its details.\n */\n capacity: z.number().int().min(1).max(100_000).optional(),\n primaryPhone: phoneSchema,\n secondaryPhone: optionalPhoneSchema,\n email: optionalEmailSchema,\n website: optionalTrimmedString(200),\n hours: weeklyHoursSchema,\n /** Uploaded gallery image URLs. */\n photos: z.array(z.string().trim().max(2000)).max(20).default([]),\n /**\n * A pasted Google Maps link. The server reads the coordinates out of it; the\n * client never sends them.\n *\n * This is the pin attendance is measured against, and it is NOT the same as\n * `address.coords`: that is an area centroid resolved from master data, which\n * can be a whole district's midpoint and is useless for proximity.\n */\n ...mapLinkInputSchema.shape,\n /**\n * How close a member must be for a check-in to count, metres. Optional — the\n * server applies its own default when a gym has not chosen one.\n */\n checkInRadiusM: z.number().int().min(50).max(5000).optional(),\n});\nexport type CreateCenterBody = z.infer<typeof createCenterBodySchema>;\n\nexport const updateCenterBodySchema = createCenterBodySchema.partial();\nexport type UpdateCenterBody = z.infer<typeof updateCenterBodySchema>;\n\n// ─── Entity (API response) ───────────────────────────────────────────────────\n\nexport const centerSchema = z.object({\n id: objectIdSchema,\n organisationId: objectIdSchema,\n /** Public join code, `GYM-YYYY-NNN`. */\n code: gymCodeSchema,\n name: z.string(),\n description: z.string().nullable(),\n types: z.array(centerTypeSchema),\n address: addressSchema,\n floorAreaSqft: z.number().int().nullable(),\n capacity: z.number().int().nullable(),\n primaryPhone: z.string(),\n secondaryPhone: z.string().nullable(),\n email: z.string().nullable(),\n website: z.string().nullable(),\n hours: weeklyHoursSchema,\n photos: z.array(z.string()),\n status: centerStatusSchema,\n /** Denormalised active-member count for roster headers. */\n memberCount: z.number().int().nonnegative(),\n // The resolved pin. Absent until an owner pastes a Maps link, which is why\n // attendance is unenforceable rather than failing for a gym without one.\n ...placePinSchema.shape,\n checkInRadiusM: z.number().int().nullable().optional(),\n /**\n * The centre's own timezone, as an IANA identifier (\"Asia/Kolkata\").\n *\n * WHY THE CENTRE'S AND NOT THE VIEWER'S. Attendance is filed under a calendar\n * DAY, and a day is not timezone-free — whichever zone derives it defines when\n * it starts. Deriving it from whoever is looking would put one visit on\n * different days for the member and the owner, and would make an owner's daily\n * roll-up change depending on where they opened the app. A visit belongs to the\n * gym's operating day; only the display of times follows the reader.\n *\n * An IANA NAME, never a UTC offset. Offsets are a property of an instant, not\n * of a place: they move with DST, so a stored \"+05:30\" is already wrong for\n * half the year anywhere that observes it.\n *\n * Server-derived from the centre's pin, never accepted from a client — the\n * same rule the coordinates follow, and for the same reason.\n */\n timezone: timezoneSchema,\n createdAt: isoDateTimeSchema,\n updatedAt: isoDateTimeSchema,\n});\nexport type Center = z.infer<typeof centerSchema>;\n\n// ─── Center stats (owner detail header) ──────────────────────────────────────\n\n/**\n * The three-up header on a centre's detail screen.\n *\n * Computed, never stored — `activeMembers` counts live memberships and\n * `monthlyRevenueInr` sums this month's successful payments, so the card can\n * never drift from the ledger the way a denormalised total would. `capacity`\n * is the one stored value, and is null until the owner fills it in.\n */\nexport const centerStatsSchema = z.object({\n capacity: z.number().int().nullable(),\n activeMembers: z.number().int().nonnegative(),\n monthlyRevenueInr: z.number().nonnegative(),\n});\nexport type CenterStats = z.infer<typeof centerStatsSchema>;\n\n// ─── Gym directory (member \"find a gym\") ─────────────────────────────────────\n\n/** A lightweight public directory result for the \"find a gym\" screen. */\nexport const nearbyGymSchema = z.object({\n id: objectIdSchema,\n code: gymCodeSchema,\n name: z.string(),\n /** The narrowest place name we hold (locality → district → state). */\n area: z.string(),\n district: z.string().nullable(),\n state: z.string().nullable(),\n /**\n * Kilometres from the coordinates the caller supplied.\n *\n * NULL, not 0, when it cannot be computed — the caller sent no position, or\n * the gym's address carries no coordinates. Zero is a real distance (\"you are\n * standing in it\") and using it as the unknown value put every un-geocoded\n * gym at the top of a nearest-first list.\n */\n distanceKm: z.number().nonnegative().nullable(),\n memberCount: z.number().int().nonnegative().optional(),\n});\nexport type NearbyGym = z.infer<typeof nearbyGymSchema>;\n\n/**\n * The gym directory has THREE ways in, and they differ in how far they reach.\n *\n * `code` exact public gym id — matches anywhere in the country, because\n * a member holding the id already knows which gym they mean and\n * may well be joining one in another state.\n * `q` name search — deliberately narrowed to ONE STATE. Gym names\n * repeat heavily (\"Fitness First\"), so a nationwide name search\n * returns a list nobody can pick from.\n * `districtId` browse — the gyms around them, nearest first when they share\n * their position.\n *\n * `stateId` / `districtId` are master-area ids (`area.ts`), matched against the\n * address `ancestry` array every center stores.\n */\nexport const GYM_SEARCH_MAX_RESULTS = 20;\n\nexport const gymSearchQuerySchema = z\n .object({\n /** Exact public gym id, e.g. `GYM-2025-001`. Unscoped by geography. */\n code: optionalTrimmedString(40),\n /** Name fragment. Requires `stateId`. */\n q: optionalTrimmedString(120),\n stateId: optionalTrimmedString(40),\n districtId: optionalTrimmedString(40),\n lat: z.coerce.number().min(-90).max(90).optional(),\n lng: z.coerce.number().min(-180).max(180).optional(),\n limit: z.coerce\n .number()\n .int()\n .min(1)\n .max(GYM_SEARCH_MAX_RESULTS)\n .default(GYM_SEARCH_MAX_RESULTS),\n })\n .refine((v) => !v.q || Boolean(v.stateId), {\n message: 'Pick a state to search gyms by name',\n path: ['stateId'],\n })\n .refine((v) => Boolean(v.code || v.q || v.stateId || v.districtId), {\n message: 'Enter a gym id, or pick where to look',\n path: ['stateId'],\n });\nexport type GymSearchQuery = z.infer<typeof gymSearchQuerySchema>;\n\n/**\n * The directory response.\n *\n * `truncated` says the district holds more gyms than were returned, which is\n * what the screen turns into \"showing the 20 nearest — enter a gym id if yours\n * is not here\" rather than letting the member assume their gym is absent.\n */\nexport const gymSearchResultSchema = z.object({\n items: z.array(nearbyGymSchema),\n total: z.number().int().nonnegative(),\n truncated: z.boolean(),\n});\nexport type GymSearchResult = z.infer<typeof gymSearchResultSchema>;\n\n// ─── Retained: nearby-gyms query ─────────────────────────────────────────────\n// Kept for the gym-search route the owner console's repo still serves. See the\n// note in `onboarding.ts`.\nexport const nearbyGymsQuerySchema = z.object({\n lat: z.coerce.number().min(-90).max(90).optional(),\n lng: z.coerce.number().min(-180).max(180).optional(),\n q: optionalTrimmedString(120),\n});\nexport type NearbyGymsQuery = z.infer<typeof nearbyGymsQuerySchema>;\n"]}
|
|
@@ -93,13 +93,25 @@ export declare const gymRegistrationBodySchema: z.ZodObject<{
|
|
|
93
93
|
yoga: "yoga";
|
|
94
94
|
crossfit: "crossfit";
|
|
95
95
|
gym: "gym";
|
|
96
|
+
functional: "functional";
|
|
97
|
+
bodybuilding: "bodybuilding";
|
|
98
|
+
powerlifting: "powerlifting";
|
|
99
|
+
calisthenics: "calisthenics";
|
|
100
|
+
hiit: "hiit";
|
|
101
|
+
spinning: "spinning";
|
|
102
|
+
bootcamp: "bootcamp";
|
|
103
|
+
aerobics: "aerobics";
|
|
104
|
+
"sports-conditioning": "sports-conditioning";
|
|
96
105
|
pilates: "pilates";
|
|
106
|
+
meditation: "meditation";
|
|
97
107
|
zumba: "zumba";
|
|
98
|
-
|
|
108
|
+
dance: "dance";
|
|
99
109
|
boxing: "boxing";
|
|
110
|
+
kickboxing: "kickboxing";
|
|
111
|
+
mma: "mma";
|
|
112
|
+
"martial-arts": "martial-arts";
|
|
100
113
|
swimming: "swimming";
|
|
101
|
-
|
|
102
|
-
"personal-training": "personal-training";
|
|
114
|
+
physiotherapy: "physiotherapy";
|
|
103
115
|
}>>;
|
|
104
116
|
address: z.ZodObject<{
|
|
105
117
|
country: z.ZodObject<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gym-registration.d.ts","sourceRoot":"","sources":["../src/gym-registration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,sEAAsE;AACtE,eAAO,MAAM,sBAAsB;;;iBAGjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,eAAO,MAAM,yBAAyB
|
|
1
|
+
{"version":3,"file":"gym-registration.d.ts","sourceRoot":"","sources":["../src/gym-registration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,sEAAsE;AACtE,eAAO,MAAM,sBAAsB;;;iBAGjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAIpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* @module gymmonk-schema/onboarding-form
|
|
18
18
|
*/
|
|
19
19
|
import { z } from 'zod';
|
|
20
|
+
import { type CenterType } from './center.js';
|
|
20
21
|
import type { WEEKDAYS } from './shared.js';
|
|
21
22
|
import { type Address } from './shared.js';
|
|
22
23
|
/**
|
|
@@ -244,7 +245,17 @@ export declare const MEMBER_STEP_FIELDS: (keyof MemberOnboardingForm)[][];
|
|
|
244
245
|
* birth and phone, every one of which onboarding already captured. Registering
|
|
245
246
|
* a business asks about the BUSINESS; the person is already known.
|
|
246
247
|
*/
|
|
247
|
-
|
|
248
|
+
/**
|
|
249
|
+
* The gym-registration form's FIELD SHAPE, exported separately from the schema.
|
|
250
|
+
*
|
|
251
|
+
* Separate because the schema carries a `superRefine` (see
|
|
252
|
+
* `refineGymRegistrationForm`) and a refined object cannot be `.extend()`ed —
|
|
253
|
+
* zod hands back a ZodEffects, and calling `.extend` on one throws at MODULE
|
|
254
|
+
* LOAD, which no build catches and which takes the whole page down on import.
|
|
255
|
+
* The web client adds the two pasted Maps links to this form, so it needs the
|
|
256
|
+
* shape and the refinement as separate pieces it can recombine.
|
|
257
|
+
*/
|
|
258
|
+
export declare const gymRegistrationFormShape: {
|
|
248
259
|
orgName: z.ZodString;
|
|
249
260
|
orgDescription: z.ZodString;
|
|
250
261
|
hqAddress: z.ZodNullable<z.ZodObject<{
|
|
@@ -350,7 +361,59 @@ export declare const gymRegistrationFormSchema: z.ZodObject<{
|
|
|
350
361
|
}, z.core.$strip>;
|
|
351
362
|
centerName: z.ZodString;
|
|
352
363
|
centerDescription: z.ZodString;
|
|
353
|
-
|
|
364
|
+
/**
|
|
365
|
+
* The real enum, not `z.array(z.string())`.
|
|
366
|
+
*
|
|
367
|
+
* It was strings, and `toGymRegistrationBody` then CAST them to the enum on
|
|
368
|
+
* the way to the API. A cast is not a check: retiring a centre type left the
|
|
369
|
+
* wizard perfectly happy to submit the retired value and the server to
|
|
370
|
+
* reject the whole registration, with the owner told only that something was
|
|
371
|
+
* wrong. Now a value the API will not accept cannot leave the form.
|
|
372
|
+
*/
|
|
373
|
+
centerTypes: z.ZodArray<z.ZodEnum<{
|
|
374
|
+
cardio: "cardio";
|
|
375
|
+
strength: "strength";
|
|
376
|
+
yoga: "yoga";
|
|
377
|
+
crossfit: "crossfit";
|
|
378
|
+
gym: "gym";
|
|
379
|
+
functional: "functional";
|
|
380
|
+
bodybuilding: "bodybuilding";
|
|
381
|
+
powerlifting: "powerlifting";
|
|
382
|
+
calisthenics: "calisthenics";
|
|
383
|
+
hiit: "hiit";
|
|
384
|
+
spinning: "spinning";
|
|
385
|
+
bootcamp: "bootcamp";
|
|
386
|
+
aerobics: "aerobics";
|
|
387
|
+
"sports-conditioning": "sports-conditioning";
|
|
388
|
+
pilates: "pilates";
|
|
389
|
+
meditation: "meditation";
|
|
390
|
+
zumba: "zumba";
|
|
391
|
+
dance: "dance";
|
|
392
|
+
boxing: "boxing";
|
|
393
|
+
kickboxing: "kickboxing";
|
|
394
|
+
mma: "mma";
|
|
395
|
+
"martial-arts": "martial-arts";
|
|
396
|
+
swimming: "swimming";
|
|
397
|
+
physiotherapy: "physiotherapy";
|
|
398
|
+
}>>;
|
|
399
|
+
/**
|
|
400
|
+
* "Same as organisation location".
|
|
401
|
+
*
|
|
402
|
+
* Most gyms register one branch at the address they just typed on the
|
|
403
|
+
* previous screen, and asking for it twice invites a typo between two things
|
|
404
|
+
* that are meant to be the same place — one of which decides whether a
|
|
405
|
+
* member standing at the door is close enough to check in.
|
|
406
|
+
*
|
|
407
|
+
* It is the CHOICE that is stored, not a copy of the answer. The address is
|
|
408
|
+
* resolved from the organisation at submit, so an owner who goes back and
|
|
409
|
+
* corrects the HQ address does not leave a stale duplicate behind on the
|
|
410
|
+
* centre.
|
|
411
|
+
*/
|
|
412
|
+
centerSameAsOrg: z.ZodBoolean;
|
|
413
|
+
/**
|
|
414
|
+
* Nullable here and required by `refineGymRegistrationForm` instead, because
|
|
415
|
+
* "required" is now conditional — see that function.
|
|
416
|
+
*/
|
|
354
417
|
centerAddress: z.ZodNullable<z.ZodObject<{
|
|
355
418
|
country: z.ZodObject<{
|
|
356
419
|
id: z.ZodString;
|
|
@@ -387,6 +450,108 @@ export declare const gymRegistrationFormSchema: z.ZodObject<{
|
|
|
387
450
|
lng: z.ZodNumber;
|
|
388
451
|
source: z.ZodString;
|
|
389
452
|
}, z.core.$strip>>;
|
|
453
|
+
}, z.core.$strip>>;
|
|
454
|
+
floorArea: z.ZodNullable<z.ZodNumber>;
|
|
455
|
+
centerPhone1: z.ZodString;
|
|
456
|
+
centerPhone2: z.ZodString;
|
|
457
|
+
centerEmail: z.ZodString;
|
|
458
|
+
hours: z.ZodObject<{
|
|
459
|
+
mon: z.ZodObject<{
|
|
460
|
+
open: z.ZodBoolean;
|
|
461
|
+
from: z.ZodString;
|
|
462
|
+
to: z.ZodString;
|
|
463
|
+
}, z.core.$strip>;
|
|
464
|
+
tue: z.ZodObject<{
|
|
465
|
+
open: z.ZodBoolean;
|
|
466
|
+
from: z.ZodString;
|
|
467
|
+
to: z.ZodString;
|
|
468
|
+
}, z.core.$strip>;
|
|
469
|
+
wed: z.ZodObject<{
|
|
470
|
+
open: z.ZodBoolean;
|
|
471
|
+
from: z.ZodString;
|
|
472
|
+
to: z.ZodString;
|
|
473
|
+
}, z.core.$strip>;
|
|
474
|
+
thu: z.ZodObject<{
|
|
475
|
+
open: z.ZodBoolean;
|
|
476
|
+
from: z.ZodString;
|
|
477
|
+
to: z.ZodString;
|
|
478
|
+
}, z.core.$strip>;
|
|
479
|
+
fri: z.ZodObject<{
|
|
480
|
+
open: z.ZodBoolean;
|
|
481
|
+
from: z.ZodString;
|
|
482
|
+
to: z.ZodString;
|
|
483
|
+
}, z.core.$strip>;
|
|
484
|
+
sat: z.ZodObject<{
|
|
485
|
+
open: z.ZodBoolean;
|
|
486
|
+
from: z.ZodString;
|
|
487
|
+
to: z.ZodString;
|
|
488
|
+
}, z.core.$strip>;
|
|
489
|
+
sun: z.ZodObject<{
|
|
490
|
+
open: z.ZodBoolean;
|
|
491
|
+
from: z.ZodString;
|
|
492
|
+
to: z.ZodString;
|
|
493
|
+
}, z.core.$strip>;
|
|
494
|
+
}, z.core.$strip>;
|
|
495
|
+
photos: z.ZodArray<z.ZodString>;
|
|
496
|
+
planId: z.ZodNullable<z.ZodString> & z.ZodType<{} | undefined, string | null, z.core.$ZodTypeInternals<{} | undefined, string | null>>;
|
|
497
|
+
tenureId: z.ZodNullable<z.ZodString>;
|
|
498
|
+
};
|
|
499
|
+
/**
|
|
500
|
+
* The centre needs an address unless it is inheriting the organisation's.
|
|
501
|
+
*
|
|
502
|
+
* A cross-field rule, so it cannot live on `centerAddress` itself. The issue is
|
|
503
|
+
* raised AT `centerAddress` deliberately: the wizard validates each step by
|
|
504
|
+
* calling `trigger` with that step's field names, and an error parked on the
|
|
505
|
+
* object root would match none of them — Continue would refuse to advance with
|
|
506
|
+
* nothing on screen explaining why. That exact shape of bug has cost this
|
|
507
|
+
* codebase two silent forms already.
|
|
508
|
+
*
|
|
509
|
+
* Structurally typed rather than tied to `GymRegistrationForm` so the web
|
|
510
|
+
* client can apply the same function to its own extended schema.
|
|
511
|
+
*/
|
|
512
|
+
export declare function refineGymRegistrationForm(v: {
|
|
513
|
+
centerSameAsOrg: boolean;
|
|
514
|
+
centerAddress: Address | null;
|
|
515
|
+
}, ctx: z.RefinementCtx): void;
|
|
516
|
+
export declare const gymRegistrationFormSchema: z.ZodObject<{
|
|
517
|
+
orgName: z.ZodString;
|
|
518
|
+
orgDescription: z.ZodString;
|
|
519
|
+
hqAddress: z.ZodNullable<z.ZodObject<{
|
|
520
|
+
country: z.ZodObject<{
|
|
521
|
+
id: z.ZodString;
|
|
522
|
+
name: z.ZodString;
|
|
523
|
+
}, z.core.$strip>;
|
|
524
|
+
state: z.ZodObject<{
|
|
525
|
+
id: z.ZodString;
|
|
526
|
+
name: z.ZodString;
|
|
527
|
+
}, z.core.$strip>;
|
|
528
|
+
district: z.ZodOptional<z.ZodObject<{
|
|
529
|
+
id: z.ZodString;
|
|
530
|
+
name: z.ZodString;
|
|
531
|
+
}, z.core.$strip>>;
|
|
532
|
+
locality: z.ZodOptional<z.ZodObject<{
|
|
533
|
+
id: z.ZodString;
|
|
534
|
+
name: z.ZodString;
|
|
535
|
+
type: z.ZodEnum<{
|
|
536
|
+
SUB_DISTRICT: "SUB_DISTRICT";
|
|
537
|
+
BLOCK: "BLOCK";
|
|
538
|
+
GRAM_PANCHAYAT: "GRAM_PANCHAYAT";
|
|
539
|
+
VILLAGE: "VILLAGE";
|
|
540
|
+
HAMLET: "HAMLET";
|
|
541
|
+
URBAN_BODY: "URBAN_BODY";
|
|
542
|
+
URBAN_WARD: "URBAN_WARD";
|
|
543
|
+
LOCALITY: "LOCALITY";
|
|
544
|
+
}>;
|
|
545
|
+
}, z.core.$strip>>;
|
|
546
|
+
line1: z.ZodString;
|
|
547
|
+
landmark: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
|
|
548
|
+
pincode: z.ZodString;
|
|
549
|
+
ancestry: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
550
|
+
coords: z.ZodOptional<z.ZodObject<{
|
|
551
|
+
lat: z.ZodNumber;
|
|
552
|
+
lng: z.ZodNumber;
|
|
553
|
+
source: z.ZodString;
|
|
554
|
+
}, z.core.$strip>>;
|
|
390
555
|
}, z.core.$strip>> & z.ZodType<{} | undefined, {
|
|
391
556
|
country: {
|
|
392
557
|
id: string;
|
|
@@ -442,6 +607,82 @@ export declare const gymRegistrationFormSchema: z.ZodObject<{
|
|
|
442
607
|
source: string;
|
|
443
608
|
} | undefined;
|
|
444
609
|
} | null>>;
|
|
610
|
+
orgWebsite: z.ZodString;
|
|
611
|
+
gstNumber: z.ZodString;
|
|
612
|
+
cinNumber: z.ZodString;
|
|
613
|
+
numberOfCenters: z.ZodNullable<z.ZodNumber> & z.ZodType<{} | undefined, number | null, z.core.$ZodTypeInternals<{} | undefined, number | null>>;
|
|
614
|
+
orgSocials: z.ZodObject<{
|
|
615
|
+
jansathi: z.ZodString;
|
|
616
|
+
youtube: z.ZodString;
|
|
617
|
+
instagram: z.ZodString;
|
|
618
|
+
facebook: z.ZodString;
|
|
619
|
+
}, z.core.$strip>;
|
|
620
|
+
centerName: z.ZodString;
|
|
621
|
+
centerDescription: z.ZodString;
|
|
622
|
+
centerTypes: z.ZodArray<z.ZodEnum<{
|
|
623
|
+
cardio: "cardio";
|
|
624
|
+
strength: "strength";
|
|
625
|
+
yoga: "yoga";
|
|
626
|
+
crossfit: "crossfit";
|
|
627
|
+
gym: "gym";
|
|
628
|
+
functional: "functional";
|
|
629
|
+
bodybuilding: "bodybuilding";
|
|
630
|
+
powerlifting: "powerlifting";
|
|
631
|
+
calisthenics: "calisthenics";
|
|
632
|
+
hiit: "hiit";
|
|
633
|
+
spinning: "spinning";
|
|
634
|
+
bootcamp: "bootcamp";
|
|
635
|
+
aerobics: "aerobics";
|
|
636
|
+
"sports-conditioning": "sports-conditioning";
|
|
637
|
+
pilates: "pilates";
|
|
638
|
+
meditation: "meditation";
|
|
639
|
+
zumba: "zumba";
|
|
640
|
+
dance: "dance";
|
|
641
|
+
boxing: "boxing";
|
|
642
|
+
kickboxing: "kickboxing";
|
|
643
|
+
mma: "mma";
|
|
644
|
+
"martial-arts": "martial-arts";
|
|
645
|
+
swimming: "swimming";
|
|
646
|
+
physiotherapy: "physiotherapy";
|
|
647
|
+
}>>;
|
|
648
|
+
centerSameAsOrg: z.ZodBoolean;
|
|
649
|
+
centerAddress: z.ZodNullable<z.ZodObject<{
|
|
650
|
+
country: z.ZodObject<{
|
|
651
|
+
id: z.ZodString;
|
|
652
|
+
name: z.ZodString;
|
|
653
|
+
}, z.core.$strip>;
|
|
654
|
+
state: z.ZodObject<{
|
|
655
|
+
id: z.ZodString;
|
|
656
|
+
name: z.ZodString;
|
|
657
|
+
}, z.core.$strip>;
|
|
658
|
+
district: z.ZodOptional<z.ZodObject<{
|
|
659
|
+
id: z.ZodString;
|
|
660
|
+
name: z.ZodString;
|
|
661
|
+
}, z.core.$strip>>;
|
|
662
|
+
locality: z.ZodOptional<z.ZodObject<{
|
|
663
|
+
id: z.ZodString;
|
|
664
|
+
name: z.ZodString;
|
|
665
|
+
type: z.ZodEnum<{
|
|
666
|
+
SUB_DISTRICT: "SUB_DISTRICT";
|
|
667
|
+
BLOCK: "BLOCK";
|
|
668
|
+
GRAM_PANCHAYAT: "GRAM_PANCHAYAT";
|
|
669
|
+
VILLAGE: "VILLAGE";
|
|
670
|
+
HAMLET: "HAMLET";
|
|
671
|
+
URBAN_BODY: "URBAN_BODY";
|
|
672
|
+
URBAN_WARD: "URBAN_WARD";
|
|
673
|
+
LOCALITY: "LOCALITY";
|
|
674
|
+
}>;
|
|
675
|
+
}, z.core.$strip>>;
|
|
676
|
+
line1: z.ZodString;
|
|
677
|
+
landmark: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
|
|
678
|
+
pincode: z.ZodString;
|
|
679
|
+
ancestry: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
680
|
+
coords: z.ZodOptional<z.ZodObject<{
|
|
681
|
+
lat: z.ZodNumber;
|
|
682
|
+
lng: z.ZodNumber;
|
|
683
|
+
source: z.ZodString;
|
|
684
|
+
}, z.core.$strip>>;
|
|
685
|
+
}, z.core.$strip>>;
|
|
445
686
|
floorArea: z.ZodNullable<z.ZodNumber>;
|
|
446
687
|
centerPhone1: z.ZodString;
|
|
447
688
|
centerPhone2: z.ZodString;
|
|
@@ -499,7 +740,9 @@ export type GymRegistrationForm = {
|
|
|
499
740
|
orgSocials: OnboardingSocialLinks;
|
|
500
741
|
centerName: string;
|
|
501
742
|
centerDescription: string;
|
|
502
|
-
centerTypes:
|
|
743
|
+
centerTypes: CenterType[];
|
|
744
|
+
/** "Same as organisation location" — see `gymRegistrationFormShape`. */
|
|
745
|
+
centerSameAsOrg: boolean;
|
|
503
746
|
centerAddress: OnboardingAddress;
|
|
504
747
|
floorArea: number | null;
|
|
505
748
|
centerPhone1: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboarding-form.d.ts","sourceRoot":"","sources":["../src/onboarding-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"onboarding-form.d.ts","sourceRoot":"","sources":["../src/onboarding-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,aAAa,CAAC;AAEhE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,KAAK,OAAO,EAAiB,MAAM,aAAa,CAAC;AAyB1D;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAAoD,CAAC;AAEzF,6DAA6D;AAC7D,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,IAAI,CAAC;AAE/C,kCAAkC;AAClC,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAEzE;AAID,eAAO,MAAM,2BAA2B;;;;;iBAKtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAIhF,eAAO,MAAM,wBAAwB;;;;iBAInC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC;AAI1F,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,CAAC,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACxD,GAAG,EAAE,CAAC,CAAC,aAAa,GACnB,IAAI,CAsBN;AAED,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;iBAkDnC,CAAC;AAEL;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC;IAC3C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,oBAkB1C,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAoB,CAAC,MAAM,SAAS,MAAM;IACzD,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,uBAAuB,EAAE,SAAS,oBAAoB,CACjE,MAAM,oBAAoB,GAAG,MAAM,CACpC,EAyBS,CAAC;AAEX,eAAO,MAAM,6BAA6B,QAAiC,CAAC;AAE5E;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,CAAC,MAAM,oBAAoB,CAAC,EAAE,EAE9D,CAAC;AAIF;;;;;;;GAOG;AACH;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAanC;;;;;;;;OAQG;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEH;;;;;;;;;;;;OAYG;;IAEH;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmBJ,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACvC,CAAC,EAAE;IAAE,eAAe,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,EAC9D,GAAG,EAAE,CAAC,CAAC,aAAa,GACnB,IAAI,CAKN;AAED,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAEG,CAAC;AAE1C,iFAAiF;AACjF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,qBAAqB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,wEAAwE;IACxE,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,iBAAiB,CAAC;IACjC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,qBAAqB,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AASF,kDAAkD;AAClD,eAAO,MAAM,wBAAwB,EAAE,qBAQtC,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,mBAyBzC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,oBAAoB,CAChE,MAAM,mBAAmB,GAAG,MAAM,CACnC,EA0BS,CAAC;AAEX,eAAO,MAAM,4BAA4B,QAAgC,CAAC;AAE1E;;;GAGG;AACH,eAAO,MAAM,4BAA4B,EAAE,CAAC,MAAM,mBAAmB,CAAC,EAAE,EACtB,CAAC;AAYnD,kEAAkE;AAClE,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4ElC,CAAC;AAEL,iFAAiF;AACjF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,OAAO,EAAE,qBAAqB,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,qBAAqB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,iBAAiB,CAAC;IACjC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,qBAAqB,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,mBAiCzC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,oBAAoB,CAChE,MAAM,mBAAmB,GAAG,MAAM,CACnC,EAsCS,CAAC;AAEX,eAAO,MAAM,4BAA4B,QAAgC,CAAC;AAE1E;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,CAAC,MAAM,mBAAmB,CAAC,EAAE,EAE5D,CAAC;AAIF,eAAO,MAAM,kBAAkB;;iBAE7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
|
package/dist/onboarding-form.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* @module gymmonk-schema/onboarding-form
|
|
18
18
|
*/
|
|
19
19
|
import { z } from 'zod';
|
|
20
|
+
import { centerTypeSchema } from './center.js';
|
|
20
21
|
import { ageInYears, isRealDate, PHONE_REGEX } from './common.js';
|
|
21
22
|
import { addressSchema } from './shared.js';
|
|
22
23
|
// ─── Field-level rules (shared by both wizards) ──────────────────────────────
|
|
@@ -213,7 +214,17 @@ export const MEMBER_STEP_FIELDS = MEMBER_ONBOARDING_STEPS.map((s) => [...s.field
|
|
|
213
214
|
* birth and phone, every one of which onboarding already captured. Registering
|
|
214
215
|
* a business asks about the BUSINESS; the person is already known.
|
|
215
216
|
*/
|
|
216
|
-
|
|
217
|
+
/**
|
|
218
|
+
* The gym-registration form's FIELD SHAPE, exported separately from the schema.
|
|
219
|
+
*
|
|
220
|
+
* Separate because the schema carries a `superRefine` (see
|
|
221
|
+
* `refineGymRegistrationForm`) and a refined object cannot be `.extend()`ed —
|
|
222
|
+
* zod hands back a ZodEffects, and calling `.extend` on one throws at MODULE
|
|
223
|
+
* LOAD, which no build catches and which takes the whole page down on import.
|
|
224
|
+
* The web client adds the two pasted Maps links to this form, so it needs the
|
|
225
|
+
* shape and the refinement as separate pieces it can recombine.
|
|
226
|
+
*/
|
|
227
|
+
export const gymRegistrationFormShape = {
|
|
217
228
|
// 1 — organisation
|
|
218
229
|
orgName: z.string().trim().min(1, 'Organisation name is required'),
|
|
219
230
|
orgDescription: z.string(),
|
|
@@ -226,8 +237,35 @@ export const gymRegistrationFormSchema = z.object({
|
|
|
226
237
|
// 2 — center
|
|
227
238
|
centerName: z.string().trim().min(1, 'Center name is required'),
|
|
228
239
|
centerDescription: z.string(),
|
|
229
|
-
|
|
230
|
-
|
|
240
|
+
/**
|
|
241
|
+
* The real enum, not `z.array(z.string())`.
|
|
242
|
+
*
|
|
243
|
+
* It was strings, and `toGymRegistrationBody` then CAST them to the enum on
|
|
244
|
+
* the way to the API. A cast is not a check: retiring a centre type left the
|
|
245
|
+
* wizard perfectly happy to submit the retired value and the server to
|
|
246
|
+
* reject the whole registration, with the owner told only that something was
|
|
247
|
+
* wrong. Now a value the API will not accept cannot leave the form.
|
|
248
|
+
*/
|
|
249
|
+
centerTypes: z.array(centerTypeSchema).min(1, 'Pick at least one type'),
|
|
250
|
+
/**
|
|
251
|
+
* "Same as organisation location".
|
|
252
|
+
*
|
|
253
|
+
* Most gyms register one branch at the address they just typed on the
|
|
254
|
+
* previous screen, and asking for it twice invites a typo between two things
|
|
255
|
+
* that are meant to be the same place — one of which decides whether a
|
|
256
|
+
* member standing at the door is close enough to check in.
|
|
257
|
+
*
|
|
258
|
+
* It is the CHOICE that is stored, not a copy of the answer. The address is
|
|
259
|
+
* resolved from the organisation at submit, so an owner who goes back and
|
|
260
|
+
* corrects the HQ address does not leave a stale duplicate behind on the
|
|
261
|
+
* centre.
|
|
262
|
+
*/
|
|
263
|
+
centerSameAsOrg: z.boolean(),
|
|
264
|
+
/**
|
|
265
|
+
* Nullable here and required by `refineGymRegistrationForm` instead, because
|
|
266
|
+
* "required" is now conditional — see that function.
|
|
267
|
+
*/
|
|
268
|
+
centerAddress: addressSchema.nullable(),
|
|
231
269
|
floorArea: z.number().nullable(),
|
|
232
270
|
centerPhone1: formPhone,
|
|
233
271
|
centerPhone2: optionalFormPhone,
|
|
@@ -245,7 +283,30 @@ export const gymRegistrationFormSchema = z.object({
|
|
|
245
283
|
// 3 — plan
|
|
246
284
|
planId: requiredChoice(z.string(), 'Choose a plan'),
|
|
247
285
|
tenureId: z.string().nullable(),
|
|
248
|
-
}
|
|
286
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* The centre needs an address unless it is inheriting the organisation's.
|
|
289
|
+
*
|
|
290
|
+
* A cross-field rule, so it cannot live on `centerAddress` itself. The issue is
|
|
291
|
+
* raised AT `centerAddress` deliberately: the wizard validates each step by
|
|
292
|
+
* calling `trigger` with that step's field names, and an error parked on the
|
|
293
|
+
* object root would match none of them — Continue would refuse to advance with
|
|
294
|
+
* nothing on screen explaining why. That exact shape of bug has cost this
|
|
295
|
+
* codebase two silent forms already.
|
|
296
|
+
*
|
|
297
|
+
* Structurally typed rather than tied to `GymRegistrationForm` so the web
|
|
298
|
+
* client can apply the same function to its own extended schema.
|
|
299
|
+
*/
|
|
300
|
+
export function refineGymRegistrationForm(v, ctx) {
|
|
301
|
+
if (v.centerSameAsOrg)
|
|
302
|
+
return;
|
|
303
|
+
if (v.centerAddress === null) {
|
|
304
|
+
ctx.addIssue({ code: 'custom', path: ['centerAddress'], message: 'Add your address' });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
export const gymRegistrationFormSchema = z
|
|
308
|
+
.object(gymRegistrationFormShape)
|
|
309
|
+
.superRefine(refineGymRegistrationForm);
|
|
249
310
|
const EMPTY_ONBOARDING_SOCIALS = {
|
|
250
311
|
jansathi: '',
|
|
251
312
|
youtube: '',
|
|
@@ -274,6 +335,10 @@ export const EMPTY_GYM_REGISTRATION_FORM = {
|
|
|
274
335
|
centerName: '',
|
|
275
336
|
centerDescription: '',
|
|
276
337
|
centerTypes: [],
|
|
338
|
+
// Unticked. A pre-ticked box would put the organisation's address on the
|
|
339
|
+
// centre without the owner ever looking at it — and that address is what a
|
|
340
|
+
// member's check-in is measured against.
|
|
341
|
+
centerSameAsOrg: false,
|
|
277
342
|
centerAddress: null,
|
|
278
343
|
floorArea: null,
|
|
279
344
|
centerPhone1: '',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboarding-form.js","sourceRoot":"","sources":["../src/onboarding-form.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — Onboarding & gym-registration FORM schemas\n * ===========================================================\n * The client-side validation contract for the member onboarding wizard and the\n * \"list your gym\" wizard.\n *\n * Why these live here and are distinct from the `*OnboardingBody` API schemas:\n * a half-filled form legitimately holds `null` for a choice the user has not\n * made yet, whereas the API body only ever sees completed values. So these are\n * the SAME rules expressed over the form's working shape — every message,\n * regex and bound is defined once, here, and both sides agree on it.\n *\n * Everything reuses the primitives in `shared.ts` / `common.ts`\n * (`INDIAN_STATES`, `CENTER_TYPES`, `WEEKDAYS`, phone/pincode rules) rather\n * than restating them.\n *\n * @module gymmonk-schema/onboarding-form\n */\n\nimport { z } from 'zod';\nimport { ageInYears, isRealDate, PHONE_REGEX } from './common.js';\nimport type { WEEKDAYS } from './shared.js';\nimport { type Address, addressSchema } from './shared.js';\n\n// ─── Field-level rules (shared by both wizards) ──────────────────────────────\n\n/** A required 10-digit Indian mobile, as typed into a form field. */\nconst formPhone = z.string().regex(PHONE_REGEX, 'Enter a valid 10-digit mobile number');\n\n/** An optional phone: blank is allowed, anything else must be valid. */\nconst optionalFormPhone = z\n .string()\n .refine((v) => v === '' || PHONE_REGEX.test(v), 'Enter a valid 10-digit mobile number');\n\n/** An optional email: blank is allowed, anything else must look like an email. */\nconst optionalFormEmail = z\n .string()\n .refine((v) => v === '' || /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(v), {\n message: 'Enter a valid email',\n });\n\n/** A choice the user has not made yet is `null` until they pick one. */\nconst requiredChoice = <T extends z.ZodTypeAny>(inner: T, message: string) =>\n inner.nullable().refine((v: unknown) => v !== null, { message });\n\n// ─── Address (form shape: one atomic value, null until picked) ────────────\n\n/**\n * An address inside an onboarding form is ONE cell, not four.\n *\n * It used to be `street` + `city` + `state` + `pincode`, each independently\n * fillable, which is why this module needed rules for a half-filled address.\n * Now it is picked through the location drawer, which either hands back a\n * complete `Address` — area chain, ids, names, ancestry, coordinates — or\n * nothing at all. So there is no partial state left to validate, and `null`\n * simply means \"not chosen yet\".\n */\nexport const onboardingAddressSchema = requiredChoice(addressSchema, 'Add your address');\n\n/** Working shape of an address inside an onboarding form. */\nexport type OnboardingAddress = Address | null;\n\n/** An address has been picked. */\nexport function isOnboardingAddressComplete(a: OnboardingAddress): boolean {\n return a !== null;\n}\n\n// ─── Social links (form shape: every handle is a plain string) ───────────────\n\nexport const onboardingSocialLinksSchema = z.object({\n jansathi: z.string(),\n youtube: z.string(),\n instagram: z.string(),\n facebook: z.string(),\n});\nexport type OnboardingSocialLinks = z.infer<typeof onboardingSocialLinksSchema>;\n\n// ─── Opening hours (form shape) ─────────────────────────────────────────────\n\nexport const onboardingDayHoursSchema = z.object({\n open: z.boolean(),\n from: z.string(),\n to: z.string(),\n});\nexport type OnboardingDayHours = z.infer<typeof onboardingDayHoursSchema>;\nexport type OnboardingWeeklyHours = Record<(typeof WEEKDAYS)[number], OnboardingDayHours>;\n\n// ─── Member onboarding form ─────────────────────────────────────────────────\n\n/** Age bounds a gym membership realistically spans. */\nexport const MEMBER_MIN_AGE_YEARS = 10;\nexport const MEMBER_MAX_AGE_YEARS = 100;\n\n/**\n * The three-cell date-of-birth rule, in ONE place.\n *\n * Each cell is bounded by its own input, but only the assembled date can catch\n * an impossible one (31 Feb) or an implausible age. The issue is attached to\n * `dobDay` so it renders under the field group rather than under one cell.\n *\n * Exported because the member's own profile editor asks for the SAME date with\n * the same three cells. When the rule lived inline in the wizard, that editor\n * had a free-text box parsed with `new Date()` instead, which accepted \"March\n * 18, 1994\", accepted nonsense just as happily, and silently dropped whatever\n * it could not read.\n */\nexport function refineMemberDob(\n v: { dobDay: string; dobMonth: string; dobYear: string },\n ctx: z.RefinementCtx,\n): void {\n if (!v.dobDay || !v.dobMonth || v.dobYear.length !== 4) return;\n\n const day = Number(v.dobDay);\n const month = Number(v.dobMonth);\n const year = Number(v.dobYear);\n\n if (!isRealDate(year, month, day)) {\n ctx.addIssue({ code: 'custom', path: ['dobDay'], message: 'That date does not exist' });\n return;\n }\n\n const age = ageInYears(year, month, day);\n if (age < MEMBER_MIN_AGE_YEARS) {\n ctx.addIssue({\n code: 'custom',\n path: ['dobDay'],\n message: `You must be at least ${MEMBER_MIN_AGE_YEARS} to join`,\n });\n } else if (age > MEMBER_MAX_AGE_YEARS) {\n ctx.addIssue({ code: 'custom', path: ['dobDay'], message: 'Check the year of birth' });\n }\n}\n\nexport const memberOnboardingFormSchema = z\n .object({\n firstName: z.string().trim().min(1, 'First name is required'),\n lastName: z.string().trim(),\n dobDay: z.string().min(1, 'Enter the day'),\n dobMonth: z.string().min(1, 'Enter the month'),\n dobYear: z.string().length(4, 'Enter a 4-digit year'),\n gender: requiredChoice(z.enum(['male', 'female', 'other']), 'Select your gender'),\n heightCm: requiredChoice(z.number(), 'Add your height'),\n weightKg: requiredChoice(z.number(), 'Add your weight'),\n neckCm: requiredChoice(z.number(), 'Add your neck'),\n waistCm: requiredChoice(z.number(), 'Add your waist'),\n hipsCm: z.number().nullable(),\n phone: formPhone,\n emergencyName: z.string().trim().min(1, 'Add an emergency contact name'),\n /**\n * How that person is related — OPTIONAL, matching `emergencyContactSchema`.\n *\n * Asked here so the three places that write an emergency contact (this\n * wizard, the member's own Legal & Emergency editor, the owner's desk-entry\n * forms) record the SAME three facts. It used to be asked in the other two\n * and not here, so a member who onboarded themselves had no relationship\n * stored, and the owner console's read fell back to showing the contact's\n * NAME in the relationship slot — which the next owner edit then wrote back\n * as the name.\n *\n * Optional rather than required because a wizard is not the place to block\n * someone over context, and the number itself is what matters in an\n * emergency.\n */\n emergencyRelation: z.string().trim().max(60),\n emergencyPhone: formPhone,\n bloodGroup: requiredChoice(z.string(), 'Select your blood group'),\n /** GCS URL of the uploaded photo. Optional — it can be added later. */\n avatarUrl: z.string(),\n })\n .superRefine(refineMemberDob)\n /**\n * The API requires the hips measurement for female members (needed for the\n * body-fat estimate), so it fails inline on the measurements step instead of\n * 422-ing at Finish. Own refine — no early-return coupling with the DOB rule.\n */\n .superRefine((v, ctx) => {\n if (v.gender === 'female' && v.hipsCm == null) {\n ctx.addIssue({\n code: 'custom',\n path: ['hipsCm'],\n message: 'Hips measurement is required',\n });\n }\n });\n\n/**\n * The form's working shape. Hand-written (not `z.infer`) so unset numeric /\n * choice fields can hold `null` while the schema's `requiredChoice` rules still\n * drive the field errors — the resolver is cast to this type at `useForm`.\n */\nexport type MemberOnboardingForm = {\n firstName: string;\n lastName: string;\n dobDay: string;\n dobMonth: string;\n dobYear: string;\n gender: 'male' | 'female' | 'other' | null;\n heightCm: number | null;\n weightKg: number | null;\n neckCm: number | null;\n waistCm: number | null;\n hipsCm: number | null;\n phone: string;\n emergencyName: string;\n emergencyRelation: string;\n emergencyPhone: string;\n bloodGroup: string | null;\n avatarUrl: string;\n};\n\nexport const EMPTY_MEMBER_ONBOARDING_FORM: MemberOnboardingForm = {\n firstName: '',\n lastName: '',\n dobDay: '',\n dobMonth: '',\n dobYear: '',\n gender: null,\n heightCm: null,\n weightKg: null,\n neckCm: null,\n waistCm: null,\n hipsCm: null,\n phone: '',\n emergencyName: '',\n emergencyRelation: '',\n emergencyPhone: '',\n bloodGroup: null,\n avatarUrl: '',\n};\n\n/**\n * One onboarding step, described ONCE.\n *\n * The wizard chrome (screen name, help line, progress denominator) and the\n * validation gate used to live in the page as a local `META` array beside a\n * separate field map — two lists that had to be kept the same length by hand.\n * They are one list here, so a step cannot exist without a title or be left\n * without a validation gate.\n */\nexport interface OnboardingStepConfig<TField extends string> {\n /** Stable identifier — safe for analytics and resume, unlike the index. */\n key: string;\n /** Screen name in the step header. */\n title: string;\n /** Help line under the screen name. */\n helpText: string;\n /**\n * Fields validated before this step may be left. Naming a parent path (e.g.\n * `address`) validates its nested fields too.\n */\n fields: readonly TField[];\n}\n\nexport const MEMBER_ONBOARDING_STEPS: readonly OnboardingStepConfig<\n keyof MemberOnboardingForm & string\n>[] = [\n {\n key: 'basics',\n title: 'Basic information',\n helpText: \"Enter your details, we'll need these to set things up.\",\n fields: ['firstName', 'dobDay', 'dobMonth', 'dobYear', 'gender'],\n },\n {\n key: 'body-metrics',\n title: 'Personal details',\n helpText: 'Your height and weight, to calculate your BMI.',\n fields: ['heightCm', 'weightKg'],\n },\n {\n key: 'body-composition',\n title: 'Personal details',\n helpText: 'A few measurements, to estimate your body fat.',\n fields: ['neckCm', 'waistCm', 'hipsCm'],\n },\n {\n key: 'emergency',\n title: 'Emergency info',\n helpText: 'In case something happens during training.',\n fields: ['phone', 'emergencyName', 'emergencyRelation', 'emergencyPhone', 'bloodGroup'],\n },\n] as const;\n\nexport const MEMBER_ONBOARDING_TOTAL_STEPS = MEMBER_ONBOARDING_STEPS.length;\n\n/**\n * Fields validated before advancing each member step.\n * DERIVED from `MEMBER_ONBOARDING_STEPS` so the two can never disagree.\n */\nexport const MEMBER_STEP_FIELDS: (keyof MemberOnboardingForm)[][] = MEMBER_ONBOARDING_STEPS.map(\n (s) => [...s.fields],\n);\n\n// ─── Gym registration form (\"list your gym\") ────────────────────────────────\n\n/**\n * The gym-registration wizard's working shape.\n *\n * THREE steps, not five. The two it used to open with — \"your details\" and\n * \"contact details\" — asked a signed-in member for their name, gender, date of\n * birth and phone, every one of which onboarding already captured. Registering\n * a business asks about the BUSINESS; the person is already known.\n */\nexport const gymRegistrationFormSchema = z.object({\n // 1 — organisation\n orgName: z.string().trim().min(1, 'Organisation name is required'),\n orgDescription: z.string(),\n hqAddress: onboardingAddressSchema,\n orgWebsite: z.string(),\n gstNumber: z.string(),\n cinNumber: z.string(),\n numberOfCenters: requiredChoice(z.number(), 'Add the number of centers'),\n orgSocials: onboardingSocialLinksSchema,\n // 2 — center\n centerName: z.string().trim().min(1, 'Center name is required'),\n centerDescription: z.string(),\n centerTypes: z.array(z.string()).min(1, 'Pick at least one type'),\n centerAddress: onboardingAddressSchema,\n floorArea: z.number().nullable(),\n centerPhone1: formPhone,\n centerPhone2: optionalFormPhone,\n centerEmail: optionalFormEmail,\n hours: z.object({\n mon: onboardingDayHoursSchema,\n tue: onboardingDayHoursSchema,\n wed: onboardingDayHoursSchema,\n thu: onboardingDayHoursSchema,\n fri: onboardingDayHoursSchema,\n sat: onboardingDayHoursSchema,\n sun: onboardingDayHoursSchema,\n }),\n photos: z.array(z.string()),\n // 3 — plan\n planId: requiredChoice(z.string(), 'Choose a plan'),\n tenureId: z.string().nullable(),\n});\n\n/** Hand-written working shape (nullable \"unset\" fields); see the member form. */\nexport type GymRegistrationForm = {\n orgName: string;\n orgDescription: string;\n hqAddress: OnboardingAddress;\n orgWebsite: string;\n gstNumber: string;\n cinNumber: string;\n numberOfCenters: number | null;\n orgSocials: OnboardingSocialLinks;\n centerName: string;\n centerDescription: string;\n centerTypes: string[];\n centerAddress: OnboardingAddress;\n floorArea: number | null;\n centerPhone1: string;\n centerPhone2: string;\n centerEmail: string;\n hours: OnboardingWeeklyHours;\n photos: string[];\n planId: string | null;\n tenureId: string | null;\n};\n\nconst EMPTY_ONBOARDING_SOCIALS: OnboardingSocialLinks = {\n jansathi: '',\n youtube: '',\n instagram: '',\n facebook: '',\n};\n\n/** The opening hours a new center starts with. */\nexport const DEFAULT_ONBOARDING_HOURS: OnboardingWeeklyHours = {\n mon: { open: true, from: '06:00', to: '22:00' },\n tue: { open: true, from: '06:00', to: '22:00' },\n wed: { open: true, from: '06:00', to: '22:00' },\n thu: { open: true, from: '06:00', to: '22:00' },\n fri: { open: true, from: '06:00', to: '22:00' },\n sat: { open: true, from: '07:00', to: '21:00' },\n sun: { open: false, from: '07:00', to: '13:00' },\n};\n\nexport const EMPTY_GYM_REGISTRATION_FORM: GymRegistrationForm = {\n orgName: '',\n orgDescription: '',\n hqAddress: null,\n orgWebsite: '',\n gstNumber: '',\n cinNumber: '',\n numberOfCenters: null,\n orgSocials: { ...EMPTY_ONBOARDING_SOCIALS },\n centerName: '',\n centerDescription: '',\n centerTypes: [],\n centerAddress: null,\n floorArea: null,\n centerPhone1: '',\n centerPhone2: '',\n centerEmail: '',\n hours: structuredClone(DEFAULT_ONBOARDING_HOURS),\n photos: [],\n planId: null,\n tenureId: null,\n};\n\n/**\n * The gym-registration wizard, described once.\n *\n * The last step is the pricing screen, which renders its own header; its title\n * and help line are unused by the wizard chrome but kept so every step has one.\n */\nexport const GYM_REGISTRATION_STEPS: readonly OnboardingStepConfig<\n keyof GymRegistrationForm & string\n>[] = [\n {\n key: 'organisation',\n title: 'Your organisation',\n helpText: 'Tell us about the business that runs the gym.',\n fields: ['orgName', 'hqAddress', 'numberOfCenters'],\n },\n {\n key: 'center',\n title: 'Your first center',\n helpText: 'Set up your primary center. You can add more later.',\n fields: [\n 'centerName',\n 'centerTypes',\n 'centerAddress',\n 'centerPhone1',\n 'centerPhone2',\n 'centerEmail',\n ],\n },\n {\n key: 'plan',\n title: 'Choose your plan',\n helpText: 'Pick the plan that fits your gym.',\n fields: ['planId', 'tenureId'],\n },\n] as const;\n\nexport const GYM_REGISTRATION_TOTAL_STEPS = GYM_REGISTRATION_STEPS.length;\n\n/**\n * Fields validated (via `form.trigger`) before advancing each step.\n * DERIVED from `GYM_REGISTRATION_STEPS` so the two can never disagree.\n */\nexport const GYM_REGISTRATION_STEP_FIELDS: (keyof GymRegistrationForm)[][] =\n GYM_REGISTRATION_STEPS.map((s) => [...s.fields]);\n\n// ─── Retained: the owner onboarding wizard ───────────────────────────────────\n// The gym-registration flow above replaces this, but the owner console's repo\n// still ships the four-step owner wizard and cannot migrate in the same change\n// that publishes this package. Nothing above imports any of it.\n//\n// TODO(onboarding-migration): delete once the owner wizard moves to\n// `GYM_REGISTRATION_STEPS`.\n\n// ─── Owner onboarding form ──────────────────────────────────────────────────\n\n/** Age bounds for a gym owner (the DOB itself stays optional). */\nexport const OWNER_MIN_AGE_YEARS = 16;\nexport const OWNER_MAX_AGE_YEARS = 100;\n\nexport const ownerOnboardingFormSchema = z\n .object({\n // 1 — basics\n firstName: z.string().trim().min(1, 'First name is required'),\n lastName: z.string().trim(),\n dobDay: z.string(),\n dobMonth: z.string(),\n dobYear: z.string(),\n gender: requiredChoice(z.enum(['male', 'female', 'other']), 'Select your gender'),\n // 2 — contact\n phone: formPhone,\n altEmail: optionalFormEmail,\n website: z.string(),\n noWebsite: z.boolean(),\n address: onboardingAddressSchema,\n socials: onboardingSocialLinksSchema,\n // 3 — organisation\n orgName: z.string().trim().min(1, 'Organisation name is required'),\n orgDescription: z.string(),\n hqAddress: onboardingAddressSchema,\n gstNumber: z.string(),\n cinNumber: z.string(),\n numberOfCenters: requiredChoice(z.number(), 'Add the number of centers'),\n orgSocials: onboardingSocialLinksSchema,\n // 4 — center\n centerName: z.string().trim().min(1, 'Center name is required'),\n centerDescription: z.string(),\n centerTypes: z.array(z.string()).min(1, 'Pick at least one type'),\n centerAddress: onboardingAddressSchema,\n floorArea: z.number().nullable(),\n centerPhone1: formPhone,\n centerPhone2: optionalFormPhone,\n centerEmail: optionalFormEmail,\n hours: z.object({\n mon: onboardingDayHoursSchema,\n tue: onboardingDayHoursSchema,\n wed: onboardingDayHoursSchema,\n thu: onboardingDayHoursSchema,\n fri: onboardingDayHoursSchema,\n sat: onboardingDayHoursSchema,\n sun: onboardingDayHoursSchema,\n }),\n photos: z.array(z.string()),\n /** GCS URL of the owner's uploaded photo. Optional. */\n avatarUrl: z.string(),\n // 5 — plan\n planId: requiredChoice(z.string(), 'Choose a plan'),\n tenureId: z.string().nullable(),\n })\n /**\n * The owner's DOB is optional, so this only engages once any cell is touched:\n * then all three are required and must form a real, plausible date.\n */\n .superRefine((v, ctx) => {\n const touched = Boolean(v.dobDay || v.dobMonth || v.dobYear);\n if (!touched) return;\n\n const fail = (message: string) => ctx.addIssue({ code: 'custom', path: ['dobDay'], message });\n\n if (!v.dobDay || !v.dobMonth || v.dobYear.length !== 4) {\n fail('Enter the full date of birth');\n return;\n }\n\n const day = Number(v.dobDay);\n const month = Number(v.dobMonth);\n const year = Number(v.dobYear);\n\n if (!isRealDate(year, month, day)) {\n fail('That date does not exist');\n return;\n }\n\n const age = ageInYears(year, month, day);\n if (age < OWNER_MIN_AGE_YEARS) fail(`You must be at least ${OWNER_MIN_AGE_YEARS}`);\n else if (age > OWNER_MAX_AGE_YEARS) fail('Check the year of birth');\n });\n\n/** Hand-written working shape (nullable \"unset\" fields); see the member form. */\nexport type OwnerOnboardingForm = {\n firstName: string;\n lastName: string;\n dobDay: string;\n dobMonth: string;\n dobYear: string;\n gender: 'male' | 'female' | 'other' | null;\n phone: string;\n altEmail: string;\n website: string;\n noWebsite: boolean;\n address: OnboardingAddress;\n socials: OnboardingSocialLinks;\n orgName: string;\n orgDescription: string;\n hqAddress: OnboardingAddress;\n gstNumber: string;\n cinNumber: string;\n numberOfCenters: number | null;\n orgSocials: OnboardingSocialLinks;\n centerName: string;\n centerDescription: string;\n centerTypes: string[];\n centerAddress: OnboardingAddress;\n floorArea: number | null;\n centerPhone1: string;\n centerPhone2: string;\n centerEmail: string;\n hours: OnboardingWeeklyHours;\n photos: string[];\n avatarUrl: string;\n planId: string | null;\n tenureId: string | null;\n};\n\nexport const EMPTY_OWNER_ONBOARDING_FORM: OwnerOnboardingForm = {\n firstName: '',\n lastName: '',\n dobDay: '',\n dobMonth: '',\n dobYear: '',\n gender: null,\n phone: '',\n altEmail: '',\n website: '',\n noWebsite: false,\n address: null,\n socials: { ...EMPTY_ONBOARDING_SOCIALS },\n orgName: '',\n orgDescription: '',\n hqAddress: null,\n gstNumber: '',\n cinNumber: '',\n numberOfCenters: null,\n orgSocials: { ...EMPTY_ONBOARDING_SOCIALS },\n centerName: '',\n centerDescription: '',\n centerTypes: [],\n centerAddress: null,\n floorArea: null,\n centerPhone1: '',\n centerPhone2: '',\n centerEmail: '',\n hours: structuredClone(DEFAULT_ONBOARDING_HOURS),\n photos: [],\n avatarUrl: '',\n planId: null,\n tenureId: null,\n};\n\n/**\n * The owner wizard, described once.\n *\n * The DOB cells belong in step 1's `fields` even though the date is optional:\n * the refine above still rejects a half-typed date, and a field missing from\n * this map would be unreachable twice over — Continue would not surface it, and\n * the final submit (which finds the offending STEP by searching these lists)\n * would find no step and leave the plan screen dead.\n *\n * The last step is the pricing screen, which renders its own header; its title\n * and help line are unused by the wizard chrome but kept so every step has one.\n */\nexport const OWNER_ONBOARDING_STEPS: readonly OnboardingStepConfig<\n keyof OwnerOnboardingForm & string\n>[] = [\n {\n key: 'basics',\n title: 'Your details',\n helpText: 'A few basics about you.',\n fields: ['firstName', 'gender', 'dobDay', 'dobMonth', 'dobYear'],\n },\n {\n key: 'contact',\n title: 'Contact details',\n helpText: 'How members and we can reach you.',\n fields: ['phone', 'altEmail', 'address'],\n },\n {\n key: 'organisation',\n title: 'Organisation',\n helpText: 'Tell us about your business.',\n fields: ['orgName', 'hqAddress', 'numberOfCenters'],\n },\n {\n key: 'center',\n title: 'Your first center',\n helpText: 'Set up your primary center. You can add more later.',\n fields: [\n 'centerName',\n 'centerTypes',\n 'centerAddress',\n 'centerPhone1',\n 'centerPhone2',\n 'centerEmail',\n ],\n },\n {\n key: 'plan',\n title: 'Choose your plan',\n helpText: 'Pick the plan that fits your gym.',\n fields: ['planId', 'tenureId'],\n },\n] as const;\n\nexport const OWNER_ONBOARDING_TOTAL_STEPS = OWNER_ONBOARDING_STEPS.length;\n\n/**\n * Fields validated (via `form.trigger`) before advancing each owner step.\n * DERIVED from `OWNER_ONBOARDING_STEPS` so the two can never disagree.\n */\nexport const OWNER_STEP_FIELDS: (keyof OwnerOnboardingForm)[][] = OWNER_ONBOARDING_STEPS.map(\n (s) => [...s.fields],\n);\n\n// ─── Gym selection (member step 0) ──────────────────────────────────────────\n\nexport const gymSelectionSchema = z.object({\n gymId: z.string().trim().min(1, 'Enter your gym id or pick a gym below'),\n});\nexport type GymSelection = z.infer<typeof gymSelectionSchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"onboarding-form.js","sourceRoot":"","sources":["../src/onboarding-form.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — Onboarding & gym-registration FORM schemas\n * ===========================================================\n * The client-side validation contract for the member onboarding wizard and the\n * \"list your gym\" wizard.\n *\n * Why these live here and are distinct from the `*OnboardingBody` API schemas:\n * a half-filled form legitimately holds `null` for a choice the user has not\n * made yet, whereas the API body only ever sees completed values. So these are\n * the SAME rules expressed over the form's working shape — every message,\n * regex and bound is defined once, here, and both sides agree on it.\n *\n * Everything reuses the primitives in `shared.ts` / `common.ts`\n * (`INDIAN_STATES`, `CENTER_TYPES`, `WEEKDAYS`, phone/pincode rules) rather\n * than restating them.\n *\n * @module gymmonk-schema/onboarding-form\n */\n\nimport { z } from 'zod';\nimport { type CenterType, centerTypeSchema } from './center.js';\nimport { ageInYears, isRealDate, PHONE_REGEX } from './common.js';\nimport type { WEEKDAYS } from './shared.js';\nimport { type Address, addressSchema } from './shared.js';\n\n// ─── Field-level rules (shared by both wizards) ──────────────────────────────\n\n/** A required 10-digit Indian mobile, as typed into a form field. */\nconst formPhone = z.string().regex(PHONE_REGEX, 'Enter a valid 10-digit mobile number');\n\n/** An optional phone: blank is allowed, anything else must be valid. */\nconst optionalFormPhone = z\n .string()\n .refine((v) => v === '' || PHONE_REGEX.test(v), 'Enter a valid 10-digit mobile number');\n\n/** An optional email: blank is allowed, anything else must look like an email. */\nconst optionalFormEmail = z\n .string()\n .refine((v) => v === '' || /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(v), {\n message: 'Enter a valid email',\n });\n\n/** A choice the user has not made yet is `null` until they pick one. */\nconst requiredChoice = <T extends z.ZodTypeAny>(inner: T, message: string) =>\n inner.nullable().refine((v: unknown) => v !== null, { message });\n\n// ─── Address (form shape: one atomic value, null until picked) ────────────\n\n/**\n * An address inside an onboarding form is ONE cell, not four.\n *\n * It used to be `street` + `city` + `state` + `pincode`, each independently\n * fillable, which is why this module needed rules for a half-filled address.\n * Now it is picked through the location drawer, which either hands back a\n * complete `Address` — area chain, ids, names, ancestry, coordinates — or\n * nothing at all. So there is no partial state left to validate, and `null`\n * simply means \"not chosen yet\".\n */\nexport const onboardingAddressSchema = requiredChoice(addressSchema, 'Add your address');\n\n/** Working shape of an address inside an onboarding form. */\nexport type OnboardingAddress = Address | null;\n\n/** An address has been picked. */\nexport function isOnboardingAddressComplete(a: OnboardingAddress): boolean {\n return a !== null;\n}\n\n// ─── Social links (form shape: every handle is a plain string) ───────────────\n\nexport const onboardingSocialLinksSchema = z.object({\n jansathi: z.string(),\n youtube: z.string(),\n instagram: z.string(),\n facebook: z.string(),\n});\nexport type OnboardingSocialLinks = z.infer<typeof onboardingSocialLinksSchema>;\n\n// ─── Opening hours (form shape) ─────────────────────────────────────────────\n\nexport const onboardingDayHoursSchema = z.object({\n open: z.boolean(),\n from: z.string(),\n to: z.string(),\n});\nexport type OnboardingDayHours = z.infer<typeof onboardingDayHoursSchema>;\nexport type OnboardingWeeklyHours = Record<(typeof WEEKDAYS)[number], OnboardingDayHours>;\n\n// ─── Member onboarding form ─────────────────────────────────────────────────\n\n/** Age bounds a gym membership realistically spans. */\nexport const MEMBER_MIN_AGE_YEARS = 10;\nexport const MEMBER_MAX_AGE_YEARS = 100;\n\n/**\n * The three-cell date-of-birth rule, in ONE place.\n *\n * Each cell is bounded by its own input, but only the assembled date can catch\n * an impossible one (31 Feb) or an implausible age. The issue is attached to\n * `dobDay` so it renders under the field group rather than under one cell.\n *\n * Exported because the member's own profile editor asks for the SAME date with\n * the same three cells. When the rule lived inline in the wizard, that editor\n * had a free-text box parsed with `new Date()` instead, which accepted \"March\n * 18, 1994\", accepted nonsense just as happily, and silently dropped whatever\n * it could not read.\n */\nexport function refineMemberDob(\n v: { dobDay: string; dobMonth: string; dobYear: string },\n ctx: z.RefinementCtx,\n): void {\n if (!v.dobDay || !v.dobMonth || v.dobYear.length !== 4) return;\n\n const day = Number(v.dobDay);\n const month = Number(v.dobMonth);\n const year = Number(v.dobYear);\n\n if (!isRealDate(year, month, day)) {\n ctx.addIssue({ code: 'custom', path: ['dobDay'], message: 'That date does not exist' });\n return;\n }\n\n const age = ageInYears(year, month, day);\n if (age < MEMBER_MIN_AGE_YEARS) {\n ctx.addIssue({\n code: 'custom',\n path: ['dobDay'],\n message: `You must be at least ${MEMBER_MIN_AGE_YEARS} to join`,\n });\n } else if (age > MEMBER_MAX_AGE_YEARS) {\n ctx.addIssue({ code: 'custom', path: ['dobDay'], message: 'Check the year of birth' });\n }\n}\n\nexport const memberOnboardingFormSchema = z\n .object({\n firstName: z.string().trim().min(1, 'First name is required'),\n lastName: z.string().trim(),\n dobDay: z.string().min(1, 'Enter the day'),\n dobMonth: z.string().min(1, 'Enter the month'),\n dobYear: z.string().length(4, 'Enter a 4-digit year'),\n gender: requiredChoice(z.enum(['male', 'female', 'other']), 'Select your gender'),\n heightCm: requiredChoice(z.number(), 'Add your height'),\n weightKg: requiredChoice(z.number(), 'Add your weight'),\n neckCm: requiredChoice(z.number(), 'Add your neck'),\n waistCm: requiredChoice(z.number(), 'Add your waist'),\n hipsCm: z.number().nullable(),\n phone: formPhone,\n emergencyName: z.string().trim().min(1, 'Add an emergency contact name'),\n /**\n * How that person is related — OPTIONAL, matching `emergencyContactSchema`.\n *\n * Asked here so the three places that write an emergency contact (this\n * wizard, the member's own Legal & Emergency editor, the owner's desk-entry\n * forms) record the SAME three facts. It used to be asked in the other two\n * and not here, so a member who onboarded themselves had no relationship\n * stored, and the owner console's read fell back to showing the contact's\n * NAME in the relationship slot — which the next owner edit then wrote back\n * as the name.\n *\n * Optional rather than required because a wizard is not the place to block\n * someone over context, and the number itself is what matters in an\n * emergency.\n */\n emergencyRelation: z.string().trim().max(60),\n emergencyPhone: formPhone,\n bloodGroup: requiredChoice(z.string(), 'Select your blood group'),\n /** GCS URL of the uploaded photo. Optional — it can be added later. */\n avatarUrl: z.string(),\n })\n .superRefine(refineMemberDob)\n /**\n * The API requires the hips measurement for female members (needed for the\n * body-fat estimate), so it fails inline on the measurements step instead of\n * 422-ing at Finish. Own refine — no early-return coupling with the DOB rule.\n */\n .superRefine((v, ctx) => {\n if (v.gender === 'female' && v.hipsCm == null) {\n ctx.addIssue({\n code: 'custom',\n path: ['hipsCm'],\n message: 'Hips measurement is required',\n });\n }\n });\n\n/**\n * The form's working shape. Hand-written (not `z.infer`) so unset numeric /\n * choice fields can hold `null` while the schema's `requiredChoice` rules still\n * drive the field errors — the resolver is cast to this type at `useForm`.\n */\nexport type MemberOnboardingForm = {\n firstName: string;\n lastName: string;\n dobDay: string;\n dobMonth: string;\n dobYear: string;\n gender: 'male' | 'female' | 'other' | null;\n heightCm: number | null;\n weightKg: number | null;\n neckCm: number | null;\n waistCm: number | null;\n hipsCm: number | null;\n phone: string;\n emergencyName: string;\n emergencyRelation: string;\n emergencyPhone: string;\n bloodGroup: string | null;\n avatarUrl: string;\n};\n\nexport const EMPTY_MEMBER_ONBOARDING_FORM: MemberOnboardingForm = {\n firstName: '',\n lastName: '',\n dobDay: '',\n dobMonth: '',\n dobYear: '',\n gender: null,\n heightCm: null,\n weightKg: null,\n neckCm: null,\n waistCm: null,\n hipsCm: null,\n phone: '',\n emergencyName: '',\n emergencyRelation: '',\n emergencyPhone: '',\n bloodGroup: null,\n avatarUrl: '',\n};\n\n/**\n * One onboarding step, described ONCE.\n *\n * The wizard chrome (screen name, help line, progress denominator) and the\n * validation gate used to live in the page as a local `META` array beside a\n * separate field map — two lists that had to be kept the same length by hand.\n * They are one list here, so a step cannot exist without a title or be left\n * without a validation gate.\n */\nexport interface OnboardingStepConfig<TField extends string> {\n /** Stable identifier — safe for analytics and resume, unlike the index. */\n key: string;\n /** Screen name in the step header. */\n title: string;\n /** Help line under the screen name. */\n helpText: string;\n /**\n * Fields validated before this step may be left. Naming a parent path (e.g.\n * `address`) validates its nested fields too.\n */\n fields: readonly TField[];\n}\n\nexport const MEMBER_ONBOARDING_STEPS: readonly OnboardingStepConfig<\n keyof MemberOnboardingForm & string\n>[] = [\n {\n key: 'basics',\n title: 'Basic information',\n helpText: \"Enter your details, we'll need these to set things up.\",\n fields: ['firstName', 'dobDay', 'dobMonth', 'dobYear', 'gender'],\n },\n {\n key: 'body-metrics',\n title: 'Personal details',\n helpText: 'Your height and weight, to calculate your BMI.',\n fields: ['heightCm', 'weightKg'],\n },\n {\n key: 'body-composition',\n title: 'Personal details',\n helpText: 'A few measurements, to estimate your body fat.',\n fields: ['neckCm', 'waistCm', 'hipsCm'],\n },\n {\n key: 'emergency',\n title: 'Emergency info',\n helpText: 'In case something happens during training.',\n fields: ['phone', 'emergencyName', 'emergencyRelation', 'emergencyPhone', 'bloodGroup'],\n },\n] as const;\n\nexport const MEMBER_ONBOARDING_TOTAL_STEPS = MEMBER_ONBOARDING_STEPS.length;\n\n/**\n * Fields validated before advancing each member step.\n * DERIVED from `MEMBER_ONBOARDING_STEPS` so the two can never disagree.\n */\nexport const MEMBER_STEP_FIELDS: (keyof MemberOnboardingForm)[][] = MEMBER_ONBOARDING_STEPS.map(\n (s) => [...s.fields],\n);\n\n// ─── Gym registration form (\"list your gym\") ────────────────────────────────\n\n/**\n * The gym-registration wizard's working shape.\n *\n * THREE steps, not five. The two it used to open with — \"your details\" and\n * \"contact details\" — asked a signed-in member for their name, gender, date of\n * birth and phone, every one of which onboarding already captured. Registering\n * a business asks about the BUSINESS; the person is already known.\n */\n/**\n * The gym-registration form's FIELD SHAPE, exported separately from the schema.\n *\n * Separate because the schema carries a `superRefine` (see\n * `refineGymRegistrationForm`) and a refined object cannot be `.extend()`ed —\n * zod hands back a ZodEffects, and calling `.extend` on one throws at MODULE\n * LOAD, which no build catches and which takes the whole page down on import.\n * The web client adds the two pasted Maps links to this form, so it needs the\n * shape and the refinement as separate pieces it can recombine.\n */\nexport const gymRegistrationFormShape = {\n // 1 — organisation\n orgName: z.string().trim().min(1, 'Organisation name is required'),\n orgDescription: z.string(),\n hqAddress: onboardingAddressSchema,\n orgWebsite: z.string(),\n gstNumber: z.string(),\n cinNumber: z.string(),\n numberOfCenters: requiredChoice(z.number(), 'Add the number of centers'),\n orgSocials: onboardingSocialLinksSchema,\n // 2 — center\n centerName: z.string().trim().min(1, 'Center name is required'),\n centerDescription: z.string(),\n /**\n * The real enum, not `z.array(z.string())`.\n *\n * It was strings, and `toGymRegistrationBody` then CAST them to the enum on\n * the way to the API. A cast is not a check: retiring a centre type left the\n * wizard perfectly happy to submit the retired value and the server to\n * reject the whole registration, with the owner told only that something was\n * wrong. Now a value the API will not accept cannot leave the form.\n */\n centerTypes: z.array(centerTypeSchema).min(1, 'Pick at least one type'),\n /**\n * \"Same as organisation location\".\n *\n * Most gyms register one branch at the address they just typed on the\n * previous screen, and asking for it twice invites a typo between two things\n * that are meant to be the same place — one of which decides whether a\n * member standing at the door is close enough to check in.\n *\n * It is the CHOICE that is stored, not a copy of the answer. The address is\n * resolved from the organisation at submit, so an owner who goes back and\n * corrects the HQ address does not leave a stale duplicate behind on the\n * centre.\n */\n centerSameAsOrg: z.boolean(),\n /**\n * Nullable here and required by `refineGymRegistrationForm` instead, because\n * \"required\" is now conditional — see that function.\n */\n centerAddress: addressSchema.nullable(),\n floorArea: z.number().nullable(),\n centerPhone1: formPhone,\n centerPhone2: optionalFormPhone,\n centerEmail: optionalFormEmail,\n hours: z.object({\n mon: onboardingDayHoursSchema,\n tue: onboardingDayHoursSchema,\n wed: onboardingDayHoursSchema,\n thu: onboardingDayHoursSchema,\n fri: onboardingDayHoursSchema,\n sat: onboardingDayHoursSchema,\n sun: onboardingDayHoursSchema,\n }),\n photos: z.array(z.string()),\n // 3 — plan\n planId: requiredChoice(z.string(), 'Choose a plan'),\n tenureId: z.string().nullable(),\n};\n\n/**\n * The centre needs an address unless it is inheriting the organisation's.\n *\n * A cross-field rule, so it cannot live on `centerAddress` itself. The issue is\n * raised AT `centerAddress` deliberately: the wizard validates each step by\n * calling `trigger` with that step's field names, and an error parked on the\n * object root would match none of them — Continue would refuse to advance with\n * nothing on screen explaining why. That exact shape of bug has cost this\n * codebase two silent forms already.\n *\n * Structurally typed rather than tied to `GymRegistrationForm` so the web\n * client can apply the same function to its own extended schema.\n */\nexport function refineGymRegistrationForm(\n v: { centerSameAsOrg: boolean; centerAddress: Address | null },\n ctx: z.RefinementCtx,\n): void {\n if (v.centerSameAsOrg) return;\n if (v.centerAddress === null) {\n ctx.addIssue({ code: 'custom', path: ['centerAddress'], message: 'Add your address' });\n }\n}\n\nexport const gymRegistrationFormSchema = z\n .object(gymRegistrationFormShape)\n .superRefine(refineGymRegistrationForm);\n\n/** Hand-written working shape (nullable \"unset\" fields); see the member form. */\nexport type GymRegistrationForm = {\n orgName: string;\n orgDescription: string;\n hqAddress: OnboardingAddress;\n orgWebsite: string;\n gstNumber: string;\n cinNumber: string;\n numberOfCenters: number | null;\n orgSocials: OnboardingSocialLinks;\n centerName: string;\n centerDescription: string;\n centerTypes: CenterType[];\n /** \"Same as organisation location\" — see `gymRegistrationFormShape`. */\n centerSameAsOrg: boolean;\n centerAddress: OnboardingAddress;\n floorArea: number | null;\n centerPhone1: string;\n centerPhone2: string;\n centerEmail: string;\n hours: OnboardingWeeklyHours;\n photos: string[];\n planId: string | null;\n tenureId: string | null;\n};\n\nconst EMPTY_ONBOARDING_SOCIALS: OnboardingSocialLinks = {\n jansathi: '',\n youtube: '',\n instagram: '',\n facebook: '',\n};\n\n/** The opening hours a new center starts with. */\nexport const DEFAULT_ONBOARDING_HOURS: OnboardingWeeklyHours = {\n mon: { open: true, from: '06:00', to: '22:00' },\n tue: { open: true, from: '06:00', to: '22:00' },\n wed: { open: true, from: '06:00', to: '22:00' },\n thu: { open: true, from: '06:00', to: '22:00' },\n fri: { open: true, from: '06:00', to: '22:00' },\n sat: { open: true, from: '07:00', to: '21:00' },\n sun: { open: false, from: '07:00', to: '13:00' },\n};\n\nexport const EMPTY_GYM_REGISTRATION_FORM: GymRegistrationForm = {\n orgName: '',\n orgDescription: '',\n hqAddress: null,\n orgWebsite: '',\n gstNumber: '',\n cinNumber: '',\n numberOfCenters: null,\n orgSocials: { ...EMPTY_ONBOARDING_SOCIALS },\n centerName: '',\n centerDescription: '',\n centerTypes: [],\n // Unticked. A pre-ticked box would put the organisation's address on the\n // centre without the owner ever looking at it — and that address is what a\n // member's check-in is measured against.\n centerSameAsOrg: false,\n centerAddress: null,\n floorArea: null,\n centerPhone1: '',\n centerPhone2: '',\n centerEmail: '',\n hours: structuredClone(DEFAULT_ONBOARDING_HOURS),\n photos: [],\n planId: null,\n tenureId: null,\n};\n\n/**\n * The gym-registration wizard, described once.\n *\n * The last step is the pricing screen, which renders its own header; its title\n * and help line are unused by the wizard chrome but kept so every step has one.\n */\nexport const GYM_REGISTRATION_STEPS: readonly OnboardingStepConfig<\n keyof GymRegistrationForm & string\n>[] = [\n {\n key: 'organisation',\n title: 'Your organisation',\n helpText: 'Tell us about the business that runs the gym.',\n fields: ['orgName', 'hqAddress', 'numberOfCenters'],\n },\n {\n key: 'center',\n title: 'Your first center',\n helpText: 'Set up your primary center. You can add more later.',\n fields: [\n 'centerName',\n 'centerTypes',\n 'centerAddress',\n 'centerPhone1',\n 'centerPhone2',\n 'centerEmail',\n ],\n },\n {\n key: 'plan',\n title: 'Choose your plan',\n helpText: 'Pick the plan that fits your gym.',\n fields: ['planId', 'tenureId'],\n },\n] as const;\n\nexport const GYM_REGISTRATION_TOTAL_STEPS = GYM_REGISTRATION_STEPS.length;\n\n/**\n * Fields validated (via `form.trigger`) before advancing each step.\n * DERIVED from `GYM_REGISTRATION_STEPS` so the two can never disagree.\n */\nexport const GYM_REGISTRATION_STEP_FIELDS: (keyof GymRegistrationForm)[][] =\n GYM_REGISTRATION_STEPS.map((s) => [...s.fields]);\n\n// ─── Retained: the owner onboarding wizard ───────────────────────────────────\n// The gym-registration flow above replaces this, but the owner console's repo\n// still ships the four-step owner wizard and cannot migrate in the same change\n// that publishes this package. Nothing above imports any of it.\n//\n// TODO(onboarding-migration): delete once the owner wizard moves to\n// `GYM_REGISTRATION_STEPS`.\n\n// ─── Owner onboarding form ──────────────────────────────────────────────────\n\n/** Age bounds for a gym owner (the DOB itself stays optional). */\nexport const OWNER_MIN_AGE_YEARS = 16;\nexport const OWNER_MAX_AGE_YEARS = 100;\n\nexport const ownerOnboardingFormSchema = z\n .object({\n // 1 — basics\n firstName: z.string().trim().min(1, 'First name is required'),\n lastName: z.string().trim(),\n dobDay: z.string(),\n dobMonth: z.string(),\n dobYear: z.string(),\n gender: requiredChoice(z.enum(['male', 'female', 'other']), 'Select your gender'),\n // 2 — contact\n phone: formPhone,\n altEmail: optionalFormEmail,\n website: z.string(),\n noWebsite: z.boolean(),\n address: onboardingAddressSchema,\n socials: onboardingSocialLinksSchema,\n // 3 — organisation\n orgName: z.string().trim().min(1, 'Organisation name is required'),\n orgDescription: z.string(),\n hqAddress: onboardingAddressSchema,\n gstNumber: z.string(),\n cinNumber: z.string(),\n numberOfCenters: requiredChoice(z.number(), 'Add the number of centers'),\n orgSocials: onboardingSocialLinksSchema,\n // 4 — center\n centerName: z.string().trim().min(1, 'Center name is required'),\n centerDescription: z.string(),\n centerTypes: z.array(z.string()).min(1, 'Pick at least one type'),\n centerAddress: onboardingAddressSchema,\n floorArea: z.number().nullable(),\n centerPhone1: formPhone,\n centerPhone2: optionalFormPhone,\n centerEmail: optionalFormEmail,\n hours: z.object({\n mon: onboardingDayHoursSchema,\n tue: onboardingDayHoursSchema,\n wed: onboardingDayHoursSchema,\n thu: onboardingDayHoursSchema,\n fri: onboardingDayHoursSchema,\n sat: onboardingDayHoursSchema,\n sun: onboardingDayHoursSchema,\n }),\n photos: z.array(z.string()),\n /** GCS URL of the owner's uploaded photo. Optional. */\n avatarUrl: z.string(),\n // 5 — plan\n planId: requiredChoice(z.string(), 'Choose a plan'),\n tenureId: z.string().nullable(),\n })\n /**\n * The owner's DOB is optional, so this only engages once any cell is touched:\n * then all three are required and must form a real, plausible date.\n */\n .superRefine((v, ctx) => {\n const touched = Boolean(v.dobDay || v.dobMonth || v.dobYear);\n if (!touched) return;\n\n const fail = (message: string) => ctx.addIssue({ code: 'custom', path: ['dobDay'], message });\n\n if (!v.dobDay || !v.dobMonth || v.dobYear.length !== 4) {\n fail('Enter the full date of birth');\n return;\n }\n\n const day = Number(v.dobDay);\n const month = Number(v.dobMonth);\n const year = Number(v.dobYear);\n\n if (!isRealDate(year, month, day)) {\n fail('That date does not exist');\n return;\n }\n\n const age = ageInYears(year, month, day);\n if (age < OWNER_MIN_AGE_YEARS) fail(`You must be at least ${OWNER_MIN_AGE_YEARS}`);\n else if (age > OWNER_MAX_AGE_YEARS) fail('Check the year of birth');\n });\n\n/** Hand-written working shape (nullable \"unset\" fields); see the member form. */\nexport type OwnerOnboardingForm = {\n firstName: string;\n lastName: string;\n dobDay: string;\n dobMonth: string;\n dobYear: string;\n gender: 'male' | 'female' | 'other' | null;\n phone: string;\n altEmail: string;\n website: string;\n noWebsite: boolean;\n address: OnboardingAddress;\n socials: OnboardingSocialLinks;\n orgName: string;\n orgDescription: string;\n hqAddress: OnboardingAddress;\n gstNumber: string;\n cinNumber: string;\n numberOfCenters: number | null;\n orgSocials: OnboardingSocialLinks;\n centerName: string;\n centerDescription: string;\n centerTypes: string[];\n centerAddress: OnboardingAddress;\n floorArea: number | null;\n centerPhone1: string;\n centerPhone2: string;\n centerEmail: string;\n hours: OnboardingWeeklyHours;\n photos: string[];\n avatarUrl: string;\n planId: string | null;\n tenureId: string | null;\n};\n\nexport const EMPTY_OWNER_ONBOARDING_FORM: OwnerOnboardingForm = {\n firstName: '',\n lastName: '',\n dobDay: '',\n dobMonth: '',\n dobYear: '',\n gender: null,\n phone: '',\n altEmail: '',\n website: '',\n noWebsite: false,\n address: null,\n socials: { ...EMPTY_ONBOARDING_SOCIALS },\n orgName: '',\n orgDescription: '',\n hqAddress: null,\n gstNumber: '',\n cinNumber: '',\n numberOfCenters: null,\n orgSocials: { ...EMPTY_ONBOARDING_SOCIALS },\n centerName: '',\n centerDescription: '',\n centerTypes: [],\n centerAddress: null,\n floorArea: null,\n centerPhone1: '',\n centerPhone2: '',\n centerEmail: '',\n hours: structuredClone(DEFAULT_ONBOARDING_HOURS),\n photos: [],\n avatarUrl: '',\n planId: null,\n tenureId: null,\n};\n\n/**\n * The owner wizard, described once.\n *\n * The DOB cells belong in step 1's `fields` even though the date is optional:\n * the refine above still rejects a half-typed date, and a field missing from\n * this map would be unreachable twice over — Continue would not surface it, and\n * the final submit (which finds the offending STEP by searching these lists)\n * would find no step and leave the plan screen dead.\n *\n * The last step is the pricing screen, which renders its own header; its title\n * and help line are unused by the wizard chrome but kept so every step has one.\n */\nexport const OWNER_ONBOARDING_STEPS: readonly OnboardingStepConfig<\n keyof OwnerOnboardingForm & string\n>[] = [\n {\n key: 'basics',\n title: 'Your details',\n helpText: 'A few basics about you.',\n fields: ['firstName', 'gender', 'dobDay', 'dobMonth', 'dobYear'],\n },\n {\n key: 'contact',\n title: 'Contact details',\n helpText: 'How members and we can reach you.',\n fields: ['phone', 'altEmail', 'address'],\n },\n {\n key: 'organisation',\n title: 'Organisation',\n helpText: 'Tell us about your business.',\n fields: ['orgName', 'hqAddress', 'numberOfCenters'],\n },\n {\n key: 'center',\n title: 'Your first center',\n helpText: 'Set up your primary center. You can add more later.',\n fields: [\n 'centerName',\n 'centerTypes',\n 'centerAddress',\n 'centerPhone1',\n 'centerPhone2',\n 'centerEmail',\n ],\n },\n {\n key: 'plan',\n title: 'Choose your plan',\n helpText: 'Pick the plan that fits your gym.',\n fields: ['planId', 'tenureId'],\n },\n] as const;\n\nexport const OWNER_ONBOARDING_TOTAL_STEPS = OWNER_ONBOARDING_STEPS.length;\n\n/**\n * Fields validated (via `form.trigger`) before advancing each owner step.\n * DERIVED from `OWNER_ONBOARDING_STEPS` so the two can never disagree.\n */\nexport const OWNER_STEP_FIELDS: (keyof OwnerOnboardingForm)[][] = OWNER_ONBOARDING_STEPS.map(\n (s) => [...s.fields],\n);\n\n// ─── Gym selection (member step 0) ──────────────────────────────────────────\n\nexport const gymSelectionSchema = z.object({\n gymId: z.string().trim().min(1, 'Enter your gym id or pick a gym below'),\n});\nexport type GymSelection = z.infer<typeof gymSelectionSchema>;\n"]}
|
package/dist/onboarding.d.ts
CHANGED
|
@@ -313,13 +313,25 @@ export declare const ownerOnboardingBodySchema: z.ZodObject<{
|
|
|
313
313
|
yoga: "yoga";
|
|
314
314
|
crossfit: "crossfit";
|
|
315
315
|
gym: "gym";
|
|
316
|
+
functional: "functional";
|
|
317
|
+
bodybuilding: "bodybuilding";
|
|
318
|
+
powerlifting: "powerlifting";
|
|
319
|
+
calisthenics: "calisthenics";
|
|
320
|
+
hiit: "hiit";
|
|
321
|
+
spinning: "spinning";
|
|
322
|
+
bootcamp: "bootcamp";
|
|
323
|
+
aerobics: "aerobics";
|
|
324
|
+
"sports-conditioning": "sports-conditioning";
|
|
316
325
|
pilates: "pilates";
|
|
326
|
+
meditation: "meditation";
|
|
317
327
|
zumba: "zumba";
|
|
318
|
-
|
|
328
|
+
dance: "dance";
|
|
319
329
|
boxing: "boxing";
|
|
330
|
+
kickboxing: "kickboxing";
|
|
331
|
+
mma: "mma";
|
|
332
|
+
"martial-arts": "martial-arts";
|
|
320
333
|
swimming: "swimming";
|
|
321
|
-
|
|
322
|
-
"personal-training": "personal-training";
|
|
334
|
+
physiotherapy: "physiotherapy";
|
|
323
335
|
}>>;
|
|
324
336
|
address: z.ZodObject<{
|
|
325
337
|
country: z.ZodObject<{
|
package/dist/onboarding.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../src/onboarding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgBxB,eAAO,MAAM,oBAAoB;;;EAA8B,CAAC;AAChE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAIlE,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgCnC,CAAC;AACL,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAI9E;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,wCAAoC,CAAC;AACvE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;iBAQnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,sFAAsF;AACtF,eAAO,MAAM,8BAA8B;;;;;;;;;;iBAS/B,CAAC;AACb,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAEtF;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;iBAUjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAatE,2EAA2E;AAC3E,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAa5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,mEAAmE;AACnE,eAAO,MAAM,6BAA6B;;;iBAGxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF,eAAO,MAAM,yBAAyB
|
|
1
|
+
{"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../src/onboarding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgBxB,eAAO,MAAM,oBAAoB;;;EAA8B,CAAC;AAChE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAIlE,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgCnC,CAAC;AACL,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAI9E;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,wCAAoC,CAAC;AACvE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;iBAQnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,sFAAsF;AACtF,eAAO,MAAM,8BAA8B;;;;;;;;;;iBAS/B,CAAC;AACb,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAEtF;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;iBAUjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAatE,2EAA2E;AAC3E,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAa5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,mEAAmE;AACnE,eAAO,MAAM,6BAA6B;;;iBAGxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAKpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gymmonk-schema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.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",
|