@voyant-travel/inventory 0.4.3 → 0.4.4

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.
@@ -62,15 +62,27 @@ export declare const optionUnitTranslationsRelations: import("drizzle-orm").Rela
62
62
  }>;
63
63
  export declare const productItinerariesRelations: import("drizzle-orm").Relations<"product_itineraries", {
64
64
  product: import("drizzle-orm").One<"products", true>;
65
+ translations: import("drizzle-orm").Many<"product_itinerary_translations">;
65
66
  days: import("drizzle-orm").Many<"product_days">;
66
67
  }>;
68
+ export declare const productItineraryTranslationsRelations: import("drizzle-orm").Relations<"product_itinerary_translations", {
69
+ itinerary: import("drizzle-orm").One<"product_itineraries", true>;
70
+ }>;
67
71
  export declare const productDaysRelations: import("drizzle-orm").Relations<"product_days", {
68
72
  itinerary: import("drizzle-orm").One<"product_itineraries", true>;
73
+ translations: import("drizzle-orm").Many<"product_day_translations">;
69
74
  services: import("drizzle-orm").Many<"product_day_services">;
70
75
  media: import("drizzle-orm").Many<"product_media">;
71
76
  }>;
77
+ export declare const productDayTranslationsRelations: import("drizzle-orm").Relations<"product_day_translations", {
78
+ day: import("drizzle-orm").One<"product_days", true>;
79
+ }>;
72
80
  export declare const productDayServicesRelations: import("drizzle-orm").Relations<"product_day_services", {
73
81
  day: import("drizzle-orm").One<"product_days", true>;
82
+ translations: import("drizzle-orm").Many<"product_day_service_translations">;
83
+ }>;
84
+ export declare const productDayServiceTranslationsRelations: import("drizzle-orm").Relations<"product_day_service_translations", {
85
+ service: import("drizzle-orm").One<"product_day_services", true>;
74
86
  }>;
75
87
  export declare const productVersionsRelations: import("drizzle-orm").Relations<"product_versions", {
76
88
  product: import("drizzle-orm").One<"products", true>;
@@ -1 +1 @@
1
- {"version":3,"file":"schema-relations.d.ts","sourceRoot":"","sources":["../src/schema-relations.ts"],"names":[],"mappings":"AAmCA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;EAsB3B,CAAA;AAEH,eAAO,MAAM,uBAAuB;;;;EAIjC,CAAA;AAEH,eAAO,MAAM,oBAAoB;;;EAG9B,CAAA;AAEH,eAAO,MAAM,kCAAkC;;EAQ9C,CAAA;AAED,eAAO,MAAM,8BAA8B;;EAKxC,CAAA;AAEH,eAAO,MAAM,kCAAkC;;EAQ9C,CAAA;AAED,eAAO,MAAM,4BAA4B;;EAKtC,CAAA;AAEH,eAAO,MAAM,+BAA+B;;EAKzC,CAAA;AAEH,eAAO,MAAM,wBAAwB;;EAKlC,CAAA;AAEH,eAAO,MAAM,oBAAoB;;EAK9B,CAAA;AAEH,eAAO,MAAM,yBAAyB;;EAKnC,CAAA;AAEH,eAAO,MAAM,4BAA4B;;EAKtC,CAAA;AAEH,eAAO,MAAM,kCAAkC;;EAQ9C,CAAA;AAED,eAAO,MAAM,+BAA+B;;EAKzC,CAAA;AAEH,eAAO,MAAM,2BAA2B;;;EAMrC,CAAA;AAEH,eAAO,MAAM,oBAAoB;;;;EAO9B,CAAA;AAEH,eAAO,MAAM,2BAA2B;;EAErC,CAAA;AAEH,eAAO,MAAM,wBAAwB;;EAElC,CAAA;AAEH,eAAO,MAAM,qBAAqB;;EAE/B,CAAA;AAEH,eAAO,MAAM,qBAAqB;;;EAG/B,CAAA;AAEH,eAAO,MAAM,qBAAqB;;EAE/B,CAAA;AAEH,eAAO,MAAM,qBAAqB;;;;;EAS/B,CAAA;AAEH,eAAO,MAAM,gCAAgC;;EAK1C,CAAA;AAEH,eAAO,MAAM,0BAA0B;;;;EAQpC,CAAA;AAEH,eAAO,MAAM,oBAAoB;;EAE9B,CAAA;AAEH,eAAO,MAAM,gCAAgC;;;EAS1C,CAAA;AAEH,eAAO,MAAM,2BAA2B;;;EASrC,CAAA;AAEH,eAAO,MAAM,4BAA4B;;;EAStC,CAAA"}
1
+ {"version":3,"file":"schema-relations.d.ts","sourceRoot":"","sources":["../src/schema-relations.ts"],"names":[],"mappings":"AAsCA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;EAsB3B,CAAA;AAEH,eAAO,MAAM,uBAAuB;;;;EAIjC,CAAA;AAEH,eAAO,MAAM,oBAAoB;;;EAG9B,CAAA;AAEH,eAAO,MAAM,kCAAkC;;EAQ9C,CAAA;AAED,eAAO,MAAM,8BAA8B;;EAKxC,CAAA;AAEH,eAAO,MAAM,kCAAkC;;EAQ9C,CAAA;AAED,eAAO,MAAM,4BAA4B;;EAKtC,CAAA;AAEH,eAAO,MAAM,+BAA+B;;EAKzC,CAAA;AAEH,eAAO,MAAM,wBAAwB;;EAKlC,CAAA;AAEH,eAAO,MAAM,oBAAoB;;EAK9B,CAAA;AAEH,eAAO,MAAM,yBAAyB;;EAKnC,CAAA;AAEH,eAAO,MAAM,4BAA4B;;EAKtC,CAAA;AAEH,eAAO,MAAM,kCAAkC;;EAQ9C,CAAA;AAED,eAAO,MAAM,+BAA+B;;EAKzC,CAAA;AAEH,eAAO,MAAM,2BAA2B;;;;EAOrC,CAAA;AAEH,eAAO,MAAM,qCAAqC;;EAQjD,CAAA;AAED,eAAO,MAAM,oBAAoB;;;;;EAQ9B,CAAA;AAEH,eAAO,MAAM,+BAA+B;;EAEzC,CAAA;AAEH,eAAO,MAAM,2BAA2B;;;EAGrC,CAAA;AAEH,eAAO,MAAM,sCAAsC;;EAQlD,CAAA;AAED,eAAO,MAAM,wBAAwB;;EAElC,CAAA;AAEH,eAAO,MAAM,qBAAqB;;EAE/B,CAAA;AAEH,eAAO,MAAM,qBAAqB;;;EAG/B,CAAA;AAEH,eAAO,MAAM,qBAAqB;;EAE/B,CAAA;AAEH,eAAO,MAAM,qBAAqB;;;;;EAS/B,CAAA;AAEH,eAAO,MAAM,gCAAgC;;EAK1C,CAAA;AAEH,eAAO,MAAM,0BAA0B;;;;EAQpC,CAAA;AAEH,eAAO,MAAM,oBAAoB;;EAE9B,CAAA;AAEH,eAAO,MAAM,gCAAgC;;;EAS1C,CAAA;AAEH,eAAO,MAAM,2BAA2B;;;EASrC,CAAA;AAEH,eAAO,MAAM,4BAA4B;;;EAStC,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { relations } from "drizzle-orm";
2
2
  import { optionUnits, productOptions, products } from "./schema-core.js";
3
- import { productDayServices, productDays, productItineraries, productMedia, productNotes, productVersions, } from "./schema-itinerary.js";
3
+ import { productDayServices, productDayServiceTranslations, productDays, productDayTranslations, productItineraries, productItineraryTranslations, productMedia, productNotes, productVersions, } from "./schema-itinerary.js";
4
4
  import { optionUnitTranslations, productActivationSettings, productCapabilities, productDeliveryFormats, productFaqs, productFeatures, productLocations, productOptionTranslations, productTicketSettings, productTranslations, productVisibilitySettings, } from "./schema-settings.js";
5
5
  import { destinations, destinationTranslations, productCategories, productCategoryProducts, productDestinations, productTagProducts, productTags, productTypes, } from "./schema-taxonomy.js";
6
6
  export const productsRelations = relations(products, ({ one, many }) => ({
@@ -106,18 +106,36 @@ export const productItinerariesRelations = relations(productItineraries, ({ one,
106
106
  fields: [productItineraries.productId],
107
107
  references: [products.id],
108
108
  }),
109
+ translations: many(productItineraryTranslations),
109
110
  days: many(productDays),
110
111
  }));
112
+ export const productItineraryTranslationsRelations = relations(productItineraryTranslations, ({ one }) => ({
113
+ itinerary: one(productItineraries, {
114
+ fields: [productItineraryTranslations.itineraryId],
115
+ references: [productItineraries.id],
116
+ }),
117
+ }));
111
118
  export const productDaysRelations = relations(productDays, ({ one, many }) => ({
112
119
  itinerary: one(productItineraries, {
113
120
  fields: [productDays.itineraryId],
114
121
  references: [productItineraries.id],
115
122
  }),
123
+ translations: many(productDayTranslations),
116
124
  services: many(productDayServices),
117
125
  media: many(productMedia),
118
126
  }));
119
- export const productDayServicesRelations = relations(productDayServices, ({ one }) => ({
127
+ export const productDayTranslationsRelations = relations(productDayTranslations, ({ one }) => ({
128
+ day: one(productDays, { fields: [productDayTranslations.dayId], references: [productDays.id] }),
129
+ }));
130
+ export const productDayServicesRelations = relations(productDayServices, ({ one, many }) => ({
120
131
  day: one(productDays, { fields: [productDayServices.dayId], references: [productDays.id] }),
132
+ translations: many(productDayServiceTranslations),
133
+ }));
134
+ export const productDayServiceTranslationsRelations = relations(productDayServiceTranslations, ({ one }) => ({
135
+ service: one(productDayServices, {
136
+ fields: [productDayServiceTranslations.serviceId],
137
+ references: [productDayServices.id],
138
+ }),
121
139
  }));
122
140
  export const productVersionsRelations = relations(productVersions, ({ one }) => ({
123
141
  product: one(products, { fields: [productVersions.productId], references: [products.id] }),
@@ -20,15 +20,11 @@
20
20
  * The projection reads in parallel:
21
21
  * - `products` row → product summary + tags + supplier
22
22
  * - `product_translations` → localized name + description per locale
23
- * - `product_itineraries` + `product_days` itinerary days (no
24
- * translation table today; falls back to source)
23
+ * - `product_itineraries` + `product_days` + translations localized
24
+ * itinerary days + day service labels
25
25
  * - `product_options` + `product_option_translations` → options +
26
26
  * localized labels
27
27
  * - `product_media` → hero + gallery
28
- *
29
- * Day translations don't exist in the schema yet — when
30
- * `product_day_translations` lands, this function picks them up the
31
- * same way.
32
28
  */
33
29
  import type { ContentLocaleMatchKind } from "@voyant-travel/catalog";
34
30
  import type { AnyDrizzleDb } from "@voyant-travel/db";
@@ -1 +1 @@
1
- {"version":3,"file":"service-content-owned.d.ts","sourceRoot":"","sources":["../src/service-content-owned.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGrD,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAA;AAW3B,MAAM,WAAW,+BAA+B;IAC9C;;;;;OAKG;IACH,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,8BAA8B;IAC7C,qDAAqD;IACrD,OAAO,EAAE,cAAc,CAAA;IACvB;;;;;OAKG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,SAAS,EAAE,sBAAsB,CAAA;CAClC;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAkJhD"}
1
+ {"version":3,"file":"service-content-owned.d.ts","sourceRoot":"","sources":["../src/service-content-owned.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGrD,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAA;AAc3B,MAAM,WAAW,+BAA+B;IAC9C;;;;;OAKG;IACH,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,8BAA8B;IAC7C,qDAAqD;IACrD,OAAO,EAAE,cAAc,CAAA;IACvB;;;;;OAKG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,SAAS,EAAE,sBAAsB,CAAA;CAClC;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC,CA4LhD"}
@@ -20,20 +20,16 @@
20
20
  * The projection reads in parallel:
21
21
  * - `products` row → product summary + tags + supplier
22
22
  * - `product_translations` → localized name + description per locale
23
- * - `product_itineraries` + `product_days` itinerary days (no
24
- * translation table today; falls back to source)
23
+ * - `product_itineraries` + `product_days` + translations localized
24
+ * itinerary days + day service labels
25
25
  * - `product_options` + `product_option_translations` → options +
26
26
  * localized labels
27
27
  * - `product_media` → hero + gallery
28
- *
29
- * Day translations don't exist in the schema yet — when
30
- * `product_day_translations` lands, this function picks them up the
31
- * same way.
32
28
  */
33
29
  import { pickBestCachedLocale } from "@voyant-travel/catalog";
34
30
  import { and, asc, eq, inArray, sql } from "drizzle-orm";
35
31
  import { productContentSchema, validateProductContent, } from "./content-shape.js";
36
- import { productDays, productItineraries, productMedia, productOptions, productOptionTranslations, products, productTranslations, } from "./schema.js";
32
+ import { productDayServices, productDayServiceTranslations, productDays, productDayTranslations, productItineraries, productMedia, productOptions, productOptionTranslations, products, productTranslations, } from "./schema.js";
37
33
  /**
38
34
  * Read the owned product + related rows and project to `ProductContent`,
39
35
  * resolving translations against the supplied locale-preference chain.
@@ -71,6 +67,30 @@ export async function buildOwnedProductContent(db, entityId, options) {
71
67
  .where(and(eq(productDays.itineraryId, defaultItinerary.id)))
72
68
  .orderBy(asc(productDays.dayNumber))
73
69
  : [];
70
+ const dayIds = days.map((d) => d.id);
71
+ const [dayTrns, dayServiceRows] = dayIds.length > 0
72
+ ? await Promise.all([
73
+ db
74
+ .select()
75
+ .from(productDayTranslations)
76
+ .where(inArray(productDayTranslations.dayId, dayIds)),
77
+ db
78
+ .select()
79
+ .from(productDayServices)
80
+ .where(inArray(productDayServices.dayId, dayIds))
81
+ .orderBy(asc(productDayServices.dayId), asc(productDayServices.sortOrder)),
82
+ ])
83
+ : [[], []];
84
+ const dayServiceIds = dayServiceRows.map((s) => s.id);
85
+ const dayServiceTrns = dayServiceIds.length > 0
86
+ ? await db
87
+ .select()
88
+ .from(productDayServiceTranslations)
89
+ .where(inArray(productDayServiceTranslations.serviceId, dayServiceIds))
90
+ : [];
91
+ const dayTranslationsByDay = groupBy(dayTrns, (row) => row.dayId);
92
+ const dayServicesByDay = groupBy(dayServiceRows, (row) => row.dayId);
93
+ const serviceTranslationsByService = groupBy(dayServiceTrns, (row) => row.serviceId);
74
94
  // Pull option translations in one round-trip for every option in this
75
95
  // product. Fan-out per-option would be wasteful when products
76
96
  // typically have a small number of options.
@@ -130,19 +150,23 @@ export async function buildOwnedProductContent(db, entityId, options) {
130
150
  inclusions: [],
131
151
  };
132
152
  }),
133
- days: days.map((d) => ({
134
- // Days don't have a translation table today; source values flow
135
- // through. When `product_day_translations` lands, slot in here
136
- // with the same pickBestCachedLocale call.
137
- day_number: d.dayNumber,
138
- title: d.title ?? null,
139
- description: d.description ?? null,
140
- location: d.location ?? null,
141
- // Per-day hero prefer the cover, fall back to the first sorted
142
- // image attached to this day in `product_media`.
143
- hero_image_url: pickDayHeroImage(mediaRows, d.id),
144
- services: [],
145
- })),
153
+ days: days.map((d) => {
154
+ const bestDayTrn = pickBestDayTranslation(dayTranslationsByDay.get(d.id) ?? [], options.preferredLocales);
155
+ const services = (dayServicesByDay.get(d.id) ?? []).map((service) => {
156
+ const bestServiceTrn = pickBestDayServiceTranslation(serviceTranslationsByService.get(service.id) ?? [], options.preferredLocales);
157
+ return bestServiceTrn?.candidate.name ?? service.name;
158
+ });
159
+ return {
160
+ day_number: d.dayNumber,
161
+ title: bestDayTrn?.candidate.title ?? d.title ?? null,
162
+ description: bestDayTrn?.candidate.description ?? d.description ?? null,
163
+ location: bestDayTrn?.candidate.location ?? d.location ?? null,
164
+ // Per-day hero — prefer the cover, fall back to the first sorted
165
+ // image attached to this day in `product_media`.
166
+ hero_image_url: pickDayHeroImage(mediaRows, d.id),
167
+ services,
168
+ };
169
+ }),
146
170
  media: mediaRows
147
171
  .filter((m) => !m.isBrochure)
148
172
  .map((m) => ({
@@ -282,6 +306,28 @@ function pickBestOptionTranslation(rows, preferred) {
282
306
  }));
283
307
  return pickBestCachedLocale(candidates, preferred);
284
308
  }
309
+ function pickBestDayTranslation(rows, preferred) {
310
+ if (rows.length === 0)
311
+ return null;
312
+ const candidates = rows.map((r) => ({
313
+ locale: r.languageTag,
314
+ title: r.title,
315
+ description: r.description,
316
+ location: r.location,
317
+ }));
318
+ return pickBestCachedLocale(candidates, preferred);
319
+ }
320
+ function pickBestDayServiceTranslation(rows, preferred) {
321
+ if (rows.length === 0)
322
+ return null;
323
+ const candidates = rows.map((r) => ({
324
+ locale: r.languageTag,
325
+ name: r.name,
326
+ description: r.description,
327
+ notes: r.notes,
328
+ }));
329
+ return pickBestCachedLocale(candidates, preferred);
330
+ }
285
331
  /**
286
332
  * When no translation rows exist for the product, the source row's
287
333
  * `name` + `description` are surfaced. We don't know what locale they
@@ -327,3 +373,17 @@ function mediaType(value) {
327
373
  return "document";
328
374
  return "image";
329
375
  }
376
+ function groupBy(rows, keyFor) {
377
+ const grouped = new Map();
378
+ for (const row of rows) {
379
+ const key = keyFor(row);
380
+ const bucket = grouped.get(key);
381
+ if (bucket) {
382
+ bucket.push(row);
383
+ }
384
+ else {
385
+ grouped.set(key, [row]);
386
+ }
387
+ }
388
+ return grouped;
389
+ }
@@ -0,0 +1,122 @@
1
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
+ import type { z } from "zod";
3
+ import type { dayServiceTranslationListQuerySchema, insertDayServiceTranslationSchema, insertProductItineraryTranslationSchema, productItineraryTranslationListQuerySchema, updateDayServiceTranslationSchema, updateProductItineraryTranslationSchema } from "./validation.js";
4
+ type ProductItineraryTranslationListQuery = z.infer<typeof productItineraryTranslationListQuerySchema>;
5
+ type CreateProductItineraryTranslationInput = z.infer<typeof insertProductItineraryTranslationSchema>;
6
+ type UpdateProductItineraryTranslationInput = z.infer<typeof updateProductItineraryTranslationSchema>;
7
+ type DayServiceTranslationListQuery = z.infer<typeof dayServiceTranslationListQuerySchema>;
8
+ type CreateDayServiceTranslationInput = z.infer<typeof insertDayServiceTranslationSchema>;
9
+ type UpdateDayServiceTranslationInput = z.infer<typeof updateDayServiceTranslationSchema>;
10
+ export declare const itineraryTranslationProductsService: {
11
+ listProductItineraryTranslations(db: PostgresJsDatabase, query: ProductItineraryTranslationListQuery): Promise<{
12
+ data: {
13
+ id: string;
14
+ itineraryId: string;
15
+ languageTag: string;
16
+ name: string;
17
+ createdAt: Date;
18
+ updatedAt: Date;
19
+ }[];
20
+ total: number;
21
+ limit: number;
22
+ offset: number;
23
+ }>;
24
+ getProductItineraryTranslationById(db: PostgresJsDatabase, id: string): Promise<{
25
+ id: string;
26
+ itineraryId: string;
27
+ languageTag: string;
28
+ name: string;
29
+ createdAt: Date;
30
+ updatedAt: Date;
31
+ } | null>;
32
+ getItineraryTranslationForProductMutation(db: PostgresJsDatabase, id: string): Promise<{
33
+ productId: string;
34
+ id: string;
35
+ itineraryId: string;
36
+ languageTag: string;
37
+ name: string;
38
+ createdAt: Date;
39
+ updatedAt: Date;
40
+ } | null>;
41
+ createProductItineraryTranslation(db: PostgresJsDatabase, productId: string, itineraryId: string, data: CreateProductItineraryTranslationInput): Promise<{
42
+ id: string;
43
+ name: string;
44
+ createdAt: Date;
45
+ updatedAt: Date;
46
+ itineraryId: string;
47
+ languageTag: string;
48
+ } | null>;
49
+ updateProductItineraryTranslation(db: PostgresJsDatabase, id: string, data: UpdateProductItineraryTranslationInput): Promise<{
50
+ id: string;
51
+ itineraryId: string;
52
+ languageTag: string;
53
+ name: string;
54
+ createdAt: Date;
55
+ updatedAt: Date;
56
+ } | null>;
57
+ deleteProductItineraryTranslation(db: PostgresJsDatabase, id: string): Promise<{
58
+ id: string;
59
+ } | null>;
60
+ listDayServiceTranslations(db: PostgresJsDatabase, query: DayServiceTranslationListQuery): Promise<{
61
+ data: {
62
+ id: string;
63
+ serviceId: string;
64
+ languageTag: string;
65
+ name: string;
66
+ description: string | null;
67
+ notes: string | null;
68
+ createdAt: Date;
69
+ updatedAt: Date;
70
+ }[];
71
+ total: number;
72
+ limit: number;
73
+ offset: number;
74
+ }>;
75
+ getDayServiceTranslationById(db: PostgresJsDatabase, id: string): Promise<{
76
+ id: string;
77
+ serviceId: string;
78
+ languageTag: string;
79
+ name: string;
80
+ description: string | null;
81
+ notes: string | null;
82
+ createdAt: Date;
83
+ updatedAt: Date;
84
+ } | null>;
85
+ getDayServiceTranslationForProductMutation(db: PostgresJsDatabase, id: string): Promise<{
86
+ dayId: string;
87
+ productId: string;
88
+ id: string;
89
+ serviceId: string;
90
+ languageTag: string;
91
+ name: string;
92
+ description: string | null;
93
+ notes: string | null;
94
+ createdAt: Date;
95
+ updatedAt: Date;
96
+ } | null>;
97
+ createDayServiceTranslation(db: PostgresJsDatabase, productId: string, dayId: string, serviceId: string, data: CreateDayServiceTranslationInput): Promise<{
98
+ id: string;
99
+ name: string;
100
+ createdAt: Date;
101
+ description: string | null;
102
+ updatedAt: Date;
103
+ languageTag: string;
104
+ notes: string | null;
105
+ serviceId: string;
106
+ } | null>;
107
+ updateDayServiceTranslation(db: PostgresJsDatabase, id: string, data: UpdateDayServiceTranslationInput): Promise<{
108
+ id: string;
109
+ serviceId: string;
110
+ languageTag: string;
111
+ name: string;
112
+ description: string | null;
113
+ notes: string | null;
114
+ createdAt: Date;
115
+ updatedAt: Date;
116
+ } | null>;
117
+ deleteDayServiceTranslation(db: PostgresJsDatabase, id: string): Promise<{
118
+ id: string;
119
+ } | null>;
120
+ };
121
+ export {};
122
+ //# sourceMappingURL=service-itinerary-translations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-itinerary-translations.d.ts","sourceRoot":"","sources":["../src/service-itinerary-translations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AACjE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAS5B,OAAO,KAAK,EACV,oCAAoC,EACpC,iCAAiC,EACjC,uCAAuC,EACvC,0CAA0C,EAC1C,iCAAiC,EACjC,uCAAuC,EACxC,MAAM,iBAAiB,CAAA;AAExB,KAAK,oCAAoC,GAAG,CAAC,CAAC,KAAK,CACjD,OAAO,0CAA0C,CAClD,CAAA;AACD,KAAK,sCAAsC,GAAG,CAAC,CAAC,KAAK,CACnD,OAAO,uCAAuC,CAC/C,CAAA;AACD,KAAK,sCAAsC,GAAG,CAAC,CAAC,KAAK,CACnD,OAAO,uCAAuC,CAC/C,CAAA;AACD,KAAK,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oCAAoC,CAAC,CAAA;AAC1F,KAAK,gCAAgC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAA;AACzF,KAAK,gCAAgC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAA;AAkCzF,eAAO,MAAM,mCAAmC;yCAExC,kBAAkB,SACf,oCAAoC;;;;;;;;;;;;;2CAuCA,kBAAkB,MAAM,MAAM;;;;;;;;kDAUvB,kBAAkB,MAAM,MAAM;;;;;;;;;0CAe5E,kBAAkB,aACX,MAAM,eACJ,MAAM,QACb,sCAAsC;;;;;;;;0CAgBxC,kBAAkB,MAClB,MAAM,QACJ,sCAAsC;;;;;;;;0CAWF,kBAAkB,MAAM,MAAM;;;mCASrC,kBAAkB,SAAS,8BAA8B;;;;;;;;;;;;;;;qCAsCvD,kBAAkB,MAAM,MAAM;;;;;;;;;;mDAUhB,kBAAkB,MAAM,MAAM;;;;;;;;;;;;oCAe7E,kBAAkB,aACX,MAAM,SACV,MAAM,aACF,MAAM,QACX,gCAAgC;;;;;;;;;;oCAgBlC,kBAAkB,MAClB,MAAM,QACJ,gCAAgC;;;;;;;;;;oCAWF,kBAAkB,MAAM,MAAM;;;CAQrE,CAAA"}
@@ -0,0 +1,176 @@
1
+ import { and, asc, eq, sql } from "drizzle-orm";
2
+ import { productDayServices, productDayServiceTranslations, productDays, productItineraries, productItineraryTranslations, } from "./schema.js";
3
+ async function getItineraryById(db, itineraryId) {
4
+ const [itinerary] = await db
5
+ .select({ id: productItineraries.id, productId: productItineraries.productId })
6
+ .from(productItineraries)
7
+ .where(eq(productItineraries.id, itineraryId))
8
+ .limit(1);
9
+ return itinerary ?? null;
10
+ }
11
+ async function getDayServiceById(db, serviceId) {
12
+ const [service] = await db
13
+ .select({
14
+ id: productDayServices.id,
15
+ dayId: productDayServices.dayId,
16
+ productId: productItineraries.productId,
17
+ })
18
+ .from(productDayServices)
19
+ .innerJoin(productDays, eq(productDayServices.dayId, productDays.id))
20
+ .innerJoin(productItineraries, eq(productDays.itineraryId, productItineraries.id))
21
+ .where(eq(productDayServices.id, serviceId))
22
+ .limit(1);
23
+ return service ?? null;
24
+ }
25
+ export const itineraryTranslationProductsService = {
26
+ async listProductItineraryTranslations(db, query) {
27
+ const conditions = [];
28
+ if (query.itineraryId) {
29
+ conditions.push(eq(productItineraryTranslations.itineraryId, query.itineraryId));
30
+ }
31
+ if (query.languageTag) {
32
+ conditions.push(eq(productItineraryTranslations.languageTag, query.languageTag));
33
+ }
34
+ const where = conditions.length > 0 ? and(...conditions) : undefined;
35
+ const [rows, countResult] = await Promise.all([
36
+ db
37
+ .select()
38
+ .from(productItineraryTranslations)
39
+ .where(where)
40
+ .limit(query.limit)
41
+ .offset(query.offset)
42
+ .orderBy(asc(productItineraryTranslations.languageTag), asc(productItineraryTranslations.createdAt)),
43
+ db
44
+ .select({ count: sql `count(*)::int` })
45
+ .from(productItineraryTranslations)
46
+ .where(where),
47
+ ]);
48
+ return {
49
+ data: rows,
50
+ total: countResult[0]?.count ?? 0,
51
+ limit: query.limit,
52
+ offset: query.offset,
53
+ };
54
+ },
55
+ async getProductItineraryTranslationById(db, id) {
56
+ const [row] = await db
57
+ .select()
58
+ .from(productItineraryTranslations)
59
+ .where(eq(productItineraryTranslations.id, id))
60
+ .limit(1);
61
+ return row ?? null;
62
+ },
63
+ async getItineraryTranslationForProductMutation(db, id) {
64
+ const [row] = await db
65
+ .select()
66
+ .from(productItineraryTranslations)
67
+ .where(eq(productItineraryTranslations.id, id))
68
+ .limit(1);
69
+ if (!row) {
70
+ return null;
71
+ }
72
+ const itineraryRef = await getItineraryById(db, row.itineraryId);
73
+ return itineraryRef ? { ...row, productId: itineraryRef.productId } : null;
74
+ },
75
+ async createProductItineraryTranslation(db, productId, itineraryId, data) {
76
+ const itineraryRef = await getItineraryById(db, itineraryId);
77
+ if (!itineraryRef || itineraryRef.productId !== productId) {
78
+ return null;
79
+ }
80
+ const [row] = await db
81
+ .insert(productItineraryTranslations)
82
+ .values({ ...data, itineraryId })
83
+ .returning();
84
+ return row ?? null;
85
+ },
86
+ async updateProductItineraryTranslation(db, id, data) {
87
+ const [row] = await db
88
+ .update(productItineraryTranslations)
89
+ .set({ ...data, updatedAt: new Date() })
90
+ .where(eq(productItineraryTranslations.id, id))
91
+ .returning();
92
+ return row ?? null;
93
+ },
94
+ async deleteProductItineraryTranslation(db, id) {
95
+ const [row] = await db
96
+ .delete(productItineraryTranslations)
97
+ .where(eq(productItineraryTranslations.id, id))
98
+ .returning({ id: productItineraryTranslations.id });
99
+ return row ?? null;
100
+ },
101
+ async listDayServiceTranslations(db, query) {
102
+ const conditions = [];
103
+ if (query.serviceId) {
104
+ conditions.push(eq(productDayServiceTranslations.serviceId, query.serviceId));
105
+ }
106
+ if (query.languageTag) {
107
+ conditions.push(eq(productDayServiceTranslations.languageTag, query.languageTag));
108
+ }
109
+ const where = conditions.length > 0 ? and(...conditions) : undefined;
110
+ const [rows, countResult] = await Promise.all([
111
+ db
112
+ .select()
113
+ .from(productDayServiceTranslations)
114
+ .where(where)
115
+ .limit(query.limit)
116
+ .offset(query.offset)
117
+ .orderBy(asc(productDayServiceTranslations.languageTag), asc(productDayServiceTranslations.createdAt)),
118
+ db
119
+ .select({ count: sql `count(*)::int` })
120
+ .from(productDayServiceTranslations)
121
+ .where(where),
122
+ ]);
123
+ return {
124
+ data: rows,
125
+ total: countResult[0]?.count ?? 0,
126
+ limit: query.limit,
127
+ offset: query.offset,
128
+ };
129
+ },
130
+ async getDayServiceTranslationById(db, id) {
131
+ const [row] = await db
132
+ .select()
133
+ .from(productDayServiceTranslations)
134
+ .where(eq(productDayServiceTranslations.id, id))
135
+ .limit(1);
136
+ return row ?? null;
137
+ },
138
+ async getDayServiceTranslationForProductMutation(db, id) {
139
+ const [row] = await db
140
+ .select()
141
+ .from(productDayServiceTranslations)
142
+ .where(eq(productDayServiceTranslations.id, id))
143
+ .limit(1);
144
+ if (!row) {
145
+ return null;
146
+ }
147
+ const serviceRef = await getDayServiceById(db, row.serviceId);
148
+ return serviceRef ? { ...row, dayId: serviceRef.dayId, productId: serviceRef.productId } : null;
149
+ },
150
+ async createDayServiceTranslation(db, productId, dayId, serviceId, data) {
151
+ const serviceRef = await getDayServiceById(db, serviceId);
152
+ if (!serviceRef || serviceRef.productId !== productId || serviceRef.dayId !== dayId) {
153
+ return null;
154
+ }
155
+ const [row] = await db
156
+ .insert(productDayServiceTranslations)
157
+ .values({ ...data, serviceId })
158
+ .returning();
159
+ return row ?? null;
160
+ },
161
+ async updateDayServiceTranslation(db, id, data) {
162
+ const [row] = await db
163
+ .update(productDayServiceTranslations)
164
+ .set({ ...data, updatedAt: new Date() })
165
+ .where(eq(productDayServiceTranslations.id, id))
166
+ .returning();
167
+ return row ?? null;
168
+ },
169
+ async deleteDayServiceTranslation(db, id) {
170
+ const [row] = await db
171
+ .delete(productDayServiceTranslations)
172
+ .where(eq(productDayServiceTranslations.id, id))
173
+ .returning({ id: productDayServiceTranslations.id });
174
+ return row ?? null;
175
+ },
176
+ };
@@ -127,10 +127,10 @@ export declare const optionTranslationProductsService: {
127
127
  createdAt: Date;
128
128
  description: string | null;
129
129
  updatedAt: Date;
130
+ languageTag: string;
130
131
  title: string | null;
131
132
  location: string | null;
132
133
  dayId: string;
133
- languageTag: string;
134
134
  } | null>;
135
135
  updateProductDayTranslation(db: PostgresJsDatabase, id: string, data: UpdateProductDayTranslationInput): Promise<{
136
136
  id: string;