@tbs-fitness/contract 0.8.0 → 0.10.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/README.md +18 -0
- package/dist/cjs/enums/index.d.ts +23 -0
- package/dist/cjs/enums/index.js +31 -1
- package/dist/cjs/goals/goal.dto.d.ts +137 -0
- package/dist/cjs/goals/goal.dto.js +80 -0
- package/dist/cjs/goals/index.d.ts +1 -0
- package/dist/cjs/goals/index.js +17 -0
- package/dist/cjs/supplements/index.d.ts +1 -0
- package/dist/cjs/supplements/index.js +17 -0
- package/dist/cjs/supplements/supplement.dto.d.ts +386 -0
- package/dist/cjs/supplements/supplement.dto.js +109 -0
- package/dist/esm/enums/index.d.ts +23 -0
- package/dist/esm/enums/index.js +30 -0
- package/dist/esm/goals/goal.dto.d.ts +137 -0
- package/dist/esm/goals/goal.dto.js +76 -0
- package/dist/esm/goals/index.d.ts +1 -0
- package/dist/esm/goals/index.js +1 -0
- package/dist/esm/supplements/index.d.ts +1 -0
- package/dist/esm/supplements/index.js +1 -0
- package/dist/esm/supplements/supplement.dto.d.ts +386 -0
- package/dist/esm/supplements/supplement.dto.js +90 -0
- package/package.json +21 -1
package/README.md
CHANGED
|
@@ -43,6 +43,24 @@ const result = createFlagSchema.safeParse(input); // The same rules as the API.
|
|
|
43
43
|
|
|
44
44
|
Requests have two types. Clients use `*Input`, where fields with a default are optional. The backend uses `*Body`, which is the result after validation. List queries come from the URL as strings, so they have no `*Input`: use `Partial<PaginationQuery>` or `Partial<AuditLogQuery>`.
|
|
45
45
|
|
|
46
|
+
## Version 0.10.0
|
|
47
|
+
|
|
48
|
+
The supplement library. Additive; consumers install `^0.10.0` explicitly.
|
|
49
|
+
|
|
50
|
+
- New `/supplements`. It keeps the food library's spreadsheet format, nutrition rules and labels, so most schemas are the food library's own under supplement names:
|
|
51
|
+
- Library: `createSupplementSchema`, `updateSupplementSchema` (with `version`), `supplementListQuerySchema`, `supplementResponseSchema` (no suggestion link; `source` is `ADMIN`, `COACH` or `IMPORT`), `similarSupplementResponseSchema`, `supplementStatsResponseSchema`.
|
|
52
|
+
- Labels: the metadata type and value schemas, re-exported from `/food`.
|
|
53
|
+
- Import: `supplementImportRowSchema`, `commitSupplementImportSchema`, `supplementImportBatchResponseSchema`, `supplementImportRowResponseSchema` (`matchedSupplementId`, `resultSupplementId`), `SUPPLEMENT_IMPORT_LIMITS`.
|
|
54
|
+
- Client: `clientSupplementSearchQuerySchema`, `clientSupplementResponseSchema`.
|
|
55
|
+
- `/enums`: `SUPPLEMENT_SOURCES`, and the permissions `supplements:read`, `supplements:create`, `supplements:update`, `supplements:delete`, `supplements:import`, `supplement_metadata:manage` and `supplements:search:self`.
|
|
56
|
+
|
|
57
|
+
## Version 0.9.0
|
|
58
|
+
|
|
59
|
+
The goal library. Additive; consumers install `^0.9.0` explicitly.
|
|
60
|
+
|
|
61
|
+
- `/goals`: `createGoalSchema`, `updateGoalSchema` (with `version`), `goalListQuerySchema`, `goalResponseSchema`, `goalStatsResponseSchema`, and the client's `clientGoalResponseSchema` and `clientGoalListResponseSchema`. `normalizeGoalName` is what duplicate name checks compare.
|
|
62
|
+
- `/enums`: `GOAL_ICONS` (icon names each app maps to its own icon), `GOAL_STATUSES` (`active`, `inactive`), `GOAL_SORT_FIELDS`, and the permissions `goals:read`, `goals:create`, `goals:update`, `goals:delete` and `goals:read:self`.
|
|
63
|
+
|
|
46
64
|
## Version 0.8.0 (breaking)
|
|
47
65
|
|
|
48
66
|
- `newPasswordSchema` now matches the new-password screens: at least 8 characters (was 12) and at least one number. `MIN_PASSWORD_LENGTH` is `8`, and the new `PASSWORD_DIGIT_PATTERN` lets apps show the number rule as it is typed. Sign-up, password change and reset all use it; login does not, so existing passwords keep working.
|
|
@@ -32,6 +32,18 @@ export declare const PERMISSIONS: {
|
|
|
32
32
|
readonly foodsSearchSelf: "foods:search:self";
|
|
33
33
|
readonly foodSuggestionsSubmitSelf: "food_suggestions:submit:self";
|
|
34
34
|
readonly foodSuggestionsReadSelf: "food_suggestions:read:self";
|
|
35
|
+
readonly goalsRead: "goals:read";
|
|
36
|
+
readonly goalsCreate: "goals:create";
|
|
37
|
+
readonly goalsUpdate: "goals:update";
|
|
38
|
+
readonly goalsDelete: "goals:delete";
|
|
39
|
+
readonly goalsReadSelf: "goals:read:self";
|
|
40
|
+
readonly supplementsRead: "supplements:read";
|
|
41
|
+
readonly supplementsCreate: "supplements:create";
|
|
42
|
+
readonly supplementsUpdate: "supplements:update";
|
|
43
|
+
readonly supplementsDelete: "supplements:delete";
|
|
44
|
+
readonly supplementsImport: "supplements:import";
|
|
45
|
+
readonly supplementMetadataManage: "supplement_metadata:manage";
|
|
46
|
+
readonly supplementsSearchSelf: "supplements:search:self";
|
|
35
47
|
};
|
|
36
48
|
export type Permission = (typeof PERMISSIONS)[keyof typeof PERMISSIONS];
|
|
37
49
|
/**
|
|
@@ -124,3 +136,14 @@ export declare const FOOD_SORT_FIELDS: readonly ["name", "updatedAt", "calories"
|
|
|
124
136
|
export type FoodSortField = (typeof FOOD_SORT_FIELDS)[number];
|
|
125
137
|
export declare const SORT_ORDERS: readonly ["asc", "desc"];
|
|
126
138
|
export type SortOrder = (typeof SORT_ORDERS)[number];
|
|
139
|
+
/** The icons a goal can show, by name. Each app maps a name to its own icon. */
|
|
140
|
+
export declare const GOAL_ICONS: readonly ["target", "trending-down", "trending-up", "dumbbell", "refresh", "activity", "trophy", "heart", "flame", "scale"];
|
|
141
|
+
export type GoalIcon = (typeof GOAL_ICONS)[number];
|
|
142
|
+
/** Whether clients can pick a goal: live goals are either shown or hidden. */
|
|
143
|
+
export declare const GOAL_STATUSES: readonly ["active", "inactive"];
|
|
144
|
+
export type GoalStatus = (typeof GOAL_STATUSES)[number];
|
|
145
|
+
export declare const GOAL_SORT_FIELDS: readonly ["sortOrder", "name", "updatedAt"];
|
|
146
|
+
export type GoalSortField = (typeof GOAL_SORT_FIELDS)[number];
|
|
147
|
+
/** Where a supplement came from. Clients cannot suggest supplements, unlike foods. */
|
|
148
|
+
export declare const SUPPLEMENT_SOURCES: readonly ["ADMIN", "COACH", "IMPORT"];
|
|
149
|
+
export type SupplementSource = (typeof SUPPLEMENT_SOURCES)[number];
|
package/dist/cjs/enums/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// These values are stored in the database and sent over the API. Renaming or
|
|
3
3
|
// removing one is a breaking change, so it needs a major version.
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.SORT_ORDERS = exports.FOOD_SORT_FIELDS = exports.FOOD_METADATA_SELECTIONS = exports.FOOD_IMPORT_ISSUE_SEVERITIES = exports.FOOD_IMPORT_FILE_TYPES = exports.FOOD_IMPORT_ROW_STATUSES = exports.FOOD_IMPORT_STATUSES = exports.FOOD_IMPORT_MODES = exports.FOOD_SUGGESTION_RESOLUTIONS = exports.FOOD_SUGGESTION_STATUSES = exports.NUTRITION_BASES = exports.PORTION_UNITS = exports.SERVING_UNITS = exports.FOOD_SOURCES = exports.TEMPLATE_STATUSES = exports.DELIVERY_STATUSES = exports.MESSAGE_TYPES = exports.COMMUNICATION_CHANNELS = exports.FEATURE_FLAG_RULE_TYPES = exports.AUDIT_OUTCOMES = exports.CSRF_HEADER = exports.CSRF_COOKIE_NAMES = exports.TOKEN_TRANSPORTS = exports.TIMEZONE_SOURCES = exports.ADMIN_SETTABLE_ACCOUNT_STATUSES = exports.ACCOUNT_STATUSES = exports.PERMISSIONS = exports.ROLES = exports.APP_ENVIRONMENTS = void 0;
|
|
5
|
+
exports.SUPPLEMENT_SOURCES = exports.GOAL_SORT_FIELDS = exports.GOAL_STATUSES = exports.GOAL_ICONS = exports.SORT_ORDERS = exports.FOOD_SORT_FIELDS = exports.FOOD_METADATA_SELECTIONS = exports.FOOD_IMPORT_ISSUE_SEVERITIES = exports.FOOD_IMPORT_FILE_TYPES = exports.FOOD_IMPORT_ROW_STATUSES = exports.FOOD_IMPORT_STATUSES = exports.FOOD_IMPORT_MODES = exports.FOOD_SUGGESTION_RESOLUTIONS = exports.FOOD_SUGGESTION_STATUSES = exports.NUTRITION_BASES = exports.PORTION_UNITS = exports.SERVING_UNITS = exports.FOOD_SOURCES = exports.TEMPLATE_STATUSES = exports.DELIVERY_STATUSES = exports.MESSAGE_TYPES = exports.COMMUNICATION_CHANNELS = exports.FEATURE_FLAG_RULE_TYPES = exports.AUDIT_OUTCOMES = exports.CSRF_HEADER = exports.CSRF_COOKIE_NAMES = exports.TOKEN_TRANSPORTS = exports.TIMEZONE_SOURCES = exports.ADMIN_SETTABLE_ACCOUNT_STATUSES = exports.ACCOUNT_STATUSES = exports.PERMISSIONS = exports.ROLES = exports.APP_ENVIRONMENTS = void 0;
|
|
6
6
|
exports.APP_ENVIRONMENTS = [
|
|
7
7
|
'development',
|
|
8
8
|
'staging',
|
|
@@ -39,6 +39,18 @@ exports.PERMISSIONS = {
|
|
|
39
39
|
foodsSearchSelf: 'foods:search:self',
|
|
40
40
|
foodSuggestionsSubmitSelf: 'food_suggestions:submit:self',
|
|
41
41
|
foodSuggestionsReadSelf: 'food_suggestions:read:self',
|
|
42
|
+
goalsRead: 'goals:read',
|
|
43
|
+
goalsCreate: 'goals:create',
|
|
44
|
+
goalsUpdate: 'goals:update',
|
|
45
|
+
goalsDelete: 'goals:delete',
|
|
46
|
+
goalsReadSelf: 'goals:read:self',
|
|
47
|
+
supplementsRead: 'supplements:read',
|
|
48
|
+
supplementsCreate: 'supplements:create',
|
|
49
|
+
supplementsUpdate: 'supplements:update',
|
|
50
|
+
supplementsDelete: 'supplements:delete',
|
|
51
|
+
supplementsImport: 'supplements:import',
|
|
52
|
+
supplementMetadataManage: 'supplement_metadata:manage',
|
|
53
|
+
supplementsSearchSelf: 'supplements:search:self',
|
|
42
54
|
};
|
|
43
55
|
/**
|
|
44
56
|
* `pending_verification` is a self-registered user who has not yet proved they
|
|
@@ -156,3 +168,21 @@ exports.FOOD_SORT_FIELDS = [
|
|
|
156
168
|
'protein',
|
|
157
169
|
];
|
|
158
170
|
exports.SORT_ORDERS = ['asc', 'desc'];
|
|
171
|
+
/** The icons a goal can show, by name. Each app maps a name to its own icon. */
|
|
172
|
+
exports.GOAL_ICONS = [
|
|
173
|
+
'target',
|
|
174
|
+
'trending-down',
|
|
175
|
+
'trending-up',
|
|
176
|
+
'dumbbell',
|
|
177
|
+
'refresh',
|
|
178
|
+
'activity',
|
|
179
|
+
'trophy',
|
|
180
|
+
'heart',
|
|
181
|
+
'flame',
|
|
182
|
+
'scale',
|
|
183
|
+
];
|
|
184
|
+
/** Whether clients can pick a goal: live goals are either shown or hidden. */
|
|
185
|
+
exports.GOAL_STATUSES = ['active', 'inactive'];
|
|
186
|
+
exports.GOAL_SORT_FIELDS = ['sortOrder', 'name', 'updatedAt'];
|
|
187
|
+
/** Where a supplement came from. Clients cannot suggest supplements, unlike foods. */
|
|
188
|
+
exports.SUPPLEMENT_SOURCES = ['ADMIN', 'COACH', 'IMPORT'];
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const MAX_GOAL_NAME_LENGTH = 60;
|
|
3
|
+
export declare const MAX_GOAL_DESCRIPTION_LENGTH = 200;
|
|
4
|
+
export declare const MAX_GOAL_SEARCH_LENGTH = 60;
|
|
5
|
+
export declare const MAX_GOAL_SORT_ORDER = 9999;
|
|
6
|
+
export declare const goalIdParamSchema: z.ZodUUID;
|
|
7
|
+
/** Lower case with spaces collapsed: what duplicate name checks compare. */
|
|
8
|
+
export declare const normalizeGoalName: (name: string) => string;
|
|
9
|
+
export declare const createGoalSchema: z.ZodObject<{
|
|
10
|
+
name: z.ZodString;
|
|
11
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
12
|
+
icon: z.ZodDefault<z.ZodEnum<{
|
|
13
|
+
target: "target";
|
|
14
|
+
"trending-down": "trending-down";
|
|
15
|
+
"trending-up": "trending-up";
|
|
16
|
+
dumbbell: "dumbbell";
|
|
17
|
+
refresh: "refresh";
|
|
18
|
+
activity: "activity";
|
|
19
|
+
trophy: "trophy";
|
|
20
|
+
heart: "heart";
|
|
21
|
+
flame: "flame";
|
|
22
|
+
scale: "scale";
|
|
23
|
+
}>>;
|
|
24
|
+
isActive: z.ZodDefault<z.ZodBoolean>;
|
|
25
|
+
sortOrder: z.ZodOptional<z.ZodNumber>;
|
|
26
|
+
}, z.core.$strict>;
|
|
27
|
+
export type CreateGoalInput = z.input<typeof createGoalSchema>;
|
|
28
|
+
export type CreateGoalBody = z.output<typeof createGoalSchema>;
|
|
29
|
+
export declare const updateGoalSchema: z.ZodObject<{
|
|
30
|
+
version: z.ZodNumber;
|
|
31
|
+
name: z.ZodOptional<z.ZodString>;
|
|
32
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
33
|
+
icon: z.ZodOptional<z.ZodEnum<{
|
|
34
|
+
target: "target";
|
|
35
|
+
"trending-down": "trending-down";
|
|
36
|
+
"trending-up": "trending-up";
|
|
37
|
+
dumbbell: "dumbbell";
|
|
38
|
+
refresh: "refresh";
|
|
39
|
+
activity: "activity";
|
|
40
|
+
trophy: "trophy";
|
|
41
|
+
heart: "heart";
|
|
42
|
+
flame: "flame";
|
|
43
|
+
scale: "scale";
|
|
44
|
+
}>>;
|
|
45
|
+
isActive: z.ZodOptional<z.ZodBoolean>;
|
|
46
|
+
sortOrder: z.ZodOptional<z.ZodNumber>;
|
|
47
|
+
}, z.core.$strict>;
|
|
48
|
+
export type UpdateGoalInput = z.input<typeof updateGoalSchema>;
|
|
49
|
+
export type UpdateGoalBody = z.output<typeof updateGoalSchema>;
|
|
50
|
+
export declare const goalListQuerySchema: z.ZodObject<{
|
|
51
|
+
page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
52
|
+
pageSize: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
53
|
+
search: z.ZodOptional<z.ZodString>;
|
|
54
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
55
|
+
active: "active";
|
|
56
|
+
inactive: "inactive";
|
|
57
|
+
}>>;
|
|
58
|
+
sort: z.ZodDefault<z.ZodEnum<{
|
|
59
|
+
name: "name";
|
|
60
|
+
updatedAt: "updatedAt";
|
|
61
|
+
sortOrder: "sortOrder";
|
|
62
|
+
}>>;
|
|
63
|
+
order: z.ZodDefault<z.ZodEnum<{
|
|
64
|
+
asc: "asc";
|
|
65
|
+
desc: "desc";
|
|
66
|
+
}>>;
|
|
67
|
+
}, z.core.$strip>;
|
|
68
|
+
export type GoalListQuery = z.output<typeof goalListQuerySchema>;
|
|
69
|
+
export declare const goalResponseSchema: z.ZodObject<{
|
|
70
|
+
id: z.ZodUUID;
|
|
71
|
+
name: z.ZodString;
|
|
72
|
+
description: z.ZodNullable<z.ZodString>;
|
|
73
|
+
icon: z.ZodEnum<{
|
|
74
|
+
target: "target";
|
|
75
|
+
"trending-down": "trending-down";
|
|
76
|
+
"trending-up": "trending-up";
|
|
77
|
+
dumbbell: "dumbbell";
|
|
78
|
+
refresh: "refresh";
|
|
79
|
+
activity: "activity";
|
|
80
|
+
trophy: "trophy";
|
|
81
|
+
heart: "heart";
|
|
82
|
+
flame: "flame";
|
|
83
|
+
scale: "scale";
|
|
84
|
+
}>;
|
|
85
|
+
isActive: z.ZodBoolean;
|
|
86
|
+
sortOrder: z.ZodNumber;
|
|
87
|
+
createdBy: z.ZodObject<{
|
|
88
|
+
id: z.ZodUUID;
|
|
89
|
+
fullName: z.ZodNullable<z.ZodString>;
|
|
90
|
+
}, z.core.$strip>;
|
|
91
|
+
version: z.ZodNumber;
|
|
92
|
+
createdAt: z.ZodISODateTime;
|
|
93
|
+
updatedAt: z.ZodISODateTime;
|
|
94
|
+
}, z.core.$strip>;
|
|
95
|
+
export type GoalResponse = z.output<typeof goalResponseSchema>;
|
|
96
|
+
export declare const goalStatsResponseSchema: z.ZodObject<{
|
|
97
|
+
total: z.ZodNumber;
|
|
98
|
+
active: z.ZodNumber;
|
|
99
|
+
inactive: z.ZodNumber;
|
|
100
|
+
}, z.core.$strip>;
|
|
101
|
+
export type GoalStatsResponse = z.output<typeof goalStatsResponseSchema>;
|
|
102
|
+
/** What a client sees: active goals only, without who edited them. */
|
|
103
|
+
export declare const clientGoalResponseSchema: z.ZodObject<{
|
|
104
|
+
id: z.ZodUUID;
|
|
105
|
+
name: z.ZodString;
|
|
106
|
+
description: z.ZodNullable<z.ZodString>;
|
|
107
|
+
icon: z.ZodEnum<{
|
|
108
|
+
target: "target";
|
|
109
|
+
"trending-down": "trending-down";
|
|
110
|
+
"trending-up": "trending-up";
|
|
111
|
+
dumbbell: "dumbbell";
|
|
112
|
+
refresh: "refresh";
|
|
113
|
+
activity: "activity";
|
|
114
|
+
trophy: "trophy";
|
|
115
|
+
heart: "heart";
|
|
116
|
+
flame: "flame";
|
|
117
|
+
scale: "scale";
|
|
118
|
+
}>;
|
|
119
|
+
}, z.core.$strip>;
|
|
120
|
+
export type ClientGoalResponse = z.output<typeof clientGoalResponseSchema>;
|
|
121
|
+
export declare const clientGoalListResponseSchema: z.ZodArray<z.ZodObject<{
|
|
122
|
+
id: z.ZodUUID;
|
|
123
|
+
name: z.ZodString;
|
|
124
|
+
description: z.ZodNullable<z.ZodString>;
|
|
125
|
+
icon: z.ZodEnum<{
|
|
126
|
+
target: "target";
|
|
127
|
+
"trending-down": "trending-down";
|
|
128
|
+
"trending-up": "trending-up";
|
|
129
|
+
dumbbell: "dumbbell";
|
|
130
|
+
refresh: "refresh";
|
|
131
|
+
activity: "activity";
|
|
132
|
+
trophy: "trophy";
|
|
133
|
+
heart: "heart";
|
|
134
|
+
flame: "flame";
|
|
135
|
+
scale: "scale";
|
|
136
|
+
}>;
|
|
137
|
+
}, z.core.$strip>>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.clientGoalListResponseSchema = exports.clientGoalResponseSchema = exports.goalStatsResponseSchema = exports.goalResponseSchema = exports.goalListQuerySchema = exports.updateGoalSchema = exports.createGoalSchema = exports.normalizeGoalName = exports.goalIdParamSchema = exports.MAX_GOAL_SORT_ORDER = exports.MAX_GOAL_SEARCH_LENGTH = exports.MAX_GOAL_DESCRIPTION_LENGTH = exports.MAX_GOAL_NAME_LENGTH = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const date_time_schema_js_1 = require("../common/date-time.schema.js");
|
|
6
|
+
const pagination_schema_js_1 = require("../common/pagination.schema.js");
|
|
7
|
+
const safe_text_schema_js_1 = require("../common/safe-text.schema.js");
|
|
8
|
+
const index_js_1 = require("../enums/index.js");
|
|
9
|
+
const food_dto_js_1 = require("../food/food.dto.js");
|
|
10
|
+
// A goal is what a client works towards, such as "Build muscle". Coaches keep
|
|
11
|
+
// the list; clients pick one from the goals that are active.
|
|
12
|
+
//
|
|
13
|
+
// Each request has two types. `*Input` is what a client sends, where fields with
|
|
14
|
+
// a default are optional. `*Body` is what the API receives after validation.
|
|
15
|
+
exports.MAX_GOAL_NAME_LENGTH = 60;
|
|
16
|
+
exports.MAX_GOAL_DESCRIPTION_LENGTH = 200;
|
|
17
|
+
exports.MAX_GOAL_SEARCH_LENGTH = 60;
|
|
18
|
+
exports.MAX_GOAL_SORT_ORDER = 9999;
|
|
19
|
+
exports.goalIdParamSchema = zod_1.z.uuid();
|
|
20
|
+
/** Lower case with spaces collapsed: what duplicate name checks compare. */
|
|
21
|
+
const normalizeGoalName = (name) => name.trim().replace(/\s+/g, ' ').toLowerCase();
|
|
22
|
+
exports.normalizeGoalName = normalizeGoalName;
|
|
23
|
+
const goalNameSchema = (0, safe_text_schema_js_1.safeTextSchema)(exports.MAX_GOAL_NAME_LENGTH);
|
|
24
|
+
const goalDescriptionSchema = (0, safe_text_schema_js_1.safeTextSchema)(exports.MAX_GOAL_DESCRIPTION_LENGTH);
|
|
25
|
+
const sortOrderSchema = zod_1.z.number().int().min(0).max(exports.MAX_GOAL_SORT_ORDER);
|
|
26
|
+
// Strict objects reject unknown fields, so an attempt to set `createdBy`
|
|
27
|
+
// through the API fails loudly.
|
|
28
|
+
exports.createGoalSchema = zod_1.z.strictObject({
|
|
29
|
+
name: goalNameSchema,
|
|
30
|
+
/** One line under the name, such as "Focus on hypertrophy and progressive overload." */
|
|
31
|
+
description: goalDescriptionSchema.nullable().optional(),
|
|
32
|
+
icon: zod_1.z.enum(index_js_1.GOAL_ICONS).default('target'),
|
|
33
|
+
/** Inactive goals stay in the list but are not offered to clients. */
|
|
34
|
+
isActive: zod_1.z.boolean().default(true),
|
|
35
|
+
/** Lower comes first. Left out, the goal goes after the others. */
|
|
36
|
+
sortOrder: sortOrderSchema.optional(),
|
|
37
|
+
});
|
|
38
|
+
exports.updateGoalSchema = zod_1.z
|
|
39
|
+
.strictObject({
|
|
40
|
+
// The version last read, so that a stale edit is refused rather than lost.
|
|
41
|
+
version: zod_1.z.number().int().min(1),
|
|
42
|
+
name: goalNameSchema.optional(),
|
|
43
|
+
description: goalDescriptionSchema.nullable().optional(),
|
|
44
|
+
icon: zod_1.z.enum(index_js_1.GOAL_ICONS).optional(),
|
|
45
|
+
isActive: zod_1.z.boolean().optional(),
|
|
46
|
+
sortOrder: sortOrderSchema.optional(),
|
|
47
|
+
})
|
|
48
|
+
.refine(({ version: _version, ...changes }) => Object.values(changes).some((value) => value !== undefined), { error: 'Provide at least one field to change' });
|
|
49
|
+
exports.goalListQuerySchema = pagination_schema_js_1.paginationQuerySchema.extend({
|
|
50
|
+
search: zod_1.z.string().trim().min(1).max(exports.MAX_GOAL_SEARCH_LENGTH).optional(),
|
|
51
|
+
status: zod_1.z.enum(index_js_1.GOAL_STATUSES).optional(),
|
|
52
|
+
sort: zod_1.z.enum(index_js_1.GOAL_SORT_FIELDS).default('sortOrder'),
|
|
53
|
+
order: zod_1.z.enum(index_js_1.SORT_ORDERS).default('asc'),
|
|
54
|
+
});
|
|
55
|
+
// Responses
|
|
56
|
+
exports.goalResponseSchema = zod_1.z.object({
|
|
57
|
+
id: zod_1.z.uuid(),
|
|
58
|
+
name: zod_1.z.string(),
|
|
59
|
+
description: zod_1.z.string().nullable(),
|
|
60
|
+
icon: zod_1.z.enum(index_js_1.GOAL_ICONS),
|
|
61
|
+
isActive: zod_1.z.boolean(),
|
|
62
|
+
sortOrder: zod_1.z.number().int(),
|
|
63
|
+
createdBy: food_dto_js_1.userRefSchema,
|
|
64
|
+
version: zod_1.z.number().int(),
|
|
65
|
+
createdAt: date_time_schema_js_1.utcTimestampSchema,
|
|
66
|
+
updatedAt: date_time_schema_js_1.utcTimestampSchema,
|
|
67
|
+
});
|
|
68
|
+
exports.goalStatsResponseSchema = zod_1.z.object({
|
|
69
|
+
total: zod_1.z.number().int(),
|
|
70
|
+
active: zod_1.z.number().int(),
|
|
71
|
+
inactive: zod_1.z.number().int(),
|
|
72
|
+
});
|
|
73
|
+
/** What a client sees: active goals only, without who edited them. */
|
|
74
|
+
exports.clientGoalResponseSchema = zod_1.z.object({
|
|
75
|
+
id: zod_1.z.uuid(),
|
|
76
|
+
name: zod_1.z.string(),
|
|
77
|
+
description: zod_1.z.string().nullable(),
|
|
78
|
+
icon: zod_1.z.enum(index_js_1.GOAL_ICONS),
|
|
79
|
+
});
|
|
80
|
+
exports.clientGoalListResponseSchema = zod_1.z.array(exports.clientGoalResponseSchema);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './goal.dto.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./goal.dto.js"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './supplement.dto.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./supplement.dto.js"), exports);
|