@lcas58/esmi-api-types 1.0.28 → 1.0.30

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.
@@ -1,8 +1,28 @@
1
- import { and, count, eq, gte, ilike, lte, or, sql } from "drizzle-orm";
1
+ import { and, count, eq, gte, ilike, inArray, lte, or, sql } from "drizzle-orm";
2
2
  import * as HttpStatusCodes from "stoker/http-status-codes";
3
3
  import * as HttpStatusPhrases from "stoker/http-status-phrases";
4
4
  import { createDb } from "../../db/index.js";
5
- import { event, group } from "../../db/schema/index.js";
5
+ import { event, group, groupTag, tag } from "../../db/schema/index.js";
6
+ function withTags(g) {
7
+ const { groupTags, ...rest } = g;
8
+ return { ...rest, tags: groupTags.map(gt => ({ id: gt.tag.id, name: gt.tag.name })) };
9
+ }
10
+ async function upsertTags(db, groupId, tagNames) {
11
+ if (tagNames.length === 0) {
12
+ await db.delete(groupTag).where(eq(groupTag.groupId, groupId));
13
+ return;
14
+ }
15
+ const slugs = tagNames.map(n => slugify(n));
16
+ await db.insert(tag)
17
+ .values(tagNames.map((name, i) => ({ name, slug: slugs[i] })))
18
+ .onConflictDoNothing({ target: tag.slug });
19
+ const tagRecords = await db.select({ id: tag.id, slug: tag.slug })
20
+ .from(tag)
21
+ .where(inArray(tag.slug, slugs));
22
+ await db.delete(groupTag).where(eq(groupTag.groupId, groupId));
23
+ await db.insert(groupTag)
24
+ .values(tagRecords.map(t => ({ groupId, tagId: t.id })));
25
+ }
6
26
  function slugify(name) {
7
27
  return name
8
28
  .toLowerCase()
@@ -14,7 +34,7 @@ function slugify(name) {
14
34
  export const list = async (c) => {
15
35
  const { db } = createDb(c.env);
16
36
  const query = c.req.valid("query");
17
- const { sportId, source, search } = query;
37
+ const { sportId, platform, search } = query;
18
38
  const limit = query.limit ?? 20;
19
39
  const offset = query.offset ?? 0;
20
40
  const city = query.city?.trim();
@@ -23,8 +43,8 @@ export const list = async (c) => {
23
43
  if (sportId) {
24
44
  whereConditions.push(eq(group.sportId, sportId));
25
45
  }
26
- if (source) {
27
- whereConditions.push(eq(group.source, source));
46
+ if (platform) {
47
+ whereConditions.push(eq(group.platform, platform));
28
48
  }
29
49
  if (city) {
30
50
  whereConditions.push(ilike(group.city, city));
@@ -46,6 +66,9 @@ export const list = async (c) => {
46
66
  image: true,
47
67
  },
48
68
  },
69
+ groupTags: {
70
+ with: { tag: true },
71
+ },
49
72
  },
50
73
  limit,
51
74
  offset,
@@ -66,7 +89,7 @@ export const list = async (c) => {
66
89
  eventCounts = Object.fromEntries(counts.map(r => [r.groupId, r.count]));
67
90
  }
68
91
  const result = groups.map(g => ({
69
- ...g,
92
+ ...withTags(g),
70
93
  _count: { events: eventCounts[g.id] ?? 0 },
71
94
  }));
72
95
  return c.json(result, HttpStatusCodes.OK);
@@ -85,12 +108,15 @@ export const getOne = async (c) => {
85
108
  image: true,
86
109
  },
87
110
  },
111
+ groupTags: {
112
+ with: { tag: true },
113
+ },
88
114
  },
89
115
  });
90
116
  if (!foundGroup) {
91
117
  return c.json({ message: HttpStatusPhrases.NOT_FOUND }, HttpStatusCodes.NOT_FOUND);
92
118
  }
93
- return c.json(foundGroup, HttpStatusCodes.OK);
119
+ return c.json(withTags(foundGroup), HttpStatusCodes.OK);
94
120
  };
95
121
  export const getEvents = async (c) => {
96
122
  const { db } = createDb(c.env);
@@ -145,13 +171,18 @@ export const create = async (c) => {
145
171
  name: body.name,
146
172
  slug,
147
173
  description: body.description,
148
- source: "internal",
149
174
  sportId: body.sportId,
150
175
  city: body.city,
151
176
  state: body.state,
152
177
  imageUrl: body.imageUrl,
178
+ skillLevel: body.skillLevel,
179
+ vibe: body.vibe,
180
+ activityFrequency: body.activityFrequency,
153
181
  createdByUserId: user.id,
154
182
  }).returning();
183
+ if (body.tags?.length) {
184
+ await upsertTags(db, newGroup.id, body.tags);
185
+ }
155
186
  const result = await db.query.group.findFirst({
156
187
  where: eq(group.id, newGroup.id),
157
188
  with: {
@@ -163,9 +194,12 @@ export const create = async (c) => {
163
194
  image: true,
164
195
  },
165
196
  },
197
+ groupTags: {
198
+ with: { tag: true },
199
+ },
166
200
  },
167
201
  });
168
- return c.json(result, HttpStatusCodes.OK);
202
+ return c.json(withTags(result), HttpStatusCodes.OK);
169
203
  };
170
204
  export const update = async (c) => {
171
205
  const { db } = createDb(c.env);
@@ -178,16 +212,20 @@ export const update = async (c) => {
178
212
  if (!existing) {
179
213
  return c.json({ message: HttpStatusPhrases.NOT_FOUND }, HttpStatusCodes.NOT_FOUND);
180
214
  }
181
- if (existing.source === "external") {
215
+ if (existing.platform) {
182
216
  return c.json({ message: "Cannot update external groups" }, HttpStatusCodes.UNAUTHORIZED);
183
217
  }
184
218
  if (existing.createdByUserId !== user.id) {
185
219
  return c.json({ message: HttpStatusPhrases.UNAUTHORIZED }, HttpStatusCodes.UNAUTHORIZED);
186
220
  }
221
+ const { tags: tagNames, ...groupFields } = body;
187
222
  await db.update(group).set({
188
- ...body,
223
+ ...groupFields,
189
224
  updatedAt: new Date(),
190
225
  }).where(eq(group.id, id));
226
+ if (tagNames !== undefined) {
227
+ await upsertTags(db, id, tagNames ?? []);
228
+ }
191
229
  const result = await db.query.group.findFirst({
192
230
  where: eq(group.id, id),
193
231
  with: {
@@ -199,7 +237,10 @@ export const update = async (c) => {
199
237
  image: true,
200
238
  },
201
239
  },
240
+ groupTags: {
241
+ with: { tag: true },
242
+ },
202
243
  },
203
244
  });
204
- return c.json(result, HttpStatusCodes.OK);
245
+ return c.json(withTags(result), HttpStatusCodes.OK);
205
246
  };
@@ -4,10 +4,10 @@ declare const router: import("@hono/zod-openapi").OpenAPIHono<import("../../shar
4
4
  input: {
5
5
  query: {
6
6
  search?: string | string[] | undefined;
7
+ sportId?: string | string[] | undefined;
7
8
  city?: string | string[] | undefined;
8
9
  state?: string | string[] | undefined;
9
- sportId?: string | string[] | undefined;
10
- source?: string | string[] | undefined;
10
+ platform?: string | string[] | undefined;
11
11
  limit?: string | string[] | undefined;
12
12
  offset?: string | string[] | undefined;
13
13
  };
@@ -16,29 +16,35 @@ declare const router: import("@hono/zod-openapi").OpenAPIHono<import("../../shar
16
16
  id: string;
17
17
  description: string | null;
18
18
  name: string;
19
- city: string | null;
20
- state: string | null;
21
- sportId: string;
22
19
  createdAt: string;
23
20
  sport: {
24
21
  id: string;
25
22
  name: string;
26
23
  icon: string | null;
27
24
  } | null;
25
+ sportId: string;
26
+ city: string | null;
27
+ state: string | null;
28
28
  country: string;
29
- source: "external" | "internal";
30
29
  slug: string;
31
- isActive: boolean;
32
- sourceOrgId: string | null;
30
+ platform: string | null;
33
31
  externalUrl: string | null;
34
32
  updatedAt: string;
35
33
  createdByUserId: string | null;
36
34
  imageUrl: string | null;
35
+ skillLevel: "advanced" | "beginner" | "intermediate" | "all-levels" | null;
36
+ vibe: "social" | "casual" | "competitive" | "fitness" | null;
37
+ activityFrequency: "weekly" | "biweekly" | "monthly" | "sporadic" | null;
38
+ isActive: boolean;
37
39
  creator: {
38
40
  image: string | null;
39
41
  id: string;
40
42
  name: string;
41
43
  } | null;
44
+ tags: {
45
+ id: string;
46
+ name: string;
47
+ }[];
42
48
  _count?: {
43
49
  events: number;
44
50
  } | undefined;
@@ -70,29 +76,35 @@ declare const router: import("@hono/zod-openapi").OpenAPIHono<import("../../shar
70
76
  id: string;
71
77
  description: string | null;
72
78
  name: string;
73
- city: string | null;
74
- state: string | null;
75
- sportId: string;
76
79
  createdAt: string;
77
80
  sport: {
78
81
  id: string;
79
82
  name: string;
80
83
  icon: string | null;
81
84
  } | null;
85
+ sportId: string;
86
+ city: string | null;
87
+ state: string | null;
82
88
  country: string;
83
- source: "external" | "internal";
84
89
  slug: string;
85
- isActive: boolean;
86
- sourceOrgId: string | null;
90
+ platform: string | null;
87
91
  externalUrl: string | null;
88
92
  updatedAt: string;
89
93
  createdByUserId: string | null;
90
94
  imageUrl: string | null;
95
+ skillLevel: "advanced" | "beginner" | "intermediate" | "all-levels" | null;
96
+ vibe: "social" | "casual" | "competitive" | "fitness" | null;
97
+ activityFrequency: "weekly" | "biweekly" | "monthly" | "sporadic" | null;
98
+ isActive: boolean;
91
99
  creator: {
92
100
  image: string | null;
93
101
  id: string;
94
102
  name: string;
95
103
  } | null;
104
+ tags: {
105
+ id: string;
106
+ name: string;
107
+ }[];
96
108
  _count?: {
97
109
  events: number;
98
110
  } | undefined;
@@ -119,23 +131,24 @@ declare const router: import("@hono/zod-openapi").OpenAPIHono<import("../../shar
119
131
  output: {
120
132
  metadata: any;
121
133
  id: string;
134
+ description: string | null;
122
135
  title: string;
123
- sportId: string;
124
136
  createdAt: string;
125
137
  sport: {
126
138
  id: string;
127
139
  name: string;
128
140
  icon: string | null;
129
141
  } | null;
142
+ sportId: string;
130
143
  startsAt: string;
131
144
  endsAt: string | null;
132
145
  timezone: string;
133
146
  location: {
134
147
  id: string;
135
148
  name: string;
149
+ formattedAddress: string;
136
150
  city: string | null;
137
151
  state: string | null;
138
- formattedAddress: string;
139
152
  latitude: number | null;
140
153
  longitude: number | null;
141
154
  } | null;
@@ -190,6 +203,10 @@ declare const router: import("@hono/zod-openapi").OpenAPIHono<import("../../shar
190
203
  state?: string | undefined;
191
204
  slug?: string | undefined;
192
205
  imageUrl?: string | undefined;
206
+ skillLevel?: "advanced" | "beginner" | "intermediate" | "all-levels" | undefined;
207
+ vibe?: "social" | "casual" | "competitive" | "fitness" | undefined;
208
+ activityFrequency?: "weekly" | "biweekly" | "monthly" | "sporadic" | undefined;
209
+ tags?: string[] | undefined;
193
210
  };
194
211
  };
195
212
  output: {
@@ -207,35 +224,45 @@ declare const router: import("@hono/zod-openapi").OpenAPIHono<import("../../shar
207
224
  state?: string | undefined;
208
225
  slug?: string | undefined;
209
226
  imageUrl?: string | undefined;
227
+ skillLevel?: "advanced" | "beginner" | "intermediate" | "all-levels" | undefined;
228
+ vibe?: "social" | "casual" | "competitive" | "fitness" | undefined;
229
+ activityFrequency?: "weekly" | "biweekly" | "monthly" | "sporadic" | undefined;
230
+ tags?: string[] | undefined;
210
231
  };
211
232
  };
212
233
  output: {
213
234
  id: string;
214
235
  description: string | null;
215
236
  name: string;
216
- city: string | null;
217
- state: string | null;
218
- sportId: string;
219
237
  createdAt: string;
220
238
  sport: {
221
239
  id: string;
222
240
  name: string;
223
241
  icon: string | null;
224
242
  } | null;
243
+ sportId: string;
244
+ city: string | null;
245
+ state: string | null;
225
246
  country: string;
226
- source: "external" | "internal";
227
247
  slug: string;
228
- isActive: boolean;
229
- sourceOrgId: string | null;
248
+ platform: string | null;
230
249
  externalUrl: string | null;
231
250
  updatedAt: string;
232
251
  createdByUserId: string | null;
233
252
  imageUrl: string | null;
253
+ skillLevel: "advanced" | "beginner" | "intermediate" | "all-levels" | null;
254
+ vibe: "social" | "casual" | "competitive" | "fitness" | null;
255
+ activityFrequency: "weekly" | "biweekly" | "monthly" | "sporadic" | null;
256
+ isActive: boolean;
234
257
  creator: {
235
258
  image: string | null;
236
259
  id: string;
237
260
  name: string;
238
261
  } | null;
262
+ tags: {
263
+ id: string;
264
+ name: string;
265
+ }[];
239
266
  _count?: {
240
267
  events: number;
241
268
  } | undefined;
@@ -258,6 +285,10 @@ declare const router: import("@hono/zod-openapi").OpenAPIHono<import("../../shar
258
285
  city?: string | undefined;
259
286
  state?: string | undefined;
260
287
  imageUrl?: string | null | undefined;
288
+ skillLevel?: "advanced" | "beginner" | "intermediate" | "all-levels" | null | undefined;
289
+ vibe?: "social" | "casual" | "competitive" | "fitness" | null | undefined;
290
+ activityFrequency?: "weekly" | "biweekly" | "monthly" | "sporadic" | null | undefined;
291
+ tags?: string[] | null | undefined;
261
292
  };
262
293
  };
263
294
  output: {
@@ -277,6 +308,10 @@ declare const router: import("@hono/zod-openapi").OpenAPIHono<import("../../shar
277
308
  city?: string | undefined;
278
309
  state?: string | undefined;
279
310
  imageUrl?: string | null | undefined;
311
+ skillLevel?: "advanced" | "beginner" | "intermediate" | "all-levels" | null | undefined;
312
+ vibe?: "social" | "casual" | "competitive" | "fitness" | null | undefined;
313
+ activityFrequency?: "weekly" | "biweekly" | "monthly" | "sporadic" | null | undefined;
314
+ tags?: string[] | null | undefined;
280
315
  };
281
316
  };
282
317
  output: {
@@ -296,35 +331,45 @@ declare const router: import("@hono/zod-openapi").OpenAPIHono<import("../../shar
296
331
  city?: string | undefined;
297
332
  state?: string | undefined;
298
333
  imageUrl?: string | null | undefined;
334
+ skillLevel?: "advanced" | "beginner" | "intermediate" | "all-levels" | null | undefined;
335
+ vibe?: "social" | "casual" | "competitive" | "fitness" | null | undefined;
336
+ activityFrequency?: "weekly" | "biweekly" | "monthly" | "sporadic" | null | undefined;
337
+ tags?: string[] | null | undefined;
299
338
  };
300
339
  };
301
340
  output: {
302
341
  id: string;
303
342
  description: string | null;
304
343
  name: string;
305
- city: string | null;
306
- state: string | null;
307
- sportId: string;
308
344
  createdAt: string;
309
345
  sport: {
310
346
  id: string;
311
347
  name: string;
312
348
  icon: string | null;
313
349
  } | null;
350
+ sportId: string;
351
+ city: string | null;
352
+ state: string | null;
314
353
  country: string;
315
- source: "external" | "internal";
316
354
  slug: string;
317
- isActive: boolean;
318
- sourceOrgId: string | null;
355
+ platform: string | null;
319
356
  externalUrl: string | null;
320
357
  updatedAt: string;
321
358
  createdByUserId: string | null;
322
359
  imageUrl: string | null;
360
+ skillLevel: "advanced" | "beginner" | "intermediate" | "all-levels" | null;
361
+ vibe: "social" | "casual" | "competitive" | "fitness" | null;
362
+ activityFrequency: "weekly" | "biweekly" | "monthly" | "sporadic" | null;
363
+ isActive: boolean;
323
364
  creator: {
324
365
  image: string | null;
325
366
  id: string;
326
367
  name: string;
327
368
  } | null;
369
+ tags: {
370
+ id: string;
371
+ name: string;
372
+ }[];
328
373
  _count?: {
329
374
  events: number;
330
375
  } | undefined;