@battle-racing/br-common-lib 4.4.0 → 4.5.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/domains/player/Player.schema.d.ts +2 -4
- package/dist/domains/player/Player.schema.js +3 -6
- package/dist/domains/racingTeam/RacingTeam.schema.d.ts +77 -3
- package/dist/domains/racingTeam/RacingTeam.schema.js +18 -4
- package/dist/domains/racingTeam/RacingTeam.types.d.ts +4 -1
- package/dist/domains/session/Session.schema.d.ts +3 -0
- package/dist/domains/session/Session.schema.js +4 -1
- package/dist/domains/user/User.schema.d.ts +50 -6
- package/dist/domains/user/User.schema.js +40 -11
- package/dist/domains/user/User.types.d.ts +6 -1
- package/docs/TICKETING_AND_PURCHASE_FLOW.md +110 -0
- package/package.json +1 -1
- package/src/domains/player/Player.schema.ts +2 -6
- package/src/domains/racingTeam/RacingTeam.schema.ts +20 -3
- package/src/domains/racingTeam/RacingTeam.types.ts +7 -0
- package/src/domains/session/Session.schema.ts +4 -0
- package/src/domains/user/User.schema.ts +47 -13
- package/src/domains/user/User.types.ts +15 -1
|
@@ -41,13 +41,11 @@ export declare const playerSchema: z.ZodObject<{
|
|
|
41
41
|
}, z.core.$strip>;
|
|
42
42
|
export declare const createPlayerSchema: z.ZodObject<{
|
|
43
43
|
nickname: z.ZodString;
|
|
44
|
+
ownerUserId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
45
|
+
isGuest: z.ZodDefault<z.ZodBoolean>;
|
|
44
46
|
birthDate: z.ZodCoercedDate<unknown>;
|
|
45
47
|
}, z.core.$strip>;
|
|
46
48
|
export declare const signWaiverSchema: z.ZodObject<{
|
|
47
49
|
signature: z.ZodOptional<z.ZodString>;
|
|
48
50
|
accepted: z.ZodLiteral<true>;
|
|
49
51
|
}, z.core.$strip>;
|
|
50
|
-
export declare const createMinorPlayerSchema: z.ZodObject<{
|
|
51
|
-
nickname: z.ZodString;
|
|
52
|
-
birthDate: z.ZodCoercedDate<unknown>;
|
|
53
|
-
}, z.core.$strip>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.signWaiverSchema = exports.createPlayerSchema = exports.playerSchema = exports.playerStatsSchema = exports.playerWaiverStatusSchema = exports.playerTypeSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const Player_const_1 = require("./Player.const");
|
|
6
6
|
exports.playerTypeSchema = zod_1.z.enum(Player_const_1.PLAYER_TYPE);
|
|
@@ -32,6 +32,8 @@ exports.playerSchema = zod_1.z.object({
|
|
|
32
32
|
exports.createPlayerSchema = exports.playerSchema
|
|
33
33
|
.pick({
|
|
34
34
|
nickname: true,
|
|
35
|
+
ownerUserId: true,
|
|
36
|
+
isGuest: true,
|
|
35
37
|
})
|
|
36
38
|
.extend({
|
|
37
39
|
birthDate: zod_1.z.coerce.date(),
|
|
@@ -40,8 +42,3 @@ exports.signWaiverSchema = zod_1.z.object({
|
|
|
40
42
|
signature: zod_1.z.string().optional(), // Base64 or simply a boolean acceptance. Let's make it flexible.
|
|
41
43
|
accepted: zod_1.z.literal(true), // They must explicitly pass accepted: true
|
|
42
44
|
});
|
|
43
|
-
exports.createMinorPlayerSchema = zod_1.z.object({
|
|
44
|
-
nickname: zod_1.z.string().min(2).max(20),
|
|
45
|
-
birthDate: zod_1.z.coerce.date(),
|
|
46
|
-
// For a minor, the signature is implicitly granted by the tutor calling this endpoint
|
|
47
|
-
});
|
|
@@ -61,7 +61,7 @@ export declare const paginatedRacingTeamSummarySchema: z.ZodObject<{
|
|
|
61
61
|
export declare const joinRequestSchema: z.ZodObject<{
|
|
62
62
|
id: z.ZodString;
|
|
63
63
|
racingTeamId: z.ZodString;
|
|
64
|
-
|
|
64
|
+
playerId: z.ZodString;
|
|
65
65
|
status: z.ZodEnum<{
|
|
66
66
|
readonly PENDING: "PENDING";
|
|
67
67
|
readonly ACCEPTED: "ACCEPTED";
|
|
@@ -71,19 +71,93 @@ export declare const joinRequestSchema: z.ZodObject<{
|
|
|
71
71
|
updatedAt: z.ZodDate;
|
|
72
72
|
}, z.core.$strip>;
|
|
73
73
|
export declare const teamMemberSchema: z.ZodObject<{
|
|
74
|
+
id: z.ZodString;
|
|
74
75
|
racingTeamId: z.ZodString;
|
|
75
|
-
|
|
76
|
+
playerId: z.ZodString;
|
|
76
77
|
role: z.ZodEnum<{
|
|
77
78
|
readonly PILOT: "PILOT";
|
|
78
79
|
readonly TECHNICIAN: "TECHNICIAN";
|
|
79
80
|
}>;
|
|
80
81
|
joinedAt: z.ZodDate;
|
|
81
82
|
}, z.core.$strip>;
|
|
83
|
+
export declare const teamMemberWithPlayerSchema: z.ZodObject<{
|
|
84
|
+
id: z.ZodUUID;
|
|
85
|
+
nickname: z.ZodString;
|
|
86
|
+
avatarUrl: z.ZodOptional<z.ZodURL>;
|
|
87
|
+
birthDate: z.ZodDate;
|
|
88
|
+
type: z.ZodDefault<z.ZodEnum<{
|
|
89
|
+
readonly ADULT: "ADULT";
|
|
90
|
+
readonly MINOR: "MINOR";
|
|
91
|
+
}>>;
|
|
92
|
+
ownerUserId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
93
|
+
isGuest: z.ZodDefault<z.ZodBoolean>;
|
|
94
|
+
waiver: z.ZodObject<{
|
|
95
|
+
isSigned: z.ZodDefault<z.ZodBoolean>;
|
|
96
|
+
waiverId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
97
|
+
templateVersion: z.ZodOptional<z.ZodString>;
|
|
98
|
+
expiresAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
|
|
99
|
+
}, z.core.$strip>;
|
|
100
|
+
stats: z.ZodNullable<z.ZodOptional<z.ZodObject<{
|
|
101
|
+
totalRaces: z.ZodDefault<z.ZodNumber>;
|
|
102
|
+
bestTimeMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
103
|
+
xp: z.ZodDefault<z.ZodNumber>;
|
|
104
|
+
}, z.core.$strip>>>;
|
|
105
|
+
createdAt: z.ZodDate;
|
|
106
|
+
updatedAt: z.ZodDate;
|
|
107
|
+
role: z.ZodEnum<{
|
|
108
|
+
readonly PILOT: "PILOT";
|
|
109
|
+
readonly TECHNICIAN: "TECHNICIAN";
|
|
110
|
+
}>;
|
|
111
|
+
joinedAt: z.ZodDate;
|
|
112
|
+
}, z.core.$strip>;
|
|
113
|
+
export declare const racingTeamWithMembersSchema: z.ZodObject<{
|
|
114
|
+
id: z.ZodString;
|
|
115
|
+
name: z.ZodString;
|
|
116
|
+
imageUrl: z.ZodOptional<z.ZodURL>;
|
|
117
|
+
adminUserId: z.ZodString;
|
|
118
|
+
createdAt: z.ZodDate;
|
|
119
|
+
updatedAt: z.ZodDate;
|
|
120
|
+
members: z.ZodArray<z.ZodObject<{
|
|
121
|
+
id: z.ZodUUID;
|
|
122
|
+
nickname: z.ZodString;
|
|
123
|
+
avatarUrl: z.ZodOptional<z.ZodURL>;
|
|
124
|
+
birthDate: z.ZodDate;
|
|
125
|
+
type: z.ZodDefault<z.ZodEnum<{
|
|
126
|
+
readonly ADULT: "ADULT";
|
|
127
|
+
readonly MINOR: "MINOR";
|
|
128
|
+
}>>;
|
|
129
|
+
ownerUserId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
130
|
+
isGuest: z.ZodDefault<z.ZodBoolean>;
|
|
131
|
+
waiver: z.ZodObject<{
|
|
132
|
+
isSigned: z.ZodDefault<z.ZodBoolean>;
|
|
133
|
+
waiverId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
134
|
+
templateVersion: z.ZodOptional<z.ZodString>;
|
|
135
|
+
expiresAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
|
|
136
|
+
}, z.core.$strip>;
|
|
137
|
+
stats: z.ZodNullable<z.ZodOptional<z.ZodObject<{
|
|
138
|
+
totalRaces: z.ZodDefault<z.ZodNumber>;
|
|
139
|
+
bestTimeMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
140
|
+
xp: z.ZodDefault<z.ZodNumber>;
|
|
141
|
+
}, z.core.$strip>>>;
|
|
142
|
+
createdAt: z.ZodDate;
|
|
143
|
+
updatedAt: z.ZodDate;
|
|
144
|
+
role: z.ZodEnum<{
|
|
145
|
+
readonly PILOT: "PILOT";
|
|
146
|
+
readonly TECHNICIAN: "TECHNICIAN";
|
|
147
|
+
}>;
|
|
148
|
+
joinedAt: z.ZodDate;
|
|
149
|
+
}, z.core.$strip>>;
|
|
150
|
+
}, z.core.$strip>;
|
|
82
151
|
export declare const createJoinRequestSchema: z.ZodObject<{
|
|
83
152
|
racingTeamId: z.ZodString;
|
|
153
|
+
playerId: z.ZodString;
|
|
84
154
|
}, z.core.$strip>;
|
|
85
155
|
export declare const answerJoinRequestSchema: z.ZodObject<{
|
|
86
156
|
racingTeamId: z.ZodString;
|
|
87
|
-
|
|
157
|
+
targetPlayerId: z.ZodString;
|
|
88
158
|
accepted: z.ZodBoolean;
|
|
89
159
|
}, z.core.$strip>;
|
|
160
|
+
export declare const addTeamMemberSchema: z.ZodObject<{
|
|
161
|
+
nickname: z.ZodString;
|
|
162
|
+
birthDate: z.ZodCoercedDate<unknown>;
|
|
163
|
+
}, z.core.$strip>;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.answerJoinRequestSchema = exports.createJoinRequestSchema = exports.teamMemberSchema = exports.joinRequestSchema = exports.paginatedRacingTeamSummarySchema = exports.paginatedRacingTeamSchema = exports.racingTeamSummarySchema = exports.filterRacingTeamSchema = exports.updateRacingTeamSchema = exports.createRacingTeamSchema = exports.racingTeamSchema = void 0;
|
|
3
|
+
exports.addTeamMemberSchema = exports.answerJoinRequestSchema = exports.createJoinRequestSchema = exports.racingTeamWithMembersSchema = exports.teamMemberWithPlayerSchema = exports.teamMemberSchema = exports.joinRequestSchema = exports.paginatedRacingTeamSummarySchema = exports.paginatedRacingTeamSchema = exports.racingTeamSummarySchema = exports.filterRacingTeamSchema = exports.updateRacingTeamSchema = exports.createRacingTeamSchema = exports.racingTeamSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const pagination_1 = require("../pagination");
|
|
6
6
|
const RacingTeam_const_1 = require("./RacingTeam.const");
|
|
7
|
+
const Player_schema_1 = require("../player/Player.schema");
|
|
7
8
|
// --- Team Schemas ---
|
|
8
9
|
exports.racingTeamSchema = zod_1.z.object({
|
|
9
10
|
id: zod_1.z.string(),
|
|
@@ -38,22 +39,35 @@ exports.paginatedRacingTeamSummarySchema = (0, pagination_1.paginatedResponseSch
|
|
|
38
39
|
exports.joinRequestSchema = zod_1.z.object({
|
|
39
40
|
id: zod_1.z.string(),
|
|
40
41
|
racingTeamId: zod_1.z.string(),
|
|
41
|
-
|
|
42
|
+
playerId: zod_1.z.string(),
|
|
42
43
|
status: zod_1.z.enum(RacingTeam_const_1.JOIN_REQUEST_STATUS),
|
|
43
44
|
createdAt: zod_1.z.date(),
|
|
44
45
|
updatedAt: zod_1.z.date(),
|
|
45
46
|
});
|
|
46
47
|
exports.teamMemberSchema = zod_1.z.object({
|
|
48
|
+
id: zod_1.z.string(),
|
|
47
49
|
racingTeamId: zod_1.z.string(),
|
|
48
|
-
|
|
50
|
+
playerId: zod_1.z.string(),
|
|
51
|
+
role: zod_1.z.enum(RacingTeam_const_1.RACING_TEAM_ROLE),
|
|
52
|
+
joinedAt: zod_1.z.date(),
|
|
53
|
+
});
|
|
54
|
+
exports.teamMemberWithPlayerSchema = Player_schema_1.playerSchema.extend({
|
|
49
55
|
role: zod_1.z.enum(RacingTeam_const_1.RACING_TEAM_ROLE),
|
|
50
56
|
joinedAt: zod_1.z.date(),
|
|
51
57
|
});
|
|
58
|
+
exports.racingTeamWithMembersSchema = exports.racingTeamSchema.extend({
|
|
59
|
+
members: zod_1.z.array(exports.teamMemberWithPlayerSchema),
|
|
60
|
+
});
|
|
52
61
|
exports.createJoinRequestSchema = zod_1.z.object({
|
|
53
62
|
racingTeamId: zod_1.z.string(),
|
|
63
|
+
playerId: zod_1.z.string(),
|
|
54
64
|
});
|
|
55
65
|
exports.answerJoinRequestSchema = zod_1.z.object({
|
|
56
66
|
racingTeamId: zod_1.z.string(),
|
|
57
|
-
|
|
67
|
+
targetPlayerId: zod_1.z.string(),
|
|
58
68
|
accepted: zod_1.z.boolean(),
|
|
59
69
|
});
|
|
70
|
+
exports.addTeamMemberSchema = zod_1.z.object({
|
|
71
|
+
nickname: zod_1.z.string().min(2).max(20),
|
|
72
|
+
birthDate: zod_1.z.coerce.date(),
|
|
73
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { answerJoinRequestSchema, createJoinRequestSchema, createRacingTeamSchema, joinRequestSchema, paginatedRacingTeamSchema, paginatedRacingTeamSummarySchema, racingTeamSchema, racingTeamSummarySchema, teamMemberSchema, updateRacingTeamSchema } from './RacingTeam.schema';
|
|
2
|
+
import { answerJoinRequestSchema, createJoinRequestSchema, createRacingTeamSchema, joinRequestSchema, paginatedRacingTeamSchema, paginatedRacingTeamSummarySchema, racingTeamWithMembersSchema, racingTeamSchema, racingTeamSummarySchema, teamMemberWithPlayerSchema, teamMemberSchema, updateRacingTeamSchema, addTeamMemberSchema } from './RacingTeam.schema';
|
|
3
3
|
export type RacingTeam = z.infer<typeof racingTeamSchema>;
|
|
4
4
|
export type CreateRacingTeamInput = z.infer<typeof createRacingTeamSchema>;
|
|
5
5
|
export type UpdateRacingTeamInput = z.infer<typeof updateRacingTeamSchema>;
|
|
@@ -10,3 +10,6 @@ export type JoinRequest = z.infer<typeof joinRequestSchema>;
|
|
|
10
10
|
export type TeamMember = z.infer<typeof teamMemberSchema>;
|
|
11
11
|
export type CreateJoinRequest = z.infer<typeof createJoinRequestSchema>;
|
|
12
12
|
export type AnswerJoinRequest = z.infer<typeof answerJoinRequestSchema>;
|
|
13
|
+
export type TeamMemberWithPlayer = z.infer<typeof teamMemberWithPlayerSchema>;
|
|
14
|
+
export type RacingTeamWithMembers = z.infer<typeof racingTeamWithMembersSchema>;
|
|
15
|
+
export type AddTeamMember = z.infer<typeof addTeamMemberSchema>;
|
|
@@ -166,6 +166,9 @@ export declare const joinSessionSchema: z.ZodObject<{
|
|
|
166
166
|
nickname: z.ZodString;
|
|
167
167
|
birthDate: z.ZodCoercedDate<unknown>;
|
|
168
168
|
}, z.core.$strip>;
|
|
169
|
+
export declare const addExistingPlayersSchema: z.ZodObject<{
|
|
170
|
+
playerIds: z.ZodArray<z.ZodUUID>;
|
|
171
|
+
}, z.core.$strip>;
|
|
169
172
|
export declare const updateSessionStatusSchema: z.ZodObject<{
|
|
170
173
|
status: z.ZodEnum<{
|
|
171
174
|
readonly REGISTRATION: "REGISTRATION";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.bulkCreateGameTurnSchema = exports.assignSessionTicketsInputSchema = exports.createGameTurnInputSchema = exports.updateSessionStatusSchema = exports.joinSessionSchema = exports.createSessionInputSchema = exports.gameSessionWithPlayersSchema = exports.gameSessionSchema = exports.sessionTicketSchema = exports.sessionStatusSchema = void 0;
|
|
3
|
+
exports.bulkCreateGameTurnSchema = exports.assignSessionTicketsInputSchema = exports.createGameTurnInputSchema = exports.updateSessionStatusSchema = exports.addExistingPlayersSchema = exports.joinSessionSchema = exports.createSessionInputSchema = exports.gameSessionWithPlayersSchema = exports.gameSessionSchema = exports.sessionTicketSchema = exports.sessionStatusSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const Session_const_1 = require("./Session.const");
|
|
6
6
|
const Game_schema_1 = require("../game/Game.schema");
|
|
@@ -36,6 +36,9 @@ exports.joinSessionSchema = zod_1.z.object({
|
|
|
36
36
|
nickname: zod_1.z.string().min(2),
|
|
37
37
|
birthDate: zod_1.z.coerce.date(),
|
|
38
38
|
});
|
|
39
|
+
exports.addExistingPlayersSchema = zod_1.z.object({
|
|
40
|
+
playerIds: zod_1.z.array(zod_1.z.uuidv4()).min(1),
|
|
41
|
+
});
|
|
39
42
|
exports.updateSessionStatusSchema = zod_1.z.object({
|
|
40
43
|
status: exports.sessionStatusSchema,
|
|
41
44
|
});
|
|
@@ -4,12 +4,13 @@ export declare const userTypeSchema: z.ZodEnum<{
|
|
|
4
4
|
readonly PLAYER: "PLAYER";
|
|
5
5
|
}>;
|
|
6
6
|
export declare const PHONE_REGEX: RegExp;
|
|
7
|
+
export declare const emailOrPhoneSchema: z.ZodString;
|
|
7
8
|
export declare const userSchema: z.ZodObject<{
|
|
8
9
|
id: z.ZodUUID;
|
|
9
10
|
name: z.ZodString;
|
|
10
11
|
lastName: z.ZodString;
|
|
11
|
-
email: z.ZodOptional<z.ZodEmail
|
|
12
|
-
phoneNumber: z.ZodOptional<z.ZodString
|
|
12
|
+
email: z.ZodNullable<z.ZodOptional<z.ZodEmail>>;
|
|
13
|
+
phoneNumber: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
13
14
|
role: z.ZodEnum<{
|
|
14
15
|
readonly SUPER_ADMIN: "SUPER_ADMIN";
|
|
15
16
|
readonly OPERATOR: "OPERATOR";
|
|
@@ -27,12 +28,39 @@ export declare const userSchema: z.ZodObject<{
|
|
|
27
28
|
createdAt: z.ZodDate;
|
|
28
29
|
lastLoginAt: z.ZodOptional<z.ZodDate>;
|
|
29
30
|
}, z.core.$strip>;
|
|
31
|
+
/**
|
|
32
|
+
* The schema for the user returned on login
|
|
33
|
+
*/
|
|
34
|
+
export declare const authUserSchema: z.ZodObject<{
|
|
35
|
+
id: z.ZodUUID;
|
|
36
|
+
name: z.ZodString;
|
|
37
|
+
lastName: z.ZodString;
|
|
38
|
+
email: z.ZodNullable<z.ZodOptional<z.ZodEmail>>;
|
|
39
|
+
phoneNumber: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
40
|
+
role: z.ZodEnum<{
|
|
41
|
+
readonly SUPER_ADMIN: "SUPER_ADMIN";
|
|
42
|
+
readonly OPERATOR: "OPERATOR";
|
|
43
|
+
readonly PLAYER: "PLAYER";
|
|
44
|
+
readonly CASHIER: "CASHIER";
|
|
45
|
+
}>;
|
|
46
|
+
type: z.ZodEnum<{
|
|
47
|
+
readonly STAFF: "STAFF";
|
|
48
|
+
readonly PLAYER: "PLAYER";
|
|
49
|
+
}>;
|
|
50
|
+
isActive: z.ZodDefault<z.ZodBoolean>;
|
|
51
|
+
isEmailVerified: z.ZodDefault<z.ZodBoolean>;
|
|
52
|
+
isPhoneVerified: z.ZodDefault<z.ZodBoolean>;
|
|
53
|
+
defaultPlayerId: z.ZodOptional<z.ZodUUID>;
|
|
54
|
+
createdAt: z.ZodDate;
|
|
55
|
+
lastLoginAt: z.ZodOptional<z.ZodDate>;
|
|
56
|
+
authToken: z.ZodString;
|
|
57
|
+
}, z.core.$strip>;
|
|
30
58
|
export declare const userWithPasswordSchema: z.ZodIntersection<z.ZodObject<{
|
|
31
59
|
id: z.ZodUUID;
|
|
32
60
|
name: z.ZodString;
|
|
33
61
|
lastName: z.ZodString;
|
|
34
|
-
email: z.ZodOptional<z.ZodEmail
|
|
35
|
-
phoneNumber: z.ZodOptional<z.ZodString
|
|
62
|
+
email: z.ZodNullable<z.ZodOptional<z.ZodEmail>>;
|
|
63
|
+
phoneNumber: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
36
64
|
role: z.ZodEnum<{
|
|
37
65
|
readonly SUPER_ADMIN: "SUPER_ADMIN";
|
|
38
66
|
readonly OPERATOR: "OPERATOR";
|
|
@@ -52,11 +80,27 @@ export declare const userWithPasswordSchema: z.ZodIntersection<z.ZodObject<{
|
|
|
52
80
|
}, z.core.$strip>, z.ZodObject<{
|
|
53
81
|
password: z.ZodString;
|
|
54
82
|
}, z.core.$strip>>;
|
|
83
|
+
export declare const userInitialRegistrationSchema: z.ZodObject<{
|
|
84
|
+
emailOrPhone: z.ZodString;
|
|
85
|
+
password: z.ZodString;
|
|
86
|
+
}, z.core.$strip>;
|
|
87
|
+
export declare const userVerifySchema: z.ZodObject<{
|
|
88
|
+
emailOrPhone: z.ZodString;
|
|
89
|
+
code: z.ZodString;
|
|
90
|
+
}, z.core.$strip>;
|
|
91
|
+
export declare const userCompleteProfileSchema: z.ZodObject<{
|
|
92
|
+
name: z.ZodString;
|
|
93
|
+
lastName: z.ZodOptional<z.ZodString>;
|
|
94
|
+
nickname: z.ZodString;
|
|
95
|
+
birthDate: z.ZodCoercedDate<unknown>;
|
|
96
|
+
waiverSignature: z.ZodBoolean;
|
|
97
|
+
teamName: z.ZodOptional<z.ZodString>;
|
|
98
|
+
}, z.core.$strip>;
|
|
55
99
|
export declare const userRegistrationInputSchema: z.ZodObject<{
|
|
56
100
|
name: z.ZodString;
|
|
101
|
+
email: z.ZodNullable<z.ZodOptional<z.ZodEmail>>;
|
|
57
102
|
lastName: z.ZodString;
|
|
58
|
-
|
|
59
|
-
phoneNumber: z.ZodOptional<z.ZodString>;
|
|
103
|
+
phoneNumber: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
60
104
|
password: z.ZodString;
|
|
61
105
|
nickname: z.ZodString;
|
|
62
106
|
birthDate: z.ZodCoercedDate<unknown>;
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.userLoginInputSchema = exports.userRegistrationInputSchema = exports.userWithPasswordSchema = exports.userSchema = exports.PHONE_REGEX = exports.userTypeSchema = void 0;
|
|
3
|
+
exports.userLoginInputSchema = exports.userRegistrationInputSchema = exports.userCompleteProfileSchema = exports.userVerifySchema = exports.userInitialRegistrationSchema = exports.userWithPasswordSchema = exports.authUserSchema = exports.userSchema = exports.emailOrPhoneSchema = exports.PHONE_REGEX = exports.userTypeSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const role_1 = require("../role");
|
|
6
6
|
const User_const_1 = require("./User.const");
|
|
7
7
|
exports.userTypeSchema = zod_1.z.enum(User_const_1.USER_TYPE);
|
|
8
8
|
exports.PHONE_REGEX = /^([+]?[\\s0-9]+)?(\\d{3}|[(]?[0-9]+[)])?([-]?[\\s]?[0-9])+$/;
|
|
9
9
|
const MIN_PASSWORD_LENGTH = 6;
|
|
10
|
+
exports.emailOrPhoneSchema = zod_1.z
|
|
11
|
+
.string()
|
|
12
|
+
.min(1, 'Email or phone is required')
|
|
13
|
+
.refine((val) => {
|
|
14
|
+
const isEmail = zod_1.z.email().safeParse(val).success;
|
|
15
|
+
const isPhone = exports.PHONE_REGEX.test(val);
|
|
16
|
+
return isEmail || isPhone;
|
|
17
|
+
}, { message: 'Must be a valid email or phone number' });
|
|
10
18
|
const baseUserObj = zod_1.z.object({
|
|
11
19
|
id: zod_1.z.uuidv4(),
|
|
12
20
|
name: zod_1.z.string(),
|
|
13
21
|
lastName: zod_1.z.string(),
|
|
14
|
-
email: zod_1.z.email().toLowerCase().optional(),
|
|
15
|
-
phoneNumber: zod_1.z.string().regex(exports.PHONE_REGEX, 'Invalid phone number format').optional(),
|
|
22
|
+
email: zod_1.z.email().toLowerCase().optional().nullable(),
|
|
23
|
+
phoneNumber: zod_1.z.string().regex(exports.PHONE_REGEX, 'Invalid phone number format').optional().nullable(),
|
|
16
24
|
// --- ACCESS CONTROL ---
|
|
17
25
|
role: role_1.userRoleSchema,
|
|
18
26
|
type: exports.userTypeSchema,
|
|
@@ -28,9 +36,37 @@ exports.userSchema = baseUserObj.refine((data) => data.email || data.phoneNumber
|
|
|
28
36
|
message: 'User must have either an email or a phone number',
|
|
29
37
|
path: ['email', 'phoneNumber'],
|
|
30
38
|
});
|
|
39
|
+
/**
|
|
40
|
+
* The schema for the user returned on login
|
|
41
|
+
*/
|
|
42
|
+
exports.authUserSchema = baseUserObj.extend({
|
|
43
|
+
authToken: zod_1.z.string(),
|
|
44
|
+
});
|
|
31
45
|
exports.userWithPasswordSchema = exports.userSchema.and(zod_1.z.object({
|
|
32
46
|
password: zod_1.z.string().min(MIN_PASSWORD_LENGTH),
|
|
33
47
|
}));
|
|
48
|
+
exports.userInitialRegistrationSchema = zod_1.z.object({
|
|
49
|
+
emailOrPhone: exports.emailOrPhoneSchema,
|
|
50
|
+
password: zod_1.z.string().min(MIN_PASSWORD_LENGTH, `Password must be at least ${MIN_PASSWORD_LENGTH} characters`),
|
|
51
|
+
});
|
|
52
|
+
exports.userVerifySchema = zod_1.z.object({
|
|
53
|
+
emailOrPhone: exports.emailOrPhoneSchema,
|
|
54
|
+
code: zod_1.z.string().min(6, 'Code must be at least 6 characters').max(10, 'Code is too long'),
|
|
55
|
+
});
|
|
56
|
+
exports.userCompleteProfileSchema = baseUserObj
|
|
57
|
+
.pick({
|
|
58
|
+
name: true,
|
|
59
|
+
})
|
|
60
|
+
.extend({
|
|
61
|
+
lastName: zod_1.z.string().optional(),
|
|
62
|
+
nickname: zod_1.z.string().min(2),
|
|
63
|
+
birthDate: zod_1.z.coerce.date(),
|
|
64
|
+
waiverSignature: zod_1.z.boolean().refine((val) => val === true, {
|
|
65
|
+
message: 'You must accept the waiver signature',
|
|
66
|
+
}),
|
|
67
|
+
teamName: zod_1.z.string().optional(),
|
|
68
|
+
});
|
|
69
|
+
// Deprecated in favor of multi-step registration
|
|
34
70
|
exports.userRegistrationInputSchema = baseUserObj
|
|
35
71
|
.pick({
|
|
36
72
|
name: true,
|
|
@@ -48,13 +84,6 @@ exports.userRegistrationInputSchema = baseUserObj
|
|
|
48
84
|
path: ['email', 'phoneNumber'],
|
|
49
85
|
});
|
|
50
86
|
exports.userLoginInputSchema = zod_1.z.object({
|
|
51
|
-
emailOrPhone:
|
|
52
|
-
.string()
|
|
53
|
-
.min(1, 'Email or phone is required')
|
|
54
|
-
.refine((val) => {
|
|
55
|
-
const isEmail = zod_1.z.email().safeParse(val).success;
|
|
56
|
-
const isPhone = exports.PHONE_REGEX.test(val);
|
|
57
|
-
return isEmail || isPhone;
|
|
58
|
-
}, { message: 'Must be a valid email or phone number' }),
|
|
87
|
+
emailOrPhone: exports.emailOrPhoneSchema,
|
|
59
88
|
password: zod_1.z.string().min(MIN_PASSWORD_LENGTH, `Password must be at least ${MIN_PASSWORD_LENGTH} characters`),
|
|
60
89
|
});
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { userSchema, userTypeSchema, userWithPasswordSchema } from './User.schema';
|
|
2
|
+
import { userSchema, userTypeSchema, userWithPasswordSchema, userInitialRegistrationSchema, userVerifySchema, userCompleteProfileSchema, userLoginInputSchema, authUserSchema } from './User.schema';
|
|
3
3
|
export type UserType = z.infer<typeof userTypeSchema>;
|
|
4
4
|
export type User = z.infer<typeof userSchema>;
|
|
5
5
|
export type UserWithPassword = z.infer<typeof userWithPasswordSchema>;
|
|
6
|
+
export type AuthUser = z.infer<typeof authUserSchema>;
|
|
7
|
+
export type UserInitialRegistration = z.infer<typeof userInitialRegistrationSchema>;
|
|
8
|
+
export type UserVerify = z.infer<typeof userVerifySchema>;
|
|
9
|
+
export type UserCompleteProfile = z.infer<typeof userCompleteProfileSchema>;
|
|
10
|
+
export type UserLogin = z.infer<typeof userLoginInputSchema>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Flujo de Compra de Tickets y Separación User/Player
|
|
2
|
+
|
|
3
|
+
Este documento explica de forma detallada cómo está estructurado el flujo de compra de tickets en **Battle Racing**, cómo se separa la entidad que paga (User) de la entidad que corre (Player), qué hemos implementado hasta ahora y qué nos hace falta por implementar.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 👥 1. Separación: Users vs. Players
|
|
8
|
+
|
|
9
|
+
Para asegurar "cero fricción" en el entorno físico de la pista, hemos separado estrictamente el concepto de la cuenta (quien paga) del corredor (quien se sube al Kart).
|
|
10
|
+
|
|
11
|
+
### `User` (La Cuenta / El Pagador)
|
|
12
|
+
|
|
13
|
+
- **Propósito:** La persona física que se registra, gestiona un método de pago y posee el historial transaccional de compras.
|
|
14
|
+
- **Autenticación:** Usa `email` o `phoneNumber` (sin `username`), contraseña y roles (`SUPER_ADMIN`, `CASHIER`, `PLAYER`).
|
|
15
|
+
- **Relaciones:**
|
|
16
|
+
- Tiene un `defaultPlayerId` que apunta a su propio perfil de corredor.
|
|
17
|
+
- Puede ser tutor y ser "dueño" (`ownerUserId`) de múltiples `Players` menores de edad (`MINOR`).
|
|
18
|
+
|
|
19
|
+
### `Player` (El Corredor / Perfil Gamificado)
|
|
20
|
+
|
|
21
|
+
- **Propósito:** Es la entidad gamificada que se asigna físicamente a un Kart y cuyo nombre sale en las pantallas.
|
|
22
|
+
- **Identificador (`nickname`):** No son únicos en la base de datos. Varios corredores pueden llamarse "Maverick" al mismo tiempo para no generar errores al registrarse rápidamente en el POS.
|
|
23
|
+
- **Waivers:** Contiene el estado de la firma del documento de exención de responsabilidad (`waiver`).
|
|
24
|
+
- **Relaciones:** Si es un invitado anónimo (`isGuest`), no está atado a ningún `User`. Si es un menor, su firma (`waiver`) es implícitamente otorgada por el `User` tutor.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 🎟️ 2. Flujo de Compras (Purchases y Tickets)
|
|
29
|
+
|
|
30
|
+
La adquisición de carreras sigue un ciclo de vida de tres grandes capas:
|
|
31
|
+
|
|
32
|
+
### Capa 1: La Compra (`Purchase`)
|
|
33
|
+
|
|
34
|
+
1. **Creación:** Se crea un `Purchase` agrupando varios ítems de compra (ej. 3 tickets de tipo `RACE`).
|
|
35
|
+
- Se crea en el endpoint: `POST /ticketing/purchases`
|
|
36
|
+
- Si el cliente está logueado en la Web App, se asocia su `buyerUserId`.
|
|
37
|
+
- Si la compra es 100% anónima en la sucursal (POS), `buyerUserId` es `null` y se usan `guestName` y `guestContact`.
|
|
38
|
+
2. **Pago:** El pago se confirma asíncronamente vía Webhooks (`POST /ticketing/webhook/payment`) o manualmente en caja registradora (`POST /ticketing/purchases/:id/manual-payment`).
|
|
39
|
+
3. **Generación de GameTickets:** Una vez confirmada y pagada la compra, esta se _desfragmenta_ y la base de datos genera los `GameTicket` individuales correspondientes (con estado `CREATED`).
|
|
40
|
+
|
|
41
|
+
### Capa 2: La Sesión (`Game Session`)
|
|
42
|
+
|
|
43
|
+
Dado que una persona puede comprar 10 tickets, pero a la pista se van a subir sus 5 amigos 2 veces, se usa una **Session**.
|
|
44
|
+
|
|
45
|
+
1. **Creación:** Se agrupan tickets recién comprados bajo un código alfanumérico corto (ej. `A1B2C3`).
|
|
46
|
+
2. **Unión:** Los amigos escanean un QR o dictan el código para unirse a la sesión (`POST /sessions/:code/join`). Aquí sus `Players` ingresan a la sesión.
|
|
47
|
+
3. **Asignación:** Se asigna cada `GameTicket` disponible en la sesión a un `playerId` específico (`POST /sessions/:code/tickets/assign`).
|
|
48
|
+
|
|
49
|
+
### Capa 3: El Turno Físico en Pista (`Game Turn`)
|
|
50
|
+
|
|
51
|
+
1. **Cola de Carrera:** Cuando el grupo de la sesión está listo (o el operador del POS organiza la carrera), se crea un `GameTurn`.
|
|
52
|
+
- Endpoint: `POST /game-turns` o `POST /game-turns/bulk`.
|
|
53
|
+
2. **Vinculación Final:** El `GameTurn` toma los `GameTickets` asignados y los `playerIds` correspondientes, bloqueándolos para esta carrera específica.
|
|
54
|
+
3. **Ejecución y Hardware:** El hardware vincula a los jugadores con sus respectivos Karts y avanza las tareas de la carrera (preparación, en sus marcas, carrera, meta).
|
|
55
|
+
- El progreso se guarda mediante: `POST /game-turns/:id/tasks/:taskId/complete`
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## ✅ 3. ¿Qué tenemos implementado hasta ahora?
|
|
60
|
+
|
|
61
|
+
### En `br-common-lib` (Schemas)
|
|
62
|
+
|
|
63
|
+
- Zod schemas altamente detallados para `User`, `Player`, `Session`, `Ticketing` (Purchase y Tickets) y `Payment`.
|
|
64
|
+
- Control absoluto y fuente de verdad centralizada para la validación de estos modelos.
|
|
65
|
+
|
|
66
|
+
### En `game-manager-backend` (REST API)
|
|
67
|
+
|
|
68
|
+
- **Módulo `ticketing`:**
|
|
69
|
+
- Creación de compras.
|
|
70
|
+
- Procesamiento manual de pagos (para POS/Cashiers).
|
|
71
|
+
- Cancelación de compras.
|
|
72
|
+
- Recepción de webhooks de pasarelas de pago.
|
|
73
|
+
- Listado mis compras y resumen de mis tickets (`my-purchases`, `my-tickets`).
|
|
74
|
+
- **Módulo `session`:**
|
|
75
|
+
- Obtener sesión por código corto.
|
|
76
|
+
- Unirse (y unirse en bulk) a una sesión.
|
|
77
|
+
- Asignar tickets a jugadores dentro de la sesión.
|
|
78
|
+
- **Módulo `game-turn`:**
|
|
79
|
+
- Listado general de turnos (`GET /game-turns`).
|
|
80
|
+
- Creación de turnos y turnos en bloque.
|
|
81
|
+
- Avance de etapas (tasks) dentro del turno (`completeTask`).
|
|
82
|
+
- Capacidad para avanzar (`/advance`) o posponer (`/postpone`) el turno en la fila.
|
|
83
|
+
- **Módulo `player` & `user`:**
|
|
84
|
+
- Estructuras en DB/Repositorios y controladores listos para manipular estos perfiles.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 🚧 4. ¿Qué nos hace falta implementar? (Faltantes / TODOs)
|
|
89
|
+
|
|
90
|
+
1. **Autenticación Severa (Guards):**
|
|
91
|
+
- Varios enpoints importantes como la creación de compras y modificación de sesiones todavía usan `request.user?.id` asumiendo que el usuario está inyectado, pero faltan **Guards** (e.g., JWT Guards) sólidos y globales para denegar acceso a usuarios no autorizados de forma segura o están marcados como `@Public()`.
|
|
92
|
+
|
|
93
|
+
2. **Validación de Identidad de Email/Teléfono (`User`):**
|
|
94
|
+
- Aunque la base de datos soporta `isEmailVerified` e `isPhoneVerified`, el mecanismo de envío de OTP / SMS / Enlace Mágico para confirmar la propiedad aún no está programado.
|
|
95
|
+
|
|
96
|
+
3. **Verificación de Seguridad de Asignación de Tickets:**
|
|
97
|
+
- Comprobar que un User no pueda asignar tickets de un Purchase que NO le pertenece (cuando la abstracción de sesión permita saltos de seguridad).
|
|
98
|
+
- Actualmente falta un "Ownership Check" robusto en varios GET de compras y lectura de turnos.
|
|
99
|
+
|
|
100
|
+
4. **Actualización Automática de Estadísticas del Player:**
|
|
101
|
+
- Actualmente la base de datos de Players tiene sub-schemas listos para Stats (`totalRaces`, `bestTimeMs`, `xp`). Sin embargo, una vez que el `GameTurn` se completa en la pista, falta implementar el webhook o servicio que **calcule los resultados finales y actualice estos stats** dentro de cada `Player` que participó.
|
|
102
|
+
|
|
103
|
+
5. **Flujos de Error de Pagos y Reembolsos Avanzados:**
|
|
104
|
+
- Manejo exhaustivo de reembolsos desde Stripe hacia el sistema para revocar `GameTickets` si una persona pide chargeback manual.
|
|
105
|
+
|
|
106
|
+
6. **Integración Completa con POS (Front-end UI):**
|
|
107
|
+
- Falta validar en el frontend de POS que estos flujos asíncronos (como el crear compra en cash, asignar players de forma anónima) no se bloqueen por dependencias de cuentas "Guest". Asegurarse de que el frontend POS pueda transitar estas 3 capas sin recargar.
|
|
108
|
+
|
|
109
|
+
7. **Asociación Dinámica de Hardware (Karts ↔ Players) en el GameTurn:**
|
|
110
|
+
- Hay que madurar la lógica donde un `playerId` que entra a un `GameTurn` es asignado al IP / Socket / ID Hardware específico del Kart en el que se acaba de sentar.
|
package/package.json
CHANGED
|
@@ -37,6 +37,8 @@ export const playerSchema = z.object({
|
|
|
37
37
|
export const createPlayerSchema = playerSchema
|
|
38
38
|
.pick({
|
|
39
39
|
nickname: true,
|
|
40
|
+
ownerUserId: true,
|
|
41
|
+
isGuest: true,
|
|
40
42
|
})
|
|
41
43
|
.extend({
|
|
42
44
|
birthDate: z.coerce.date(),
|
|
@@ -46,9 +48,3 @@ export const signWaiverSchema = z.object({
|
|
|
46
48
|
signature: z.string().optional(), // Base64 or simply a boolean acceptance. Let's make it flexible.
|
|
47
49
|
accepted: z.literal(true), // They must explicitly pass accepted: true
|
|
48
50
|
});
|
|
49
|
-
|
|
50
|
-
export const createMinorPlayerSchema = z.object({
|
|
51
|
-
nickname: z.string().min(2).max(20),
|
|
52
|
-
birthDate: z.coerce.date(),
|
|
53
|
-
// For a minor, the signature is implicitly granted by the tutor calling this endpoint
|
|
54
|
-
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { paginatedResponseSchema } from '../pagination';
|
|
3
3
|
import { JOIN_REQUEST_STATUS, RACING_TEAM_ROLE } from './RacingTeam.const';
|
|
4
|
+
import { playerSchema } from '../player/Player.schema';
|
|
4
5
|
|
|
5
6
|
// --- Team Schemas ---
|
|
6
7
|
|
|
@@ -44,25 +45,41 @@ export const paginatedRacingTeamSummarySchema = paginatedResponseSchema(racingTe
|
|
|
44
45
|
export const joinRequestSchema = z.object({
|
|
45
46
|
id: z.string(),
|
|
46
47
|
racingTeamId: z.string(),
|
|
47
|
-
|
|
48
|
+
playerId: z.string(),
|
|
48
49
|
status: z.enum(JOIN_REQUEST_STATUS),
|
|
49
50
|
createdAt: z.date(),
|
|
50
51
|
updatedAt: z.date(),
|
|
51
52
|
});
|
|
52
53
|
|
|
53
54
|
export const teamMemberSchema = z.object({
|
|
55
|
+
id: z.string(),
|
|
54
56
|
racingTeamId: z.string(),
|
|
55
|
-
|
|
57
|
+
playerId: z.string(),
|
|
58
|
+
role: z.enum(RACING_TEAM_ROLE),
|
|
59
|
+
joinedAt: z.date(),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export const teamMemberWithPlayerSchema = playerSchema.extend({
|
|
56
63
|
role: z.enum(RACING_TEAM_ROLE),
|
|
57
64
|
joinedAt: z.date(),
|
|
58
65
|
});
|
|
59
66
|
|
|
67
|
+
export const racingTeamWithMembersSchema = racingTeamSchema.extend({
|
|
68
|
+
members: z.array(teamMemberWithPlayerSchema),
|
|
69
|
+
});
|
|
70
|
+
|
|
60
71
|
export const createJoinRequestSchema = z.object({
|
|
61
72
|
racingTeamId: z.string(),
|
|
73
|
+
playerId: z.string(),
|
|
62
74
|
});
|
|
63
75
|
|
|
64
76
|
export const answerJoinRequestSchema = z.object({
|
|
65
77
|
racingTeamId: z.string(),
|
|
66
|
-
|
|
78
|
+
targetPlayerId: z.string(),
|
|
67
79
|
accepted: z.boolean(),
|
|
68
80
|
});
|
|
81
|
+
|
|
82
|
+
export const addTeamMemberSchema = z.object({
|
|
83
|
+
nickname: z.string().min(2).max(20),
|
|
84
|
+
birthDate: z.coerce.date(),
|
|
85
|
+
});
|
|
@@ -6,10 +6,13 @@ import {
|
|
|
6
6
|
joinRequestSchema,
|
|
7
7
|
paginatedRacingTeamSchema,
|
|
8
8
|
paginatedRacingTeamSummarySchema,
|
|
9
|
+
racingTeamWithMembersSchema,
|
|
9
10
|
racingTeamSchema,
|
|
10
11
|
racingTeamSummarySchema,
|
|
12
|
+
teamMemberWithPlayerSchema,
|
|
11
13
|
teamMemberSchema,
|
|
12
14
|
updateRacingTeamSchema,
|
|
15
|
+
addTeamMemberSchema,
|
|
13
16
|
} from './RacingTeam.schema';
|
|
14
17
|
|
|
15
18
|
export type RacingTeam = z.infer<typeof racingTeamSchema>;
|
|
@@ -23,3 +26,7 @@ export type JoinRequest = z.infer<typeof joinRequestSchema>;
|
|
|
23
26
|
export type TeamMember = z.infer<typeof teamMemberSchema>;
|
|
24
27
|
export type CreateJoinRequest = z.infer<typeof createJoinRequestSchema>;
|
|
25
28
|
export type AnswerJoinRequest = z.infer<typeof answerJoinRequestSchema>;
|
|
29
|
+
|
|
30
|
+
export type TeamMemberWithPlayer = z.infer<typeof teamMemberWithPlayerSchema>;
|
|
31
|
+
export type RacingTeamWithMembers = z.infer<typeof racingTeamWithMembersSchema>;
|
|
32
|
+
export type AddTeamMember = z.infer<typeof addTeamMemberSchema>;
|
|
@@ -43,6 +43,10 @@ export const joinSessionSchema = z.object({
|
|
|
43
43
|
birthDate: z.coerce.date(),
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
+
export const addExistingPlayersSchema = z.object({
|
|
47
|
+
playerIds: z.array(z.uuidv4()).min(1),
|
|
48
|
+
});
|
|
49
|
+
|
|
46
50
|
export const updateSessionStatusSchema = z.object({
|
|
47
51
|
status: sessionStatusSchema,
|
|
48
52
|
});
|
|
@@ -7,15 +7,27 @@ export const userTypeSchema = z.enum(USER_TYPE);
|
|
|
7
7
|
export const PHONE_REGEX = /^([+]?[\\s0-9]+)?(\\d{3}|[(]?[0-9]+[)])?([-]?[\\s]?[0-9])+$/;
|
|
8
8
|
const MIN_PASSWORD_LENGTH = 6;
|
|
9
9
|
|
|
10
|
+
export const emailOrPhoneSchema = z
|
|
11
|
+
.string()
|
|
12
|
+
.min(1, 'Email or phone is required')
|
|
13
|
+
.refine(
|
|
14
|
+
(val) => {
|
|
15
|
+
const isEmail = z.email().safeParse(val).success;
|
|
16
|
+
const isPhone = PHONE_REGEX.test(val);
|
|
17
|
+
return isEmail || isPhone;
|
|
18
|
+
},
|
|
19
|
+
{ message: 'Must be a valid email or phone number' },
|
|
20
|
+
);
|
|
21
|
+
|
|
10
22
|
const baseUserObj = z.object({
|
|
11
23
|
id: z.uuidv4(),
|
|
12
24
|
|
|
13
25
|
name: z.string(),
|
|
14
26
|
lastName: z.string(),
|
|
15
27
|
|
|
16
|
-
email: z.email().toLowerCase().optional(),
|
|
28
|
+
email: z.email().toLowerCase().optional().nullable(),
|
|
17
29
|
|
|
18
|
-
phoneNumber: z.string().regex(PHONE_REGEX, 'Invalid phone number format').optional(),
|
|
30
|
+
phoneNumber: z.string().regex(PHONE_REGEX, 'Invalid phone number format').optional().nullable(),
|
|
19
31
|
|
|
20
32
|
// --- ACCESS CONTROL ---
|
|
21
33
|
role: userRoleSchema,
|
|
@@ -37,12 +49,44 @@ export const userSchema = baseUserObj.refine((data) => data.email || data.phoneN
|
|
|
37
49
|
path: ['email', 'phoneNumber'],
|
|
38
50
|
});
|
|
39
51
|
|
|
52
|
+
/**
|
|
53
|
+
* The schema for the user returned on login
|
|
54
|
+
*/
|
|
55
|
+
export const authUserSchema = baseUserObj.extend({
|
|
56
|
+
authToken: z.string(),
|
|
57
|
+
});
|
|
58
|
+
|
|
40
59
|
export const userWithPasswordSchema = userSchema.and(
|
|
41
60
|
z.object({
|
|
42
61
|
password: z.string().min(MIN_PASSWORD_LENGTH),
|
|
43
62
|
}),
|
|
44
63
|
);
|
|
45
64
|
|
|
65
|
+
export const userInitialRegistrationSchema = z.object({
|
|
66
|
+
emailOrPhone: emailOrPhoneSchema,
|
|
67
|
+
password: z.string().min(MIN_PASSWORD_LENGTH, `Password must be at least ${MIN_PASSWORD_LENGTH} characters`),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export const userVerifySchema = z.object({
|
|
71
|
+
emailOrPhone: emailOrPhoneSchema,
|
|
72
|
+
code: z.string().min(6, 'Code must be at least 6 characters').max(10, 'Code is too long'),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export const userCompleteProfileSchema = baseUserObj
|
|
76
|
+
.pick({
|
|
77
|
+
name: true,
|
|
78
|
+
})
|
|
79
|
+
.extend({
|
|
80
|
+
lastName: z.string().optional(),
|
|
81
|
+
nickname: z.string().min(2),
|
|
82
|
+
birthDate: z.coerce.date(),
|
|
83
|
+
waiverSignature: z.boolean().refine((val) => val === true, {
|
|
84
|
+
message: 'You must accept the waiver signature',
|
|
85
|
+
}),
|
|
86
|
+
teamName: z.string().optional(),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Deprecated in favor of multi-step registration
|
|
46
90
|
export const userRegistrationInputSchema = baseUserObj
|
|
47
91
|
.pick({
|
|
48
92
|
name: true,
|
|
@@ -61,16 +105,6 @@ export const userRegistrationInputSchema = baseUserObj
|
|
|
61
105
|
});
|
|
62
106
|
|
|
63
107
|
export const userLoginInputSchema = z.object({
|
|
64
|
-
emailOrPhone:
|
|
65
|
-
.string()
|
|
66
|
-
.min(1, 'Email or phone is required')
|
|
67
|
-
.refine(
|
|
68
|
-
(val) => {
|
|
69
|
-
const isEmail = z.email().safeParse(val).success;
|
|
70
|
-
const isPhone = PHONE_REGEX.test(val);
|
|
71
|
-
return isEmail || isPhone;
|
|
72
|
-
},
|
|
73
|
-
{ message: 'Must be a valid email or phone number' },
|
|
74
|
-
),
|
|
108
|
+
emailOrPhone: emailOrPhoneSchema,
|
|
75
109
|
password: z.string().min(MIN_PASSWORD_LENGTH, `Password must be at least ${MIN_PASSWORD_LENGTH} characters`),
|
|
76
110
|
});
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
userSchema,
|
|
4
|
+
userTypeSchema,
|
|
5
|
+
userWithPasswordSchema,
|
|
6
|
+
userInitialRegistrationSchema,
|
|
7
|
+
userVerifySchema,
|
|
8
|
+
userCompleteProfileSchema,
|
|
9
|
+
userLoginInputSchema,
|
|
10
|
+
authUserSchema,
|
|
11
|
+
} from './User.schema';
|
|
3
12
|
|
|
4
13
|
export type UserType = z.infer<typeof userTypeSchema>;
|
|
5
14
|
export type User = z.infer<typeof userSchema>;
|
|
6
15
|
export type UserWithPassword = z.infer<typeof userWithPasswordSchema>;
|
|
16
|
+
export type AuthUser = z.infer<typeof authUserSchema>;
|
|
17
|
+
export type UserInitialRegistration = z.infer<typeof userInitialRegistrationSchema>;
|
|
18
|
+
export type UserVerify = z.infer<typeof userVerifySchema>;
|
|
19
|
+
export type UserCompleteProfile = z.infer<typeof userCompleteProfileSchema>;
|
|
20
|
+
export type UserLogin = z.infer<typeof userLoginInputSchema>;
|