@flowselections/floriday-verkoop-module 1.0.20 → 1.0.24

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.
Files changed (89) hide show
  1. package/dist-lib/components/karindeling/AfmetingenTab.d.ts +6 -0
  2. package/dist-lib/components/karindeling/AfmetingenTab.d.ts.map +1 -0
  3. package/dist-lib/components/karindeling/AfmetingenTab.js +128 -0
  4. package/dist-lib/components/karindeling/KarindelingPage.d.ts +2 -0
  5. package/dist-lib/components/karindeling/KarindelingPage.d.ts.map +1 -0
  6. package/dist-lib/components/karindeling/KarindelingPage.js +102 -0
  7. package/dist-lib/components/verkoop/OrderControlStatusCard.d.ts +6 -0
  8. package/dist-lib/components/verkoop/OrderControlStatusCard.d.ts.map +1 -0
  9. package/dist-lib/components/verkoop/OrderControlStatusCard.js +41 -0
  10. package/dist-lib/components/verkoop/OrderKarindelingCard.d.ts.map +1 -1
  11. package/dist-lib/components/verkoop/OrderKarindelingCard.js +83 -37
  12. package/dist-lib/components/verkoop/SalesOrderDetailPage.d.ts.map +1 -1
  13. package/dist-lib/components/verkoop/SalesOrderDetailPage.js +74 -8
  14. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts +2 -1
  15. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
  16. package/dist-lib/components/verkoop/SalesOrderDetailView.js +3 -2
  17. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
  18. package/dist-lib/components/verkoop/SalesOrdersTable.js +40 -5
  19. package/dist-lib/components/verkoop/VerkoopOrdersPage.d.ts.map +1 -1
  20. package/dist-lib/components/verkoop/VerkoopOrdersPage.js +19 -3
  21. package/dist-lib/index.d.ts +1 -0
  22. package/dist-lib/index.d.ts.map +1 -1
  23. package/dist-lib/index.js +12 -1
  24. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1536 -581
  25. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  26. package/dist-lib/integrations/supabase/client.d.ts +1536 -581
  27. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  28. package/dist-lib/integrations/supabase/client.js +24 -9
  29. package/dist-lib/integrations/supabase/client.server.d.ts +1536 -581
  30. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  31. package/dist-lib/integrations/supabase/types.d.ts +1543 -562
  32. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  33. package/dist-lib/integrations/supabase/types.js +3 -9
  34. package/dist-lib/lib/cart-lock.functions.d.ts +1536 -581
  35. package/dist-lib/lib/cart-lock.functions.d.ts.map +1 -1
  36. package/dist-lib/lib/cart-planner.d.ts +9 -1
  37. package/dist-lib/lib/cart-planner.d.ts.map +1 -1
  38. package/dist-lib/lib/cart-planner.js +21 -4
  39. package/dist-lib/lib/cart-planning-settings.functions.d.ts +140842 -0
  40. package/dist-lib/lib/cart-planning-settings.functions.d.ts.map +1 -0
  41. package/dist-lib/lib/cart-planning-settings.functions.js +375 -0
  42. package/dist-lib/lib/cart-replan.functions.d.ts +15811 -1082
  43. package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
  44. package/dist-lib/lib/cart-replan.functions.js +85 -27
  45. package/dist-lib/lib/create-order.functions.d.ts +19021 -7561
  46. package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
  47. package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts +4558 -1693
  48. package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts.map +1 -1
  49. package/dist-lib/lib/floriday-auto-fulfillment.server.js +1 -1
  50. package/dist-lib/lib/floriday-central-token.server.d.ts +12 -0
  51. package/dist-lib/lib/floriday-central-token.server.d.ts.map +1 -0
  52. package/dist-lib/lib/floriday-central-token.server.js +58 -0
  53. package/dist-lib/lib/floriday-gateway.server.d.ts +2 -2
  54. package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -1
  55. package/dist-lib/lib/floriday-gateway.server.js +10 -37
  56. package/dist-lib/lib/floriday-logistic-labels.functions.d.ts +1536 -581
  57. package/dist-lib/lib/floriday-logistic-labels.functions.d.ts.map +1 -1
  58. package/dist-lib/lib/floriday-logistic-labels.functions.js +14 -4
  59. package/dist-lib/lib/floriday-mirror.functions.d.ts +12806 -0
  60. package/dist-lib/lib/floriday-mirror.functions.d.ts.map +1 -0
  61. package/dist-lib/lib/floriday-mirror.functions.js +278 -0
  62. package/dist-lib/lib/floriday-public.functions.d.ts +10989 -4301
  63. package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
  64. package/dist-lib/lib/floriday-public.functions.js +114 -38
  65. package/dist-lib/lib/floriday-sync.d.ts +0 -1
  66. package/dist-lib/lib/floriday-sync.d.ts.map +1 -1
  67. package/dist-lib/lib/floriday-sync.js +1 -1
  68. package/dist-lib/lib/floriday-writes.functions.d.ts +7772 -2997
  69. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  70. package/dist-lib/lib/order-buffers.functions.d.ts +38420 -0
  71. package/dist-lib/lib/order-buffers.functions.d.ts.map +1 -0
  72. package/dist-lib/lib/order-buffers.functions.js +244 -0
  73. package/dist-lib/lib/order-shipments.functions.d.ts +6165 -2345
  74. package/dist-lib/lib/order-shipments.functions.d.ts.map +1 -1
  75. package/dist-lib/lib/salesorders-mapping.d.ts +5 -0
  76. package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
  77. package/dist-lib/lib/salesorders-mapping.js +8 -1
  78. package/dist-lib/lib/salesorders.functions.d.ts +30443 -1979
  79. package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
  80. package/dist-lib/lib/salesorders.functions.js +307 -0
  81. package/dist-lib/lib/tradeitems.functions.d.ts +1536 -581
  82. package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
  83. package/dist-lib/lib/useSalesOrderDetailQuery.d.ts +2 -0
  84. package/dist-lib/lib/useSalesOrderDetailQuery.d.ts.map +1 -0
  85. package/dist-lib/lib/useSalesOrderDetailQuery.js +50 -0
  86. package/dist-lib/lib/useSalesOrdersQuery.d.ts +2 -2
  87. package/dist-lib/lib/useSalesOrdersQuery.d.ts.map +1 -1
  88. package/dist-lib/lib/useSalesOrdersQuery.js +139 -10
  89. package/package.json +5 -5
@@ -0,0 +1,375 @@
1
+ // Server-fns voor de Karindeling-instellingen module.
2
+ import { createServerFn } from "@tanstack/react-start";
3
+ import { z } from "zod";
4
+ import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
5
+ export const getCartPlanningBundle = createServerFn({ method: "GET" })
6
+ .middleware([requireSupabaseAuth])
7
+ .handler(async ({ context }) => {
8
+ const supabase = context.supabase;
9
+ const [{ data: s }, { data: pt }, { data: ct }, { data: cr }] = await Promise.all([
10
+ supabase.from("cart_planning_settings").select("*").eq("id", 1).maybeSingle(),
11
+ supabase.from("cart_planning_pick_times").select("*").order("pot_size"),
12
+ supabase.from("cart_types").select("*").order("name"),
13
+ supabase.from("crate_types").select("*").order("name"),
14
+ ]);
15
+ return {
16
+ settings: (s ?? {
17
+ id: 1,
18
+ max_party_share_per_layer_pct: 50,
19
+ max_beladingen_per_layer: 2,
20
+ optimization_strategy: "min_carts",
21
+ allow_editing_after_pickstation_send: false,
22
+ combine_floriday_orders: false,
23
+ combine_floriday_orders_window: "same_day",
24
+ combine_orders_scope: "same_customer",
25
+ own_cart_from_layers: 3,
26
+ max_customers_per_cart: 5,
27
+ max_one_customer_per_layer: false,
28
+ require_same_carrier: false,
29
+ require_same_delivery_location: true,
30
+ require_same_crate_type: false,
31
+ require_same_delivery_date: true,
32
+ default_workers: 1,
33
+ pick_time_mode: "per_pot_size",
34
+ plants_per_hour: 0,
35
+ plants_per_day: 0,
36
+ buffer_grouping_mode: "same_pickup_and_location",
37
+ }),
38
+ pickTimes: (pt ?? []),
39
+ cartTypes: (ct ?? []),
40
+ crateTypes: (cr ?? []),
41
+ };
42
+ });
43
+ const settingsSchema = z.object({
44
+ max_party_share_per_layer_pct: z.number().int().min(1).max(100),
45
+ max_beladingen_per_layer: z.number().int().min(1).max(999),
46
+ optimization_strategy: z.enum(["min_carts", "min_mixing"]),
47
+ allow_editing_after_pickstation_send: z.boolean(),
48
+ combine_floriday_orders: z.boolean(),
49
+ combine_floriday_orders_window: z.enum(["same_day", "same_delivery_slot", "same_week"]),
50
+ combine_orders_scope: z.enum(["same_customer", "same_delivery_location", "never"]),
51
+ own_cart_from_layers: z.number().int().min(1).max(100),
52
+ max_customers_per_cart: z.number().int().min(1).max(100),
53
+ max_one_customer_per_layer: z.boolean(),
54
+ require_same_carrier: z.boolean(),
55
+ require_same_delivery_location: z.boolean(),
56
+ require_same_crate_type: z.boolean(),
57
+ require_same_delivery_date: z.boolean(),
58
+ default_workers: z.number().int().min(1).max(1000),
59
+ pick_time_mode: z.enum(["per_pot_size", "plants_per_hour", "plants_per_day"]),
60
+ plants_per_hour: z.number().int().min(0).max(1000000),
61
+ plants_per_day: z.number().int().min(0).max(10000000),
62
+ buffer_grouping_mode: z.enum([
63
+ "same_pickup_and_location",
64
+ "same_pickup",
65
+ "same_location",
66
+ "same_customer",
67
+ "never",
68
+ ]),
69
+ });
70
+ export const updateCartPlanningSettings = createServerFn({ method: "POST" })
71
+ .middleware([requireSupabaseAuth])
72
+ .inputValidator((input) => settingsSchema.parse(input))
73
+ .handler(async ({ data, context }) => {
74
+ const supabase = context.supabase;
75
+ const { error } = await supabase
76
+ .from("cart_planning_settings")
77
+ .update({ ...data, updated_by: context.userId })
78
+ .eq("id", 1);
79
+ if (error)
80
+ throw new Error(error.message);
81
+ return { ok: true };
82
+ });
83
+ export const upsertPickTime = createServerFn({ method: "POST" })
84
+ .middleware([requireSupabaseAuth])
85
+ .inputValidator((input) => z
86
+ .object({
87
+ id: z.string().uuid().optional(),
88
+ pot_size: z.string().min(1).max(50),
89
+ seconds_per_cart: z.number().int().min(0).max(100000),
90
+ })
91
+ .parse(input))
92
+ .handler(async ({ data, context }) => {
93
+ const supabase = context.supabase;
94
+ const { error } = await supabase
95
+ .from("cart_planning_pick_times")
96
+ .upsert({ id: data.id, pot_size: data.pot_size, seconds_per_cart: data.seconds_per_cart }, { onConflict: "pot_size" });
97
+ if (error)
98
+ throw new Error(error.message);
99
+ return { ok: true };
100
+ });
101
+ export const deletePickTime = createServerFn({ method: "POST" })
102
+ .middleware([requireSupabaseAuth])
103
+ .inputValidator((input) => z.object({ id: z.string().uuid() }).parse(input))
104
+ .handler(async ({ data, context }) => {
105
+ const supabase = context.supabase;
106
+ const { error } = await supabase.from("cart_planning_pick_times").delete().eq("id", data.id);
107
+ if (error)
108
+ throw new Error(error.message);
109
+ return { ok: true };
110
+ });
111
+ export const listUsedCratePackages = createServerFn({ method: "GET" })
112
+ .middleware([requireSupabaseAuth])
113
+ .handler(async ({ context }) => {
114
+ const supabase = context.supabase;
115
+ const [{ data: tradeItems, error: e1 }, { data: carriers, error: e2 }] = await Promise.all([
116
+ supabase.from("floriday_trade_items_cache").select("data").limit(5000),
117
+ supabase.from("floricode_records").select("data").eq("resource", "LoadCarrier"),
118
+ ]);
119
+ if (e1)
120
+ throw new Error(e1.message);
121
+ if (e2)
122
+ throw new Error(e2.message);
123
+ const carrierByCode = new Map();
124
+ const mm2cm = (v) => (v == null ? null : Math.round(Number(v) / 10));
125
+ for (const row of (carriers ?? [])) {
126
+ const d = row.data;
127
+ if (!d?.code)
128
+ continue;
129
+ carrierByCode.set(String(d.code), {
130
+ description: d.description ?? null,
131
+ length_cm: mm2cm(d.length),
132
+ width_cm: mm2cm(d.width),
133
+ height_cm: mm2cm(d.height),
134
+ });
135
+ }
136
+ const usage = new Map();
137
+ for (const row of (tradeItems ?? [])) {
138
+ const cfgs = row?.data?.packingConfigurations;
139
+ if (!Array.isArray(cfgs))
140
+ continue;
141
+ for (const c of cfgs) {
142
+ const code = c?.package?.vbnPackageCode ?? c?.package?.customPackageId;
143
+ const pieces = Number(c?.piecesPerPackage ?? 0);
144
+ if (!code)
145
+ continue;
146
+ const key = `${code}-${pieces}`;
147
+ const e = usage.get(key);
148
+ if (e)
149
+ e.count += 1;
150
+ else
151
+ usage.set(key, { code: String(code), pieces, count: 1 });
152
+ }
153
+ }
154
+ const seenCodes = new Set();
155
+ const result = [];
156
+ for (const [code, meta] of carrierByCode.entries()) {
157
+ const variants = Array.from(usage.values()).filter((u) => u.code === code);
158
+ const total = variants.reduce((s, v) => s + v.count, 0);
159
+ const bestPieces = variants.sort((a, b) => b.count - a.count)[0]?.pieces || null;
160
+ const dims = meta.length_cm && meta.width_cm ? `${meta.length_cm}×${meta.width_cm} cm` : null;
161
+ const parts = [
162
+ `VBN ${code}`,
163
+ meta.description,
164
+ dims,
165
+ bestPieces ? `${bestPieces} st/fust` : null,
166
+ ].filter(Boolean);
167
+ result.push({
168
+ key: `${code}-${bestPieces ?? 0}`,
169
+ vbn_package_code: code,
170
+ description: meta.description,
171
+ length_cm: meta.length_cm,
172
+ width_cm: meta.width_cm,
173
+ height_cm: meta.height_cm,
174
+ pieces_per_package: bestPieces,
175
+ usage_count: total,
176
+ label: parts.join(" · "),
177
+ });
178
+ seenCodes.add(code);
179
+ }
180
+ for (const v of usage.values()) {
181
+ if (seenCodes.has(v.code))
182
+ continue;
183
+ result.push({
184
+ key: `${v.code}-${v.pieces}`,
185
+ vbn_package_code: v.code,
186
+ description: null,
187
+ length_cm: null,
188
+ width_cm: null,
189
+ height_cm: null,
190
+ pieces_per_package: v.pieces || null,
191
+ usage_count: v.count,
192
+ label: `VBN ${v.code}${v.pieces ? ` · ${v.pieces} st/fust` : ""}`,
193
+ });
194
+ }
195
+ return result.sort((a, b) => {
196
+ if (b.usage_count !== a.usage_count)
197
+ return b.usage_count - a.usage_count;
198
+ return a.vbn_package_code.localeCompare(b.vbn_package_code);
199
+ });
200
+ });
201
+ // Standaard afmetingen voor bekende Floriday load-carrier types (cm).
202
+ const CARRIER_METADATA = {
203
+ DANISH_TROLLEY: { name: "Deense container", length: 135, width: 56, layers: 4 },
204
+ DANISH_TROLLEY_NON_CC_RFID: { name: "Deense container (non-CC RFID)", length: 135, width: 56, layers: 4 },
205
+ DANISH_TROLLEY_WATER_TRAY: { name: "Deense container met waterschaal", length: 135, width: 56, layers: 4 },
206
+ ONE_THIRD_DANISH_TROLLEY: { name: "1/3 Deense container", length: 45, width: 56, layers: 4 },
207
+ AUCTION_TROLLEY: { name: "Veilingstapelwagen", length: 125, width: 56, layers: 5 },
208
+ EURO_TROLLEY: { name: "Euro-stapelwagen", length: 135, width: 56, layers: 4 },
209
+ EURO_PALLET: { name: "Europallet", length: 120, width: 80, layers: 1 },
210
+ HT_EURO_PALLET: { name: "HT Europallet", length: 120, width: 80, layers: 1 },
211
+ PALLET: { name: "Pallet", length: 120, width: 100, layers: 1 },
212
+ BLOCK_PALLET: { name: "Blokpallet", length: 120, width: 100, layers: 1 },
213
+ CHEP_PALLET_BLUE: { name: "CHEP-pallet (blauw)", length: 120, width: 100, layers: 1 },
214
+ CHEP_PALLET_RED: { name: "CHEP-pallet (rood)", length: 120, width: 80, layers: 1 },
215
+ IFCO_PALLET: { name: "IFCO-pallet", length: 120, width: 80, layers: 1 },
216
+ WATERING_CONTAINER: { name: "Watergeef-container", length: 120, width: 80, layers: 1 },
217
+ NONE: { name: "Geen", length: null, width: null, layers: null },
218
+ };
219
+ export const listUsedCartCarriers = createServerFn({ method: "GET" })
220
+ .middleware([requireSupabaseAuth])
221
+ .handler(async ({ context }) => {
222
+ const supabase = context.supabase;
223
+ const [{ data: tradeItems, error: e1 }, { data: codeList, error: e2 }] = await Promise.all([
224
+ supabase.from("floriday_trade_items_cache").select("data").limit(5000),
225
+ supabase.from("floriday_code_lists_cache").select("code").eq("list_name", "LoadCarrierType"),
226
+ ]);
227
+ if (e1)
228
+ throw new Error(e1.message);
229
+ if (e2)
230
+ throw new Error(e2.message);
231
+ // Tel gebruik + verzamel meest voorkomende layers per carrier.
232
+ const usage = new Map();
233
+ for (const row of (tradeItems ?? [])) {
234
+ const cfgs = row?.data?.packingConfigurations;
235
+ if (!Array.isArray(cfgs))
236
+ continue;
237
+ for (const c of cfgs) {
238
+ const code = c?.loadCarrier;
239
+ if (!code)
240
+ continue;
241
+ const layers = Number(c?.layersPerLoadCarrier ?? 0);
242
+ const entry = usage.get(code) ?? { count: 0, layersHist: new Map() };
243
+ entry.count += 1;
244
+ if (layers > 0)
245
+ entry.layersHist.set(layers, (entry.layersHist.get(layers) ?? 0) + 1);
246
+ usage.set(code, entry);
247
+ }
248
+ }
249
+ // Verzamel alle bekende codes (uit code-list + gebruik).
250
+ const allCodes = new Set([
251
+ ...Object.keys(CARRIER_METADATA),
252
+ ...(codeList ?? []).map((r) => r.code),
253
+ ...usage.keys(),
254
+ ]);
255
+ const result = [];
256
+ for (const code of allCodes) {
257
+ const meta = CARRIER_METADATA[code] ?? { name: code, length: null, width: null, layers: null };
258
+ const u = usage.get(code);
259
+ const mostLayers = u
260
+ ? Array.from(u.layersHist.entries()).sort((a, b) => b[1] - a[1])[0]?.[0] ?? meta.layers
261
+ : meta.layers;
262
+ const dims = meta.length && meta.width ? `${meta.length}×${meta.width} cm` : null;
263
+ const parts = [meta.name, dims, mostLayers ? `${mostLayers} lagen` : null].filter(Boolean);
264
+ result.push({
265
+ key: code,
266
+ code,
267
+ name: meta.name,
268
+ layer_length_cm: meta.length,
269
+ layer_width_cm: meta.width,
270
+ layers_per_cart: mostLayers ?? null,
271
+ usage_count: u?.count ?? 0,
272
+ label: parts.join(" · "),
273
+ });
274
+ }
275
+ return result.sort((a, b) => {
276
+ if (b.usage_count !== a.usage_count)
277
+ return b.usage_count - a.usage_count;
278
+ return a.name.localeCompare(b.name);
279
+ });
280
+ });
281
+ // ─── Cart-types (soorten karren / containers) ────────────────────────────────
282
+ const cartTypeSchema = z.object({
283
+ id: z.string().uuid().optional(),
284
+ name: z.string().min(1).max(100),
285
+ layer_length_cm: z.number().int().min(1).max(10000),
286
+ layer_width_cm: z.number().int().min(1).max(10000),
287
+ layers_per_cart: z.number().int().min(1).max(20),
288
+ is_default: z.boolean().optional(),
289
+ });
290
+ export const upsertCartType = createServerFn({ method: "POST" })
291
+ .middleware([requireSupabaseAuth])
292
+ .inputValidator((input) => cartTypeSchema.parse(input))
293
+ .handler(async ({ data, context }) => {
294
+ const supabase = context.supabase;
295
+ const row = {
296
+ name: data.name,
297
+ layer_length_cm: data.layer_length_cm,
298
+ layer_width_cm: data.layer_width_cm,
299
+ layers_per_cart: data.layers_per_cart,
300
+ };
301
+ if (data.id)
302
+ row.id = data.id;
303
+ if (data.is_default !== undefined)
304
+ row.is_default = data.is_default;
305
+ const { error } = await supabase.from("cart_types").upsert(row, { onConflict: "id" });
306
+ if (error)
307
+ throw new Error(error.message);
308
+ return { ok: true };
309
+ });
310
+ export const deleteCartType = createServerFn({ method: "POST" })
311
+ .middleware([requireSupabaseAuth])
312
+ .inputValidator((input) => z.object({ id: z.string().uuid() }).parse(input))
313
+ .handler(async ({ data, context }) => {
314
+ const supabase = context.supabase;
315
+ const { error } = await supabase.from("cart_types").delete().eq("id", data.id);
316
+ if (error)
317
+ throw new Error(error.message);
318
+ return { ok: true };
319
+ });
320
+ export const setDefaultCartType = createServerFn({ method: "POST" })
321
+ .middleware([requireSupabaseAuth])
322
+ .inputValidator((input) => z.object({ id: z.string().uuid() }).parse(input))
323
+ .handler(async ({ data, context }) => {
324
+ const supabase = context.supabase;
325
+ // Unique partial index (is_default) enforces at most one default; unset others first.
326
+ const { error: e1 } = await supabase
327
+ .from("cart_types")
328
+ .update({ is_default: false })
329
+ .neq("id", data.id);
330
+ if (e1)
331
+ throw new Error(e1.message);
332
+ const { error: e2 } = await supabase
333
+ .from("cart_types")
334
+ .update({ is_default: true })
335
+ .eq("id", data.id);
336
+ if (e2)
337
+ throw new Error(e2.message);
338
+ return { ok: true };
339
+ });
340
+ // ─── Crate-types (soorten fusten) ────────────────────────────────────────────
341
+ const crateTypeSchema = z.object({
342
+ id: z.string().uuid().optional(),
343
+ name: z.string().min(1).max(100),
344
+ length_cm: z.number().int().min(1).max(10000),
345
+ width_cm: z.number().int().min(1).max(10000),
346
+ plants_per_crate: z.number().int().min(1).max(10000),
347
+ });
348
+ export const upsertCrateType = createServerFn({ method: "POST" })
349
+ .middleware([requireSupabaseAuth])
350
+ .inputValidator((input) => crateTypeSchema.parse(input))
351
+ .handler(async ({ data, context }) => {
352
+ const supabase = context.supabase;
353
+ const row = {
354
+ name: data.name,
355
+ length_cm: data.length_cm,
356
+ width_cm: data.width_cm,
357
+ plants_per_crate: data.plants_per_crate,
358
+ };
359
+ if (data.id)
360
+ row.id = data.id;
361
+ const { error } = await supabase.from("crate_types").upsert(row, { onConflict: "id" });
362
+ if (error)
363
+ throw new Error(error.message);
364
+ return { ok: true };
365
+ });
366
+ export const deleteCrateType = createServerFn({ method: "POST" })
367
+ .middleware([requireSupabaseAuth])
368
+ .inputValidator((input) => z.object({ id: z.string().uuid() }).parse(input))
369
+ .handler(async ({ data, context }) => {
370
+ const supabase = context.supabase;
371
+ const { error } = await supabase.from("crate_types").delete().eq("id", data.id);
372
+ if (error)
373
+ throw new Error(error.message);
374
+ return { ok: true };
375
+ });