includio-cms 0.28.0 → 0.34.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.
Files changed (117) hide show
  1. package/API.md +39 -13
  2. package/CHANGELOG.md +19 -0
  3. package/DOCS.md +1 -1
  4. package/ROADMAP.md +1 -0
  5. package/dist/admin/api/handler.js +4 -0
  6. package/dist/admin/api/integrations.d.ts +13 -0
  7. package/dist/admin/api/integrations.js +61 -0
  8. package/dist/admin/api/test-email.d.ts +9 -0
  9. package/dist/admin/api/test-email.js +39 -0
  10. package/dist/admin/auth-client.d.ts +2209 -2209
  11. package/dist/admin/client/index.d.ts +10 -0
  12. package/dist/admin/client/index.js +12 -0
  13. package/dist/admin/client/maintenance/maintenance-page.svelte +210 -0
  14. package/dist/admin/client/shop/coupon-schema.d.ts +1 -1
  15. package/dist/admin/client/shop/restore-order-cell.svelte +29 -0
  16. package/dist/admin/client/shop/restore-order-cell.svelte.d.ts +8 -0
  17. package/dist/admin/client/shop/shop-order-detail-page.svelte +71 -1
  18. package/dist/admin/client/shop/shop-orders-list-page.svelte +113 -53
  19. package/dist/admin/components/layout/app-sidebar.svelte +2 -0
  20. package/dist/admin/components/layout/nav-custom.svelte +26 -0
  21. package/dist/admin/components/layout/nav-custom.svelte.d.ts +3 -0
  22. package/dist/admin/components/layout/page-header.svelte +13 -3
  23. package/dist/admin/components/layout/page-header.svelte.d.ts +13 -3
  24. package/dist/admin/remote/admin.remote.d.ts +7 -0
  25. package/dist/admin/remote/admin.remote.js +10 -0
  26. package/dist/admin/remote/entry.remote.d.ts +4 -4
  27. package/dist/admin/remote/index.d.ts +1 -0
  28. package/dist/admin/remote/index.js +1 -0
  29. package/dist/admin/remote/invite.d.ts +2 -2
  30. package/dist/admin/remote/shop.remote.d.ts +75 -48
  31. package/dist/admin/remote/shop.remote.js +41 -10
  32. package/dist/admin/types.d.ts +15 -0
  33. package/dist/admin/utils/csv-export.d.ts +45 -0
  34. package/dist/admin/utils/csv-export.js +61 -0
  35. package/dist/cli/scaffold/admin.js +1 -1
  36. package/dist/components/ui/button-group/button-group-separator.svelte.d.ts +1 -1
  37. package/dist/components/ui/command/command.svelte.d.ts +1 -1
  38. package/dist/components/ui/field/field-label.svelte.d.ts +1 -1
  39. package/dist/components/ui/input/input.svelte.d.ts +1 -1
  40. package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
  41. package/dist/components/ui/item/item-separator.svelte.d.ts +1 -1
  42. package/dist/components/ui/select/select-group-heading.svelte.d.ts +1 -1
  43. package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
  44. package/dist/components/ui/sidebar/sidebar-separator.svelte.d.ts +1 -1
  45. package/dist/core/cms.d.ts +44 -2
  46. package/dist/core/cms.js +64 -0
  47. package/dist/core/index.d.ts +1 -4
  48. package/dist/core/index.js +4 -4
  49. package/dist/core/server/index.d.ts +4 -1
  50. package/dist/core/server/index.js +4 -1
  51. package/dist/db-postgres/schema/shop/order.d.ts +34 -0
  52. package/dist/db-postgres/schema/shop/order.js +4 -0
  53. package/dist/paraglide/messages/_index.d.ts +3 -36
  54. package/dist/paraglide/messages/_index.js +3 -71
  55. package/dist/paraglide/messages/hello_world.d.ts +5 -0
  56. package/dist/paraglide/messages/hello_world.js +33 -0
  57. package/dist/paraglide/messages/login_hello.d.ts +16 -0
  58. package/dist/paraglide/messages/login_hello.js +34 -0
  59. package/dist/paraglide/messages/login_please_login.d.ts +16 -0
  60. package/dist/paraglide/messages/login_please_login.js +34 -0
  61. package/dist/shop/adapters/fakturownia/client.d.ts +5 -0
  62. package/dist/shop/adapters/fakturownia/client.js +20 -0
  63. package/dist/shop/adapters/fakturownia/index.js +11 -0
  64. package/dist/shop/adapters/payu/index.js +11 -0
  65. package/dist/shop/index.d.ts +1 -1
  66. package/dist/shop/server/coupons.d.ts +10 -0
  67. package/dist/shop/server/coupons.js +19 -0
  68. package/dist/shop/server/email.d.ts +7 -3
  69. package/dist/shop/server/email.js +86 -112
  70. package/dist/shop/server/emailTemplateRegistry.d.ts +47 -0
  71. package/dist/shop/server/emailTemplateRegistry.js +288 -0
  72. package/dist/shop/server/orders.d.ts +60 -1
  73. package/dist/shop/server/orders.js +145 -16
  74. package/dist/shop/templates/_partials/footer.en.html +4 -0
  75. package/dist/shop/templates/_partials/footer.pl.html +4 -0
  76. package/dist/shop/templates/_partials/header.en.html +4 -0
  77. package/dist/shop/templates/_partials/header.pl.html +4 -0
  78. package/dist/shop/templates/_partials/items.en.html +14 -0
  79. package/dist/shop/templates/_partials/items.pl.html +14 -0
  80. package/dist/shop/templates/_partials/tracking.en.html +7 -0
  81. package/dist/shop/templates/_partials/tracking.pl.html +7 -0
  82. package/dist/shop/templates/awaiting-payment.en.html +6 -0
  83. package/dist/shop/templates/awaiting-payment.pl.html +6 -0
  84. package/dist/shop/templates/cancelled.en.html +6 -0
  85. package/dist/shop/templates/cancelled.pl.html +6 -0
  86. package/dist/shop/templates/low-stock.en.html +14 -0
  87. package/dist/shop/templates/low-stock.pl.html +14 -0
  88. package/dist/shop/templates/order-completed.en.html +6 -0
  89. package/dist/shop/templates/order-completed.pl.html +6 -0
  90. package/dist/shop/templates/order-received.en.html +7 -0
  91. package/dist/shop/templates/order-received.pl.html +7 -0
  92. package/dist/shop/templates/payment-received.en.html +7 -0
  93. package/dist/shop/templates/payment-received.pl.html +7 -0
  94. package/dist/shop/templates/payment-rejected.en.html +6 -0
  95. package/dist/shop/templates/payment-rejected.pl.html +6 -0
  96. package/dist/shop/templates/preparing.en.html +7 -0
  97. package/dist/shop/templates/preparing.pl.html +7 -0
  98. package/dist/shop/templates/refunded.en.html +6 -0
  99. package/dist/shop/templates/refunded.pl.html +6 -0
  100. package/dist/shop/templates/shipped.en.html +7 -0
  101. package/dist/shop/templates/shipped.pl.html +7 -0
  102. package/dist/shop/types.d.ts +63 -0
  103. package/dist/sveltekit/index.d.ts +0 -1
  104. package/dist/sveltekit/index.js +0 -1
  105. package/dist/sveltekit/server/index.d.ts +2 -0
  106. package/dist/sveltekit/server/index.js +4 -0
  107. package/dist/types/adapters/email.d.ts +13 -0
  108. package/dist/types/cms.d.ts +30 -0
  109. package/dist/types/index.d.ts +1 -1
  110. package/dist/updates/0.34.0/index.d.ts +2 -0
  111. package/dist/updates/0.34.0/index.js +17 -0
  112. package/dist/updates/index.js +3 -1
  113. package/package.json +7 -2
  114. package/dist/paraglide/messages/en.d.ts +0 -5
  115. package/dist/paraglide/messages/en.js +0 -14
  116. package/dist/paraglide/messages/pl.d.ts +0 -5
  117. package/dist/paraglide/messages/pl.js +0 -14
@@ -93,24 +93,18 @@ export declare const reorderShippingMethodsCmd: import("@sveltejs/kit").RemoteCo
93
93
  }>>;
94
94
  export declare const listOrdersAdmin: import("@sveltejs/kit").RemoteQueryFunction<{
95
95
  status?: "done" | "new" | "awaitingPayment" | "paid" | "preparing" | "sent" | "cancelled" | "paymentRejected" | "refunded" | undefined;
96
- email?: string | undefined;
96
+ search?: string | undefined;
97
97
  limit?: number | undefined;
98
98
  offset?: number | undefined;
99
+ deleted?: "only" | "include" | "exclude" | undefined;
99
100
  } | undefined, {
100
101
  items: {
101
102
  number: string;
102
- currency: string;
103
- consents: {
104
- id: string;
105
- accepted: boolean;
106
- label: string;
107
- }[] | null;
108
103
  id: string;
109
- status: import("../../shop/types.js").OrderStatus;
110
- createdAt: Date;
111
- updatedAt: Date;
112
- language: string | null;
113
104
  carrierType: string | null;
105
+ createdAt: Date;
106
+ status: import("../../shop/types.js").OrderStatus;
107
+ currency: string;
114
108
  customerEmail: string;
115
109
  customerName: string | null;
116
110
  customerPhone: string | null;
@@ -132,10 +126,19 @@ export declare const listOrdersAdmin: import("@sveltejs/kit").RemoteQueryFunctio
132
126
  shipmentCreatedAt: Date | null;
133
127
  paymentMethod: string | null;
134
128
  paymentProviderRef: string | null;
129
+ consents: {
130
+ id: string;
131
+ accepted: boolean;
132
+ label: string;
133
+ }[] | null;
135
134
  notes: string | null;
135
+ language: string | null;
136
136
  accessToken: string;
137
137
  partialPayment: import("../../shop/types.js").PartialPayment | null;
138
138
  balanceOwed: boolean;
139
+ deletedAt: Date | null;
140
+ deletedBy: string | null;
141
+ updatedAt: Date;
139
142
  }[];
140
143
  total: number;
141
144
  limit: number;
@@ -144,18 +147,11 @@ export declare const listOrdersAdmin: import("@sveltejs/kit").RemoteQueryFunctio
144
147
  export declare const getOrderForAdmin: import("@sveltejs/kit").RemoteQueryFunction<string, {
145
148
  order: {
146
149
  number: string;
147
- currency: string;
148
- consents: {
149
- id: string;
150
- accepted: boolean;
151
- label: string;
152
- }[] | null;
153
150
  id: string;
154
- status: import("../../shop/types.js").OrderStatus;
155
- createdAt: Date;
156
- updatedAt: Date;
157
- language: string | null;
158
151
  carrierType: string | null;
152
+ createdAt: Date;
153
+ status: import("../../shop/types.js").OrderStatus;
154
+ currency: string;
159
155
  customerEmail: string;
160
156
  customerName: string | null;
161
157
  customerPhone: string | null;
@@ -177,10 +173,19 @@ export declare const getOrderForAdmin: import("@sveltejs/kit").RemoteQueryFuncti
177
173
  shipmentCreatedAt: Date | null;
178
174
  paymentMethod: string | null;
179
175
  paymentProviderRef: string | null;
176
+ consents: {
177
+ id: string;
178
+ accepted: boolean;
179
+ label: string;
180
+ }[] | null;
180
181
  notes: string | null;
182
+ language: string | null;
181
183
  accessToken: string;
182
184
  partialPayment: import("../../shop/types.js").PartialPayment | null;
183
185
  balanceOwed: boolean;
186
+ deletedAt: Date | null;
187
+ deletedBy: string | null;
188
+ updatedAt: Date;
184
189
  };
185
190
  items: {
186
191
  id: string;
@@ -196,12 +201,16 @@ export declare const getOrderForAdmin: import("@sveltejs/kit").RemoteQueryFuncti
196
201
  }[];
197
202
  history: {
198
203
  id: string;
199
- note: string | null;
200
204
  status: import("../../shop/types.js").OrderStatus;
205
+ note: string | null;
201
206
  orderId: string;
202
207
  changedBy: string | null;
203
208
  changedAt: Date;
204
209
  }[];
210
+ coupon: {
211
+ code: string;
212
+ discountAmount: number;
213
+ } | null;
205
214
  } | null>;
206
215
  export declare const updateOrderStatusCmd: import("@sveltejs/kit").RemoteCommand<{
207
216
  orderId: string;
@@ -209,18 +218,11 @@ export declare const updateOrderStatusCmd: import("@sveltejs/kit").RemoteCommand
209
218
  note?: string | undefined;
210
219
  }, Promise<{
211
220
  number: string;
212
- currency: string;
213
- consents: {
214
- id: string;
215
- accepted: boolean;
216
- label: string;
217
- }[] | null;
218
221
  id: string;
219
- status: import("../../shop/types.js").OrderStatus;
220
- createdAt: Date;
221
- updatedAt: Date;
222
- language: string | null;
223
222
  carrierType: string | null;
223
+ createdAt: Date;
224
+ status: import("../../shop/types.js").OrderStatus;
225
+ currency: string;
224
226
  customerEmail: string;
225
227
  customerName: string | null;
226
228
  customerPhone: string | null;
@@ -242,10 +244,35 @@ export declare const updateOrderStatusCmd: import("@sveltejs/kit").RemoteCommand
242
244
  shipmentCreatedAt: Date | null;
243
245
  paymentMethod: string | null;
244
246
  paymentProviderRef: string | null;
247
+ consents: {
248
+ id: string;
249
+ accepted: boolean;
250
+ label: string;
251
+ }[] | null;
245
252
  notes: string | null;
253
+ language: string | null;
246
254
  accessToken: string;
247
255
  partialPayment: import("../../shop/types.js").PartialPayment | null;
248
256
  balanceOwed: boolean;
257
+ deletedAt: Date | null;
258
+ deletedBy: string | null;
259
+ updatedAt: Date;
260
+ }>>;
261
+ export declare const deleteOrderCmd: import("@sveltejs/kit").RemoteCommand<{
262
+ orderId: string;
263
+ }, Promise<{
264
+ success: true;
265
+ reason?: undefined;
266
+ error?: undefined;
267
+ } | {
268
+ success: false;
269
+ reason: "status" | "invoice";
270
+ error: string;
271
+ }>>;
272
+ export declare const restoreOrderCmd: import("@sveltejs/kit").RemoteCommand<{
273
+ orderId: string;
274
+ }, Promise<{
275
+ success: true;
249
276
  }>>;
250
277
  export declare const resendOrderEmailCmd: import("@sveltejs/kit").RemoteCommand<{
251
278
  orderId: string;
@@ -286,18 +313,18 @@ export declare const listShopableCollections: import("@sveltejs/kit").RemoteQuer
286
313
  }[]>;
287
314
  export declare const getOrderRefundsAdmin: import("@sveltejs/kit").RemoteQueryFunction<string, {
288
315
  refunds: {
289
- amount: number;
290
- currency: string;
291
316
  id: string;
292
- status: import("../../db-postgres/schema/shop/index.js").ShopRefundStatus;
293
317
  createdAt: Date;
318
+ status: import("../../db-postgres/schema/shop/index.js").ShopRefundStatus;
319
+ currency: string;
294
320
  updatedAt: Date;
295
- orderId: string;
321
+ amount: number;
296
322
  provider: string;
323
+ createdBy: string | null;
324
+ orderId: string;
297
325
  providerRef: string | null;
298
326
  paymentId: string | null;
299
327
  reason: string | null;
300
- createdBy: string | null;
301
328
  }[];
302
329
  refundedAmount: number;
303
330
  remainingRefundable: number;
@@ -327,13 +354,13 @@ export declare const refundOrderCmd: import("@sveltejs/kit").RemoteCommand<{
327
354
  export declare const getOrderInvoiceAdmin: import("@sveltejs/kit").RemoteQueryFunction<string, {
328
355
  invoice: {
329
356
  number: string | null;
330
- raw: unknown;
331
357
  id: string;
332
- status: import("../../db-postgres/schema/shop/index.js").ShopInvoiceStatus;
333
358
  createdAt: Date;
359
+ status: import("../../db-postgres/schema/shop/index.js").ShopInvoiceStatus;
334
360
  updatedAt: Date;
335
- orderId: string;
361
+ raw: unknown;
336
362
  provider: string;
363
+ orderId: string;
337
364
  kind: string;
338
365
  externalId: string | null;
339
366
  pdfUrl: string | null;
@@ -350,13 +377,13 @@ export declare const issueInvoiceCmd: import("@sveltejs/kit").RemoteCommand<{
350
377
  success: true;
351
378
  invoice: {
352
379
  number: string | null;
353
- raw: unknown;
354
380
  id: string;
355
- status: import("../../db-postgres/schema/shop/index.js").ShopInvoiceStatus;
356
381
  createdAt: Date;
382
+ status: import("../../db-postgres/schema/shop/index.js").ShopInvoiceStatus;
357
383
  updatedAt: Date;
358
- orderId: string;
384
+ raw: unknown;
359
385
  provider: string;
386
+ orderId: string;
360
387
  kind: string;
361
388
  externalId: string | null;
362
389
  pdfUrl: string | null;
@@ -409,7 +436,7 @@ export declare const getCouponAdmin: import("@sveltejs/kit").RemoteQueryFunction
409
436
  }>;
410
437
  export declare const createCouponCmd: import("@sveltejs/kit").RemoteCommand<{
411
438
  code: string;
412
- type: "percent" | "fixed";
439
+ type: "fixed" | "percent";
413
440
  value: number;
414
441
  minOrderAmount?: number | null | undefined;
415
442
  maxUses?: number | null | undefined;
@@ -418,12 +445,12 @@ export declare const createCouponCmd: import("@sveltejs/kit").RemoteCommand<{
418
445
  }, Promise<{
419
446
  code: string;
420
447
  id: string;
421
- type: import("../../db-postgres/schema/shop/index.js").ShopCouponType;
448
+ isActive: boolean;
422
449
  createdAt: Date;
423
450
  updatedAt: Date;
424
- isActive: boolean;
425
451
  expiresAt: Date | null;
426
452
  value: string;
453
+ type: import("../../db-postgres/schema/shop/index.js").ShopCouponType;
427
454
  minOrderAmount: number | null;
428
455
  maxUses: number | null;
429
456
  usedCount: number;
@@ -432,7 +459,7 @@ export declare const updateCouponCmd: import("@sveltejs/kit").RemoteCommand<{
432
459
  id: string;
433
460
  input: {
434
461
  code?: string | undefined;
435
- type?: "percent" | "fixed" | undefined;
462
+ type?: "fixed" | "percent" | undefined;
436
463
  value?: number | undefined;
437
464
  minOrderAmount?: number | null | undefined;
438
465
  maxUses?: number | null | undefined;
@@ -457,7 +484,7 @@ export declare const deleteCouponCmd: import("@sveltejs/kit").RemoteCommand<stri
457
484
  }>>;
458
485
  export declare const exportOrdersCsv: import("@sveltejs/kit").RemoteQueryFunction<{
459
486
  status?: "done" | "new" | "awaitingPayment" | "paid" | "preparing" | "sent" | "cancelled" | "paymentRejected" | "refunded" | undefined;
460
- email?: string | undefined;
487
+ search?: string | undefined;
461
488
  } | undefined, {
462
489
  csv: string;
463
490
  count: number;
@@ -3,15 +3,16 @@ import z from 'zod';
3
3
  import { getCMS } from '../../core/cms.js';
4
4
  import { deleteShopData, getShopDataByEntry, listShopEntries, upsertShopData } from '../../shop/server/shop-data.js';
5
5
  import { createShippingMethod, deleteShippingMethod, getShippingMethod, listShippingMethods, reorderShippingMethods, updateShippingMethod } from '../../shop/server/shipping.js';
6
- import { countOrders, getOrderById, getOrderItems, getOrderStatusHistory, listOrders, updateOrderStatus } from '../../shop/server/orders.js';
6
+ import { countOrders, getOrderById, getOrderItems, getOrderStatusHistory, listOrders, OrderNotDeletableError, restoreOrder, softDeleteOrder, updateOrderStatus } from '../../shop/server/orders.js';
7
7
  import { cancelShipmentForOrder, createShipmentForOrder } from '../../shop/server/shipments.js';
8
8
  import { sendOrderStatusEmail } from '../../shop/server/email.js';
9
+ import { getOrderCoupon } from '../../shop/server/coupons.js';
9
10
  import { getInvoiceByOrderId, issueInvoiceForOrder } from '../../shop/server/invoices.js';
10
11
  import { getRefundedAmount, listRefunds, refundOrder, RefundError } from '../../shop/server/refund.js';
11
12
  import { getShopDb } from '../../shop/server/db.js';
12
13
  import { shopCouponsTable } from '../../db-postgres/schema/shop/index.js';
13
14
  import { eq } from 'drizzle-orm';
14
- import { requireAuth } from './middleware/auth.js';
15
+ import { requireAuth, requireRole } from './middleware/auth.js';
15
16
  export const getShopEnabled = query(async () => {
16
17
  return getCMS().shopConfig !== null;
17
18
  });
@@ -138,18 +139,25 @@ const orderStatusSchema = z.enum([
138
139
  export const listOrdersAdmin = query(z
139
140
  .object({
140
141
  status: orderStatusSchema.optional(),
141
- email: z.string().optional(),
142
+ search: z.string().trim().min(1).optional(),
142
143
  limit: z.number().int().optional(),
143
- offset: z.number().int().optional()
144
+ offset: z.number().int().optional(),
145
+ deleted: z.enum(['exclude', 'only', 'include']).optional()
144
146
  })
145
147
  .optional(), async (opts) => {
146
- requireAuth();
147
148
  const params = opts ?? {};
149
+ const deleted = params.deleted ?? 'exclude';
150
+ // The trash (soft-deleted orders) is admin-only; the normal list is for
151
+ // any authenticated admin-panel user.
152
+ if (deleted === 'exclude')
153
+ requireAuth();
154
+ else
155
+ requireRole('admin');
148
156
  const limit = params.limit ?? 50;
149
157
  const offset = params.offset ?? 0;
150
158
  const [items, total] = await Promise.all([
151
- listOrders({ ...params, limit, offset }),
152
- countOrders({ status: params.status, email: params.email })
159
+ listOrders({ ...params, deleted, limit, offset }),
160
+ countOrders({ status: params.status, search: params.search, deleted })
153
161
  ]);
154
162
  return { items, total, limit, offset };
155
163
  });
@@ -158,8 +166,12 @@ export const getOrderForAdmin = query(z.string(), async (id) => {
158
166
  const order = await getOrderById(id);
159
167
  if (!order)
160
168
  return null;
161
- const [items, history] = await Promise.all([getOrderItems(id), getOrderStatusHistory(id)]);
162
- return { order, items, history };
169
+ const [items, history, coupon] = await Promise.all([
170
+ getOrderItems(id),
171
+ getOrderStatusHistory(id),
172
+ getOrderCoupon(id).catch(() => null)
173
+ ]);
174
+ return { order, items, history, coupon };
163
175
  });
164
176
  export const updateOrderStatusCmd = command(z.object({
165
177
  orderId: z.string(),
@@ -170,6 +182,25 @@ export const updateOrderStatusCmd = command(z.object({
170
182
  const updated = await updateOrderStatus(orderId, status, { note, changedBy: 'admin' });
171
183
  return updated;
172
184
  });
185
+ export const deleteOrderCmd = command(z.object({ orderId: z.string() }), async ({ orderId }) => {
186
+ const { user } = requireRole('admin');
187
+ try {
188
+ await softDeleteOrder(orderId, user.id);
189
+ return { success: true };
190
+ }
191
+ catch (err) {
192
+ if (err instanceof OrderNotDeletableError) {
193
+ return { success: false, reason: err.reason, error: err.message };
194
+ }
195
+ const message = err instanceof Error ? err.message : 'Nie udało się usunąć zamówienia';
196
+ return { success: false, reason: 'status', error: message };
197
+ }
198
+ });
199
+ export const restoreOrderCmd = command(z.object({ orderId: z.string() }), async ({ orderId }) => {
200
+ requireRole('admin');
201
+ await restoreOrder(orderId);
202
+ return { success: true };
203
+ });
173
204
  export const resendOrderEmailCmd = command(z.object({ orderId: z.string(), status: orderStatusSchema }), async ({ orderId, status }) => {
174
205
  requireAuth();
175
206
  await sendOrderStatusEmail(orderId, status);
@@ -393,7 +424,7 @@ function csvEscape(value) {
393
424
  const exportFiltersSchema = z
394
425
  .object({
395
426
  status: orderStatusSchema.optional(),
396
- email: z.string().optional()
427
+ search: z.string().trim().min(1).optional()
397
428
  })
398
429
  .optional();
399
430
  export const exportOrdersCsv = query(exportFiltersSchema, async (opts) => {
@@ -8,3 +8,18 @@ export interface Breadcrumb {
8
8
  label: string;
9
9
  href?: string;
10
10
  }
11
+ /**
12
+ * One additional sidebar entry rendered below built-in nav sections
13
+ * (Dashboard, Collections, Singles, Forms, Shop). Configure via
14
+ * `defineConfig({ admin: { extraNavItems: [...] } })` — see CMSConfig.
15
+ *
16
+ * Icons are intentionally omitted from the public surface to keep userland
17
+ * free of `@tabler/icons-svelte` (or any specific icon library) as a
18
+ * dependency. The sidebar renders the title alone when no icon is provided.
19
+ *
20
+ * @public
21
+ */
22
+ export interface AdminNavItem {
23
+ title: string;
24
+ url: string;
25
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Build a CSV string from `headers` and `rows`. Values are escaped per
3
+ * RFC 4180: any cell containing `"`, `,`, `;`, `\n`, or `\r` is wrapped in
4
+ * double quotes and embedded `"` are doubled. `null` / `undefined` become
5
+ * empty cells. Lines are joined with CRLF.
6
+ *
7
+ * Pure (no DOM access) — safe on the server. Use together with
8
+ * `downloadCsv()` on the client, or return as a `Response` from a server
9
+ * action.
10
+ *
11
+ * @public
12
+ */
13
+ export declare function buildCsv(opts: {
14
+ headers: string[];
15
+ rows: (string | number | null | undefined)[][];
16
+ }): string;
17
+ /**
18
+ * Trigger a client-side CSV download. Wraps the given `csv` string in a
19
+ * `Blob` with `text/csv;charset=utf-8` and clicks a synthetic
20
+ * `<a download>`. Prepends a UTF-8 BOM (``) unless one is already
21
+ * present, so Excel honors UTF-8 (PL diacritics).
22
+ *
23
+ * Must be called from a browser context — relies on `document`, `Blob`,
24
+ * and `URL.createObjectURL`. Throws when called server-side.
25
+ *
26
+ * Typical wiring: server returns a precomposed CSV (e.g. via a
27
+ * `query()` remote like `exportOrdersCsv`) → client calls
28
+ * `downloadCsv({ filename, csv: result.csv })`.
29
+ *
30
+ * @public
31
+ * @example
32
+ * ```ts
33
+ * import { buildCsv, downloadCsv } from 'includio-cms/admin/client';
34
+ *
35
+ * const csv = buildCsv({
36
+ * headers: ['email', 'name', 'consentedAt'],
37
+ * rows: items.map((i) => [i.email, i.name, i.consentedAt])
38
+ * });
39
+ * downloadCsv({ filename: 'newsletter-2026-06-02.csv', csv });
40
+ * ```
41
+ */
42
+ export declare function downloadCsv(opts: {
43
+ filename: string;
44
+ csv: string;
45
+ }): void;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Build a CSV string from `headers` and `rows`. Values are escaped per
3
+ * RFC 4180: any cell containing `"`, `,`, `;`, `\n`, or `\r` is wrapped in
4
+ * double quotes and embedded `"` are doubled. `null` / `undefined` become
5
+ * empty cells. Lines are joined with CRLF.
6
+ *
7
+ * Pure (no DOM access) — safe on the server. Use together with
8
+ * `downloadCsv()` on the client, or return as a `Response` from a server
9
+ * action.
10
+ *
11
+ * @public
12
+ */
13
+ export function buildCsv(opts) {
14
+ const escape = (v) => {
15
+ const s = v == null ? '' : String(v);
16
+ return /["\n\r,;]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
17
+ };
18
+ const lines = [opts.headers, ...opts.rows].map((row) => row.map(escape).join(','));
19
+ return lines.join('\r\n');
20
+ }
21
+ /**
22
+ * Trigger a client-side CSV download. Wraps the given `csv` string in a
23
+ * `Blob` with `text/csv;charset=utf-8` and clicks a synthetic
24
+ * `<a download>`. Prepends a UTF-8 BOM (``) unless one is already
25
+ * present, so Excel honors UTF-8 (PL diacritics).
26
+ *
27
+ * Must be called from a browser context — relies on `document`, `Blob`,
28
+ * and `URL.createObjectURL`. Throws when called server-side.
29
+ *
30
+ * Typical wiring: server returns a precomposed CSV (e.g. via a
31
+ * `query()` remote like `exportOrdersCsv`) → client calls
32
+ * `downloadCsv({ filename, csv: result.csv })`.
33
+ *
34
+ * @public
35
+ * @example
36
+ * ```ts
37
+ * import { buildCsv, downloadCsv } from 'includio-cms/admin/client';
38
+ *
39
+ * const csv = buildCsv({
40
+ * headers: ['email', 'name', 'consentedAt'],
41
+ * rows: items.map((i) => [i.email, i.name, i.consentedAt])
42
+ * });
43
+ * downloadCsv({ filename: 'newsletter-2026-06-02.csv', csv });
44
+ * ```
45
+ */
46
+ export function downloadCsv(opts) {
47
+ if (typeof document === 'undefined') {
48
+ throw new Error('[includio-cms] downloadCsv() must be called from a browser context.');
49
+ }
50
+ const csv = opts.csv.startsWith('') ? opts.csv : '' + opts.csv;
51
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
52
+ const url = URL.createObjectURL(blob);
53
+ const a = document.createElement('a');
54
+ a.href = url;
55
+ a.download = opts.filename;
56
+ a.style.display = 'none';
57
+ document.body.appendChild(a);
58
+ a.click();
59
+ document.body.removeChild(a);
60
+ URL.revokeObjectURL(url);
61
+ }
@@ -348,7 +348,7 @@ export const { GET, POST, PUT, DELETE } = createRestApiHandler();
348
348
  import { json } from '@sveltejs/kit';
349
349
  import type { RequestHandler } from './$types';
350
350
  import { createFormSubmission, parseFormDataForSubmission } from 'includio-cms/sveltekit/server';
351
- import { getCMS } from 'includio-cms/core';
351
+ import { getCMS } from 'includio-cms/core/server';
352
352
 
353
353
  const counts = new Map<string, { count: number; resetAt: number }>();
354
354
  const LIMIT = 5;
@@ -1,7 +1,7 @@
1
1
  declare const ButtonGroupSeparator: import("svelte").Component<Omit<{
2
2
  orientation?: import("bits-ui").Orientation;
3
3
  decorative?: boolean;
4
- }, "children" | "child"> & {
4
+ }, "child" | "children"> & {
5
5
  child?: import("svelte").Snippet<[{
6
6
  props: Record<string, unknown>;
7
7
  }]> | undefined;
@@ -3,6 +3,6 @@ export type CommandRootApi = CommandPrimitive.Root;
3
3
  type $$ComponentProps = CommandPrimitive.RootProps & {
4
4
  api?: CommandRootApi | null;
5
5
  };
6
- declare const Command: import("svelte").Component<$$ComponentProps, {}, "ref" | "value" | "api">;
6
+ declare const Command: import("svelte").Component<$$ComponentProps, {}, "ref" | "api" | "value">;
7
7
  type Command = ReturnType<typeof Command>;
8
8
  export default Command;
@@ -1,4 +1,4 @@
1
- declare const FieldLabel: import("svelte").Component<Omit<{}, "children" | "child"> & {
1
+ declare const FieldLabel: import("svelte").Component<Omit<{}, "child" | "children"> & {
2
2
  child?: import("svelte").Snippet<[{
3
3
  props: Record<string, unknown>;
4
4
  }]> | undefined;
@@ -8,6 +8,6 @@ type Props = WithElementRef<Omit<HTMLInputAttributes, "type"> & ({
8
8
  type?: InputType;
9
9
  files?: undefined;
10
10
  })>;
11
- declare const Input: import("svelte").Component<Props, {}, "files" | "ref" | "value">;
11
+ declare const Input: import("svelte").Component<Props, {}, "ref" | "value" | "files">;
12
12
  type Input = ReturnType<typeof Input>;
13
13
  export default Input;
@@ -2,7 +2,7 @@ declare const InputGroupInput: import("svelte").Component<(Omit<import("svelte/e
2
2
  type: "file";
3
3
  files?: FileList;
4
4
  } | {
5
- type?: "number" | "image" | "url" | "text" | "date" | "radio" | "color" | "button" | "checkbox" | "search" | (string & {}) | "email" | "tel" | "password" | "time" | "hidden" | "reset" | "submit" | "datetime-local" | "month" | "range" | "week";
5
+ type?: "number" | "image" | "url" | "text" | "date" | "search" | "radio" | "hidden" | "email" | "password" | (string & {}) | "reset" | "color" | "button" | "checkbox" | "tel" | "time" | "month" | "submit" | "datetime-local" | "range" | "week";
6
6
  files?: undefined;
7
7
  })) & {
8
8
  ref?: HTMLElement | null | undefined;
@@ -1,7 +1,7 @@
1
1
  declare const ItemSeparator: import("svelte").Component<Omit<{
2
2
  orientation?: import("bits-ui").Orientation;
3
3
  decorative?: boolean;
4
- }, "children" | "child"> & {
4
+ }, "child" | "children"> & {
5
5
  child?: import("svelte").Snippet<[{
6
6
  props: Record<string, unknown>;
7
7
  }]> | undefined;
@@ -1,4 +1,4 @@
1
- declare const SelectGroupHeading: import("svelte").Component<Omit<{}, "children" | "child"> & {
1
+ declare const SelectGroupHeading: import("svelte").Component<Omit<{}, "child" | "children"> & {
2
2
  child?: import("svelte").Snippet<[{
3
3
  props: Record<string, unknown>;
4
4
  }]> | undefined;
@@ -2,7 +2,7 @@ declare const SidebarInput: import("svelte").Component<(Omit<import("svelte/elem
2
2
  type: "file";
3
3
  files?: FileList;
4
4
  } | {
5
- type?: "number" | "image" | "url" | "text" | "date" | "radio" | "color" | "button" | "checkbox" | "search" | (string & {}) | "email" | "tel" | "password" | "time" | "hidden" | "reset" | "submit" | "datetime-local" | "month" | "range" | "week";
5
+ type?: "number" | "image" | "url" | "text" | "date" | "search" | "radio" | "hidden" | "email" | "password" | (string & {}) | "reset" | "color" | "button" | "checkbox" | "tel" | "time" | "month" | "submit" | "datetime-local" | "range" | "week";
6
6
  files?: undefined;
7
7
  })) & {
8
8
  ref?: HTMLElement | null | undefined;
@@ -1,7 +1,7 @@
1
1
  declare const SidebarSeparator: import("svelte").Component<Omit<{
2
2
  orientation?: import("bits-ui").Orientation;
3
3
  decorative?: boolean;
4
- }, "children" | "child"> & {
4
+ }, "child" | "children"> & {
5
5
  child?: import("svelte").Snippet<[{
6
6
  props: Record<string, unknown>;
7
7
  }]> | undefined;
@@ -1,13 +1,13 @@
1
1
  import type { DatabaseAdapter } from '../types/adapters/db.js';
2
2
  import type { FilesAdapter } from '../types/adapters/files.js';
3
- import type { ApiKeyConfig, AuthConfig, CMSConfig, ICMS, MediaConfig, TypographyConfig } from '../types/cms.js';
3
+ import type { AdminConfig, ApiKeyConfig, AuthConfig, CMSConfig, ICMS, MediaConfig, TypographyConfig } from '../types/cms.js';
4
4
  import type { CollectionConfigWithType } from '../types/collections.js';
5
5
  import type { Language } from '../types/languages.js';
6
6
  import type { SingleConfigWithType } from '../types/singles.js';
7
7
  import type { CustomFieldDefinition, IconSetPlugin, Plugin } from '../types/plugins.js';
8
8
  import type { FormConfig } from '../types/forms.js';
9
9
  import type { AIAdapter } from '../types/adapters/ai.js';
10
- import type { EmailAdapter } from '../types/adapters/email.js';
10
+ import type { EmailAdapter, SendMailOptions } from '../types/adapters/email.js';
11
11
  import { betterAuth } from 'better-auth';
12
12
  import type { ResolvedShopConfig } from '../shop/types.js';
13
13
  import type { ResolvedCmpConfig } from '../cmp/types.js';
@@ -28,6 +28,7 @@ export declare class CMS implements ICMS {
28
28
  sidebarHelp: boolean;
29
29
  shopConfig: ResolvedShopConfig | null;
30
30
  cmpConfig: ResolvedCmpConfig | null;
31
+ adminConfig: AdminConfig | null;
31
32
  /**
32
33
  * Resolves once the shop's variant-attribute GIN indexes have been applied
33
34
  * by `initCMS()`. `null` when the CMS is configured without a shop. Tests
@@ -64,3 +65,44 @@ export declare function initCMS(config: CMSConfig): CMS;
64
65
  * ```
65
66
  */
66
67
  export declare function getCMS(): CMS;
68
+ /**
69
+ * Returns the configured email adapter from the active CMS singleton, or
70
+ * `null` when no `email` was passed to `defineConfig`. Use this in userland
71
+ * hooks (e.g. `ShopConfig.onOrderPaid`) to reuse the same SMTP transport the
72
+ * CMS already opened — no second nodemailer instance, no duplicated ENV.
73
+ *
74
+ * @returns The current `EmailAdapter` or `null`.
75
+ * @throws {Error} when called before `initCMS()` has run.
76
+ * @public
77
+ * @example
78
+ * ```ts
79
+ * import { getMailer } from 'includio-cms/core';
80
+ *
81
+ * const mailer = getMailer();
82
+ * if (mailer) await mailer.sendMail({ to: 'admin@…', subject: '…', html: '…' });
83
+ * ```
84
+ */
85
+ export declare function getMailer(): EmailAdapter | null;
86
+ /**
87
+ * Send an email through the configured CMS email adapter. Thin convenience
88
+ * wrapper around `getCMS().emailAdapter.sendMail` — same options shape, same
89
+ * From address, same SMTP transport as the built-in flows (reset password,
90
+ * shop order status, form notifications).
91
+ *
92
+ * @throws {Error} when no email adapter is configured (matches
93
+ * `defineConfig({ email: ... })` not being set) or when the CMS has not
94
+ * been initialized yet.
95
+ * @public
96
+ * @example
97
+ * ```ts
98
+ * import { sendMail } from 'includio-cms/core';
99
+ *
100
+ * await sendMail({
101
+ * to: process.env.ADMIN_EMAIL!,
102
+ * subject: '[shop] New consent',
103
+ * text: 'Plain fallback for clients that prefer text.',
104
+ * html: '<p>Body…</p>'
105
+ * });
106
+ * ```
107
+ */
108
+ export declare function sendMail(options: SendMailOptions): Promise<void>;