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.
- package/API.md +39 -13
- package/CHANGELOG.md +19 -0
- package/DOCS.md +1 -1
- package/ROADMAP.md +1 -0
- package/dist/admin/api/handler.js +4 -0
- package/dist/admin/api/integrations.d.ts +13 -0
- package/dist/admin/api/integrations.js +61 -0
- package/dist/admin/api/test-email.d.ts +9 -0
- package/dist/admin/api/test-email.js +39 -0
- package/dist/admin/auth-client.d.ts +2209 -2209
- package/dist/admin/client/index.d.ts +10 -0
- package/dist/admin/client/index.js +12 -0
- package/dist/admin/client/maintenance/maintenance-page.svelte +210 -0
- package/dist/admin/client/shop/coupon-schema.d.ts +1 -1
- package/dist/admin/client/shop/restore-order-cell.svelte +29 -0
- package/dist/admin/client/shop/restore-order-cell.svelte.d.ts +8 -0
- package/dist/admin/client/shop/shop-order-detail-page.svelte +71 -1
- package/dist/admin/client/shop/shop-orders-list-page.svelte +113 -53
- package/dist/admin/components/layout/app-sidebar.svelte +2 -0
- package/dist/admin/components/layout/nav-custom.svelte +26 -0
- package/dist/admin/components/layout/nav-custom.svelte.d.ts +3 -0
- package/dist/admin/components/layout/page-header.svelte +13 -3
- package/dist/admin/components/layout/page-header.svelte.d.ts +13 -3
- package/dist/admin/remote/admin.remote.d.ts +7 -0
- package/dist/admin/remote/admin.remote.js +10 -0
- package/dist/admin/remote/entry.remote.d.ts +4 -4
- package/dist/admin/remote/index.d.ts +1 -0
- package/dist/admin/remote/index.js +1 -0
- package/dist/admin/remote/invite.d.ts +2 -2
- package/dist/admin/remote/shop.remote.d.ts +75 -48
- package/dist/admin/remote/shop.remote.js +41 -10
- package/dist/admin/types.d.ts +15 -0
- package/dist/admin/utils/csv-export.d.ts +45 -0
- package/dist/admin/utils/csv-export.js +61 -0
- package/dist/cli/scaffold/admin.js +1 -1
- package/dist/components/ui/button-group/button-group-separator.svelte.d.ts +1 -1
- package/dist/components/ui/command/command.svelte.d.ts +1 -1
- package/dist/components/ui/field/field-label.svelte.d.ts +1 -1
- package/dist/components/ui/input/input.svelte.d.ts +1 -1
- package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
- package/dist/components/ui/item/item-separator.svelte.d.ts +1 -1
- package/dist/components/ui/select/select-group-heading.svelte.d.ts +1 -1
- package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
- package/dist/components/ui/sidebar/sidebar-separator.svelte.d.ts +1 -1
- package/dist/core/cms.d.ts +44 -2
- package/dist/core/cms.js +64 -0
- package/dist/core/index.d.ts +1 -4
- package/dist/core/index.js +4 -4
- package/dist/core/server/index.d.ts +4 -1
- package/dist/core/server/index.js +4 -1
- package/dist/db-postgres/schema/shop/order.d.ts +34 -0
- package/dist/db-postgres/schema/shop/order.js +4 -0
- package/dist/paraglide/messages/_index.d.ts +3 -36
- package/dist/paraglide/messages/_index.js +3 -71
- package/dist/paraglide/messages/hello_world.d.ts +5 -0
- package/dist/paraglide/messages/hello_world.js +33 -0
- package/dist/paraglide/messages/login_hello.d.ts +16 -0
- package/dist/paraglide/messages/login_hello.js +34 -0
- package/dist/paraglide/messages/login_please_login.d.ts +16 -0
- package/dist/paraglide/messages/login_please_login.js +34 -0
- package/dist/shop/adapters/fakturownia/client.d.ts +5 -0
- package/dist/shop/adapters/fakturownia/client.js +20 -0
- package/dist/shop/adapters/fakturownia/index.js +11 -0
- package/dist/shop/adapters/payu/index.js +11 -0
- package/dist/shop/index.d.ts +1 -1
- package/dist/shop/server/coupons.d.ts +10 -0
- package/dist/shop/server/coupons.js +19 -0
- package/dist/shop/server/email.d.ts +7 -3
- package/dist/shop/server/email.js +86 -112
- package/dist/shop/server/emailTemplateRegistry.d.ts +47 -0
- package/dist/shop/server/emailTemplateRegistry.js +288 -0
- package/dist/shop/server/orders.d.ts +60 -1
- package/dist/shop/server/orders.js +145 -16
- package/dist/shop/templates/_partials/footer.en.html +4 -0
- package/dist/shop/templates/_partials/footer.pl.html +4 -0
- package/dist/shop/templates/_partials/header.en.html +4 -0
- package/dist/shop/templates/_partials/header.pl.html +4 -0
- package/dist/shop/templates/_partials/items.en.html +14 -0
- package/dist/shop/templates/_partials/items.pl.html +14 -0
- package/dist/shop/templates/_partials/tracking.en.html +7 -0
- package/dist/shop/templates/_partials/tracking.pl.html +7 -0
- package/dist/shop/templates/awaiting-payment.en.html +6 -0
- package/dist/shop/templates/awaiting-payment.pl.html +6 -0
- package/dist/shop/templates/cancelled.en.html +6 -0
- package/dist/shop/templates/cancelled.pl.html +6 -0
- package/dist/shop/templates/low-stock.en.html +14 -0
- package/dist/shop/templates/low-stock.pl.html +14 -0
- package/dist/shop/templates/order-completed.en.html +6 -0
- package/dist/shop/templates/order-completed.pl.html +6 -0
- package/dist/shop/templates/order-received.en.html +7 -0
- package/dist/shop/templates/order-received.pl.html +7 -0
- package/dist/shop/templates/payment-received.en.html +7 -0
- package/dist/shop/templates/payment-received.pl.html +7 -0
- package/dist/shop/templates/payment-rejected.en.html +6 -0
- package/dist/shop/templates/payment-rejected.pl.html +6 -0
- package/dist/shop/templates/preparing.en.html +7 -0
- package/dist/shop/templates/preparing.pl.html +7 -0
- package/dist/shop/templates/refunded.en.html +6 -0
- package/dist/shop/templates/refunded.pl.html +6 -0
- package/dist/shop/templates/shipped.en.html +7 -0
- package/dist/shop/templates/shipped.pl.html +7 -0
- package/dist/shop/types.d.ts +63 -0
- package/dist/sveltekit/index.d.ts +0 -1
- package/dist/sveltekit/index.js +0 -1
- package/dist/sveltekit/server/index.d.ts +2 -0
- package/dist/sveltekit/server/index.js +4 -0
- package/dist/types/adapters/email.d.ts +13 -0
- package/dist/types/cms.d.ts +30 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/updates/0.34.0/index.d.ts +2 -0
- package/dist/updates/0.34.0/index.js +17 -0
- package/dist/updates/index.js +3 -1
- package/package.json +7 -2
- package/dist/paraglide/messages/en.d.ts +0 -5
- package/dist/paraglide/messages/en.js +0 -14
- package/dist/paraglide/messages/pl.d.ts +0 -5
- package/dist/paraglide/messages/pl.js +0 -14
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { and, asc, desc, eq, inArray, isNull, lt, sql } from 'drizzle-orm';
|
|
1
|
+
import { and, asc, desc, eq, ilike, inArray, isNotNull, isNull, lt, or, sql } from 'drizzle-orm';
|
|
2
2
|
import { shopOrderItemsTable, shopOrderStatusHistoryTable, shopOrdersTable, shopProductVariantsTable, shopProductsTable, shopShippingMethodsTable, shopStockReservationsTable } from '../../db-postgres/schema/shop/index.js';
|
|
3
3
|
import { getCMS } from '../../core/cms.js';
|
|
4
4
|
import { getShopDb, requireShopConfig } from './db.js';
|
|
@@ -9,7 +9,7 @@ import { sendLowStockEmail, sendOrderStatusEmail } from './email.js';
|
|
|
9
9
|
import { isPaymentMethodAllowed } from './payment-compat.js';
|
|
10
10
|
import { isVariantExpired, VariantExpiredError } from '../expiry.js';
|
|
11
11
|
import { resolvePaymentAmount } from './payment-policy.js';
|
|
12
|
-
import { maybeIssueInvoiceForOrder } from './invoices.js';
|
|
12
|
+
import { getInvoiceByOrderId, maybeIssueInvoiceForOrder } from './invoices.js';
|
|
13
13
|
import { CouponError, recordCouponRedemption, releaseCouponSlot, reserveCouponSlot, validateCoupon } from './coupons.js';
|
|
14
14
|
/**
|
|
15
15
|
* @public
|
|
@@ -27,6 +27,55 @@ export class MixedPaymentPolicyError extends Error {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
const STOCK_RESERVATION_TTL_MINUTES = 30;
|
|
30
|
+
/**
|
|
31
|
+
* @public
|
|
32
|
+
* Order statuses an admin is allowed to soft-delete (hide from the admin list).
|
|
33
|
+
* Restricted to states that never carry a settled payment or an issued invoice,
|
|
34
|
+
* so hiding one can't bury accounting/audit data. Paid-or-later and `refunded`
|
|
35
|
+
* orders are never deletable. The invoice guard in `softDeleteOrder` is the
|
|
36
|
+
* second line of defence.
|
|
37
|
+
*/
|
|
38
|
+
export const DELETABLE_ORDER_STATUSES = new Set([
|
|
39
|
+
'new',
|
|
40
|
+
'awaitingPayment',
|
|
41
|
+
'cancelled',
|
|
42
|
+
'paymentRejected'
|
|
43
|
+
]);
|
|
44
|
+
/** @public Pure status-level deletability check (no DB / invoice lookup). */
|
|
45
|
+
export function isOrderDeletable(status) {
|
|
46
|
+
return DELETABLE_ORDER_STATUSES.has(status);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* @public
|
|
50
|
+
* Pure decision: may this order be soft-deleted? Encodes both guards (status +
|
|
51
|
+
* existing invoice) so they're testable without a DB. `invoice` is the order's
|
|
52
|
+
* current invoice record (or null). An `issued`/`sent` invoice hard-blocks the
|
|
53
|
+
* delete; `pending`/`failed` invoices don't (no legal document was produced).
|
|
54
|
+
*/
|
|
55
|
+
export function decideOrderDeletion(status, invoice) {
|
|
56
|
+
if (!isOrderDeletable(status))
|
|
57
|
+
return { ok: false, reason: 'status' };
|
|
58
|
+
if (invoice && (invoice.status === 'issued' || invoice.status === 'sent')) {
|
|
59
|
+
return { ok: false, reason: 'invoice' };
|
|
60
|
+
}
|
|
61
|
+
return { ok: true };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* @public
|
|
65
|
+
* Thrown by `softDeleteOrder` when the order can't be hidden: either its status
|
|
66
|
+
* isn't in {@link DELETABLE_ORDER_STATUSES} or it already has an issued invoice.
|
|
67
|
+
*/
|
|
68
|
+
export class OrderNotDeletableError extends Error {
|
|
69
|
+
reason;
|
|
70
|
+
code = 'ORDER_NOT_DELETABLE';
|
|
71
|
+
constructor(reason) {
|
|
72
|
+
super(reason === 'invoice'
|
|
73
|
+
? 'Order has an issued invoice and cannot be deleted.'
|
|
74
|
+
: 'Order status does not allow deletion.');
|
|
75
|
+
this.reason = reason;
|
|
76
|
+
this.name = 'OrderNotDeletableError';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
30
79
|
async function purgeExpiredReservations() {
|
|
31
80
|
const db = getShopDb();
|
|
32
81
|
await db
|
|
@@ -361,9 +410,18 @@ export async function updateOrderStatus(orderId, status, opts = {}) {
|
|
|
361
410
|
})
|
|
362
411
|
.where(eq(shopOrdersTable.id, orderId));
|
|
363
412
|
}
|
|
413
|
+
// Auto-restore: a soft-deleted order may only sit in a deletable status. If
|
|
414
|
+
// it transitions out of that set (e.g. a late payment webhook flips a hidden
|
|
415
|
+
// `awaitingPayment` order to `paid`), un-hide it so settled orders are never
|
|
416
|
+
// buried in the trash.
|
|
417
|
+
const autoRestore = order.deletedAt != null && !isOrderDeletable(status);
|
|
364
418
|
await db
|
|
365
419
|
.update(shopOrdersTable)
|
|
366
|
-
.set({
|
|
420
|
+
.set({
|
|
421
|
+
status,
|
|
422
|
+
updatedAt: new Date(),
|
|
423
|
+
...(autoRestore ? { deletedAt: null, deletedBy: null } : {})
|
|
424
|
+
})
|
|
367
425
|
.where(eq(shopOrdersTable.id, orderId));
|
|
368
426
|
await db.insert(shopOrderStatusHistoryTable).values({
|
|
369
427
|
orderId,
|
|
@@ -439,6 +497,14 @@ export async function updateOrderStatus(orderId, status, opts = {}) {
|
|
|
439
497
|
// the guard inside skips deposit orders that still owe a balance.
|
|
440
498
|
if (status === 'paid')
|
|
441
499
|
void maybeIssueInvoiceForOrder(orderId);
|
|
500
|
+
// User-land `onOrderPaid` hook — fires on transition INTO `paid` only
|
|
501
|
+
// (no-op when oldStatus === 'paid'). Errors swallowed so a buggy callback
|
|
502
|
+
// never blocks the webhook / status write.
|
|
503
|
+
if (order.status !== 'paid' && status === 'paid' && shop.onOrderPaid && updated) {
|
|
504
|
+
Promise.resolve(shop.onOrderPaid(updated)).catch((e) => {
|
|
505
|
+
console.error('[onOrderPaid] callback failed', e);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
442
508
|
return updated;
|
|
443
509
|
}
|
|
444
510
|
/**
|
|
@@ -533,7 +599,12 @@ export async function getOrderById(id) {
|
|
|
533
599
|
}
|
|
534
600
|
export async function getOrderByNumber(number) {
|
|
535
601
|
const db = getShopDb();
|
|
536
|
-
|
|
602
|
+
// Customer-facing lookup — a soft-deleted order must not resolve on the
|
|
603
|
+
// storefront. (Admin uses getOrderById, which intentionally ignores deletedAt.)
|
|
604
|
+
const [row] = await db
|
|
605
|
+
.select()
|
|
606
|
+
.from(shopOrdersTable)
|
|
607
|
+
.where(and(eq(shopOrdersTable.number, number), isNull(shopOrdersTable.deletedAt)));
|
|
537
608
|
return row ?? null;
|
|
538
609
|
}
|
|
539
610
|
export async function getOrderItems(orderId) {
|
|
@@ -548,16 +619,28 @@ export async function getOrderStatusHistory(orderId) {
|
|
|
548
619
|
.where(eq(shopOrderStatusHistoryTable.orderId, orderId))
|
|
549
620
|
.orderBy(asc(shopOrderStatusHistoryTable.changedAt));
|
|
550
621
|
}
|
|
551
|
-
|
|
552
|
-
|
|
622
|
+
function escapeLike(value) {
|
|
623
|
+
return value.replace(/[\\%_]/g, (m) => `\\${m}`);
|
|
624
|
+
}
|
|
625
|
+
function buildOrderListConditions(opts) {
|
|
553
626
|
const conditions = [];
|
|
627
|
+
const deleted = opts.deleted ?? 'exclude';
|
|
628
|
+
if (deleted === 'exclude')
|
|
629
|
+
conditions.push(isNull(shopOrdersTable.deletedAt));
|
|
630
|
+
else if (deleted === 'only')
|
|
631
|
+
conditions.push(isNotNull(shopOrdersTable.deletedAt));
|
|
554
632
|
if (opts.status)
|
|
555
633
|
conditions.push(eq(shopOrdersTable.status, opts.status));
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
634
|
+
const search = opts.search?.trim();
|
|
635
|
+
if (search) {
|
|
636
|
+
const pattern = `%${escapeLike(search)}%`;
|
|
637
|
+
conditions.push(or(ilike(shopOrdersTable.number, pattern), ilike(shopOrdersTable.customerEmail, pattern), ilike(shopOrdersTable.customerName, pattern)));
|
|
638
|
+
}
|
|
639
|
+
return conditions;
|
|
640
|
+
}
|
|
641
|
+
export async function listOrders(opts = {}) {
|
|
642
|
+
const db = getShopDb();
|
|
643
|
+
const conditions = buildOrderListConditions(opts);
|
|
561
644
|
const where = conditions.length > 0 ? and(...conditions) : undefined;
|
|
562
645
|
return db
|
|
563
646
|
.select()
|
|
@@ -569,11 +652,7 @@ export async function listOrders(opts = {}) {
|
|
|
569
652
|
}
|
|
570
653
|
export async function countOrders(opts = {}) {
|
|
571
654
|
const db = getShopDb();
|
|
572
|
-
const conditions =
|
|
573
|
-
if (opts.status)
|
|
574
|
-
conditions.push(eq(shopOrdersTable.status, opts.status));
|
|
575
|
-
if (opts.email)
|
|
576
|
-
conditions.push(eq(shopOrdersTable.customerEmail, opts.email));
|
|
655
|
+
const conditions = buildOrderListConditions(opts);
|
|
577
656
|
const where = conditions.length > 0 ? and(...conditions) : undefined;
|
|
578
657
|
const [row] = await db
|
|
579
658
|
.select({ count: sql `count(*)::int` })
|
|
@@ -581,3 +660,53 @@ export async function countOrders(opts = {}) {
|
|
|
581
660
|
.where(where);
|
|
582
661
|
return row?.count ?? 0;
|
|
583
662
|
}
|
|
663
|
+
/**
|
|
664
|
+
* @public
|
|
665
|
+
* Soft-delete an order: hide it from the admin/customer list without removing
|
|
666
|
+
* the row (accounting/audit safety). Idempotent — a no-op on an already-deleted
|
|
667
|
+
* order. Guards on {@link decideOrderDeletion}: throws {@link OrderNotDeletableError}
|
|
668
|
+
* when the status isn't deletable or an issued/sent invoice exists. Releases any
|
|
669
|
+
* active stock reservation immediately so a hidden, abandoned order never locks
|
|
670
|
+
* stock waiting for the TTL.
|
|
671
|
+
*/
|
|
672
|
+
export async function softDeleteOrder(orderId, deletedBy) {
|
|
673
|
+
const db = getShopDb();
|
|
674
|
+
const [order] = await db.select().from(shopOrdersTable).where(eq(shopOrdersTable.id, orderId));
|
|
675
|
+
if (!order)
|
|
676
|
+
throw new Error('Order not found');
|
|
677
|
+
if (order.deletedAt)
|
|
678
|
+
return order;
|
|
679
|
+
const invoice = await getInvoiceByOrderId(orderId);
|
|
680
|
+
const decision = decideOrderDeletion(order.status, invoice);
|
|
681
|
+
if (!decision.ok)
|
|
682
|
+
throw new OrderNotDeletableError(decision.reason);
|
|
683
|
+
// Free held stock right away (no rows when the stock feature is off).
|
|
684
|
+
await db
|
|
685
|
+
.delete(shopStockReservationsTable)
|
|
686
|
+
.where(eq(shopStockReservationsTable.orderId, orderId));
|
|
687
|
+
await db
|
|
688
|
+
.update(shopOrdersTable)
|
|
689
|
+
.set({ deletedAt: new Date(), deletedBy, updatedAt: new Date() })
|
|
690
|
+
.where(eq(shopOrdersTable.id, orderId));
|
|
691
|
+
const [updated] = await db.select().from(shopOrdersTable).where(eq(shopOrdersTable.id, orderId));
|
|
692
|
+
return updated;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* @public
|
|
696
|
+
* Restore a soft-deleted order back to the visible list. Idempotent — a no-op
|
|
697
|
+
* on an order that isn't deleted.
|
|
698
|
+
*/
|
|
699
|
+
export async function restoreOrder(orderId) {
|
|
700
|
+
const db = getShopDb();
|
|
701
|
+
const [order] = await db.select().from(shopOrdersTable).where(eq(shopOrdersTable.id, orderId));
|
|
702
|
+
if (!order)
|
|
703
|
+
throw new Error('Order not found');
|
|
704
|
+
if (!order.deletedAt)
|
|
705
|
+
return order;
|
|
706
|
+
await db
|
|
707
|
+
.update(shopOrdersTable)
|
|
708
|
+
.set({ deletedAt: null, deletedBy: null, updatedAt: new Date() })
|
|
709
|
+
.where(eq(shopOrdersTable.id, orderId));
|
|
710
|
+
const [updated] = await db.select().from(shopOrdersTable).where(eq(shopOrdersTable.id, orderId));
|
|
711
|
+
return updated;
|
|
712
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en"><body style="margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;background:#f5f5f8;color:#1a1a2e;">
|
|
3
|
+
<div style="max-width:560px;margin:0 auto;padding:24px;">
|
|
4
|
+
<div style="background:#fff;border-radius:12px;padding:28px;">
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pl"><body style="margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;background:#f5f5f8;color:#1a1a2e;">
|
|
3
|
+
<div style="max-width:560px;margin:0 auto;padding:24px;">
|
|
4
|
+
<div style="background:#fff;border-radius:12px;padding:28px;">
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<table style="width:100%;border-collapse:collapse;font-size:14px;">
|
|
2
|
+
<thead><tr style="background:#f4f2fa;"><th align="left" style="padding:8px;">Item</th><th style="padding:8px;">Qty</th><th align="right" style="padding:8px;">Total</th></tr></thead>
|
|
3
|
+
<tbody>
|
|
4
|
+
{{#each items}}
|
|
5
|
+
<tr><td style="padding:6px 8px;border-bottom:1px solid #eee;">{{name}}</td><td style="padding:6px 8px;border-bottom:1px solid #eee;text-align:center;">{{qty}}</td><td style="padding:6px 8px;border-bottom:1px solid #eee;text-align:right;">{{lineGross}}</td></tr>
|
|
6
|
+
{{/each}}
|
|
7
|
+
</tbody>
|
|
8
|
+
</table>
|
|
9
|
+
<div style="margin-top:16px;text-align:right;font-size:14px;line-height:1.7;">
|
|
10
|
+
<div>Shipping: <strong>{{order.shippingGross}}</strong></div>
|
|
11
|
+
{{#if order.hasDiscount}}<div>Discount ({{order.couponCode}}): <strong style="color:#5B4A9E;">−{{order.discountAmount}}</strong></div>{{/if}}
|
|
12
|
+
<div style="font-size:16px;">Total (gross): <strong style="color:#5B4A9E;">{{order.totalGross}}</strong></div>
|
|
13
|
+
<div style="color:#8888A0;font-size:12px;">net {{order.totalNet}} · VAT {{order.vatAmount}}</div>
|
|
14
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<table style="width:100%;border-collapse:collapse;font-size:14px;">
|
|
2
|
+
<thead><tr style="background:#f4f2fa;"><th align="left" style="padding:8px;">Pozycja</th><th style="padding:8px;">Ilość</th><th align="right" style="padding:8px;">Suma</th></tr></thead>
|
|
3
|
+
<tbody>
|
|
4
|
+
{{#each items}}
|
|
5
|
+
<tr><td style="padding:6px 8px;border-bottom:1px solid #eee;">{{name}}</td><td style="padding:6px 8px;border-bottom:1px solid #eee;text-align:center;">{{qty}}</td><td style="padding:6px 8px;border-bottom:1px solid #eee;text-align:right;">{{lineGross}}</td></tr>
|
|
6
|
+
{{/each}}
|
|
7
|
+
</tbody>
|
|
8
|
+
</table>
|
|
9
|
+
<div style="margin-top:16px;text-align:right;font-size:14px;line-height:1.7;">
|
|
10
|
+
<div>Wysyłka: <strong>{{order.shippingGross}}</strong></div>
|
|
11
|
+
{{#if order.hasDiscount}}<div>Rabat ({{order.couponCode}}): <strong style="color:#5B4A9E;">−{{order.discountAmount}}</strong></div>{{/if}}
|
|
12
|
+
<div style="font-size:16px;">Razem (brutto): <strong style="color:#5B4A9E;">{{order.totalGross}}</strong></div>
|
|
13
|
+
<div style="color:#8888A0;font-size:12px;">netto {{order.totalNet}} · VAT {{order.vatAmount}}</div>
|
|
14
|
+
</div>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{{#if tracking}}
|
|
2
|
+
<div style="margin-top:20px;padding:16px;background:#F4F2FA;border-radius:10px;font-size:14px;">
|
|
3
|
+
<div style="color:#555566;margin-bottom:4px;">{{tracking.label}}</div>
|
|
4
|
+
<div style="font-family:ui-monospace,monospace;font-weight:700;word-break:break-all;">{{tracking.number}}</div>
|
|
5
|
+
{{#if tracking.url}}<a href="{{tracking.url}}" style="display:inline-block;margin-top:8px;color:#5B4A9E;font-weight:600;text-decoration:none;">{{tracking.linkLabel}}</a>{{/if}}
|
|
6
|
+
</div>
|
|
7
|
+
{{/if}}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{{#if tracking}}
|
|
2
|
+
<div style="margin-top:20px;padding:16px;background:#F4F2FA;border-radius:10px;font-size:14px;">
|
|
3
|
+
<div style="color:#555566;margin-bottom:4px;">{{tracking.label}}</div>
|
|
4
|
+
<div style="font-family:ui-monospace,monospace;font-weight:700;word-break:break-all;">{{tracking.number}}</div>
|
|
5
|
+
{{#if tracking.url}}<a href="{{tracking.url}}" style="display:inline-block;margin-top:8px;color:#5B4A9E;font-weight:600;text-decoration:none;">{{tracking.linkLabel}}</a>{{/if}}
|
|
6
|
+
</div>
|
|
7
|
+
{{/if}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Awaiting payment</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Your order {{order.number}} has been placed. Waiting for payment per the chosen method.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
6
|
+
{{> footer}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Czekamy na płatność</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Otrzymaliśmy zamówienie {{order.number}}. Czekamy na płatność zgodnie z wybraną metodą.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
6
|
+
{{> footer}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Order cancelled</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Order {{order.number}} has been cancelled.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
6
|
+
{{> footer}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Zamówienie anulowane</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Zamówienie {{order.number}} zostało anulowane.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
6
|
+
{{> footer}}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en"><body style="margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;background:#f5f5f8;color:#1a1a2e;">
|
|
3
|
+
<div style="max-width:560px;margin:0 auto;padding:24px;">
|
|
4
|
+
<div style="background:#fff;border-radius:12px;padding:28px;">
|
|
5
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;color:#C4893A;">Low stock alert</h1>
|
|
6
|
+
<p style="margin:0 0 16px;color:#555566;line-height:1.5;">
|
|
7
|
+
Product <strong>{{product.title}}</strong>{{#if product.variantSku}} (SKU {{product.variantSku}}){{/if}}
|
|
8
|
+
has only <strong>{{product.stock}}</strong> units left in stock (alert threshold: {{product.threshold}}).
|
|
9
|
+
</p>
|
|
10
|
+
<p style="margin:0;color:#8888A0;font-size:13px;">Restock in the admin panel to avoid running out.</p>
|
|
11
|
+
</div>
|
|
12
|
+
<p style="text-align:center;color:#8888A0;font-size:12px;margin-top:16px;">AriaCMS · {{shop.adminEmail}}</p>
|
|
13
|
+
</div>
|
|
14
|
+
</body></html>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pl"><body style="margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;background:#f5f5f8;color:#1a1a2e;">
|
|
3
|
+
<div style="max-width:560px;margin:0 auto;padding:24px;">
|
|
4
|
+
<div style="background:#fff;border-radius:12px;padding:28px;">
|
|
5
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;color:#C4893A;">Niski stan magazynowy</h1>
|
|
6
|
+
<p style="margin:0 0 16px;color:#555566;line-height:1.5;">
|
|
7
|
+
Produkt <strong>{{product.title}}</strong>{{#if product.variantSku}} (SKU {{product.variantSku}}){{/if}}
|
|
8
|
+
ma już tylko <strong>{{product.stock}}</strong> sztuk w magazynie (próg alertu: {{product.threshold}}).
|
|
9
|
+
</p>
|
|
10
|
+
<p style="margin:0;color:#8888A0;font-size:13px;">Uzupełnij stan w panelu admina, żeby uniknąć przerwy w sprzedaży.</p>
|
|
11
|
+
</div>
|
|
12
|
+
<p style="text-align:center;color:#8888A0;font-size:12px;margin-top:16px;">AriaCMS · {{shop.adminEmail}}</p>
|
|
13
|
+
</div>
|
|
14
|
+
</body></html>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Order completed</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Order {{order.number}} is complete. Thanks for shopping with us!</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
6
|
+
{{> footer}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Zamówienie zrealizowane</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Zamówienie {{order.number}} zostało zakończone. Dziękujemy za zakupy!</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
6
|
+
{{> footer}}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Order {{order.number}}</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Thanks! We received your order and will process it shortly.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{> tracking}}
|
|
6
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
7
|
+
{{> footer}}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Zamówienie {{order.number}}</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Dziękujemy! Otrzymaliśmy Twoje zamówienie i wkrótce je przetworzymy.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{> tracking}}
|
|
6
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
7
|
+
{{> footer}}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Payment received</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Payment for order {{order.number}} has been received. Your order is moving to fulfilment.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{> tracking}}
|
|
6
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
7
|
+
{{> footer}}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Płatność zaksięgowana</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Płatność za zamówienie {{order.number}} została zaksięgowana. Przekazujemy zamówienie do realizacji.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{> tracking}}
|
|
6
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
7
|
+
{{> footer}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Payment rejected</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Payment for order {{order.number}} was not received. Please contact us if you have questions.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
6
|
+
{{> footer}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Płatność odrzucona</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Płatność za zamówienie {{order.number}} nie została zaksięgowana. Skontaktuj się z nami w razie pytań.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
6
|
+
{{> footer}}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Preparing order</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Your order {{order.number}} is being prepared.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{> tracking}}
|
|
6
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
7
|
+
{{> footer}}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Zamówienie w przygotowaniu</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Twoje zamówienie {{order.number}} jest pakowane.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{> tracking}}
|
|
6
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
7
|
+
{{> footer}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Order refunded</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Your refund for order {{order.number}} has been issued. It should reach your account within a few business days.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
6
|
+
{{> footer}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Zwrot zaksięgowany</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Środki za zamówienie {{order.number}} zostały zwrócone. Powinny pojawić się na koncie w ciągu kilku dni roboczych.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
6
|
+
{{> footer}}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Order shipped</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Order {{order.number}} has been shipped.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{> tracking}}
|
|
6
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
7
|
+
{{> footer}}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{{> header}}
|
|
2
|
+
<h1 style="font-size:20px;font-weight:800;margin:0 0 8px;">Zamówienie wysłane</h1>
|
|
3
|
+
<p style="margin:0 0 20px;color:#555566;line-height:1.5;">Zamówienie {{order.number}} zostało wysłane.</p>
|
|
4
|
+
{{> items}}
|
|
5
|
+
{{> tracking}}
|
|
6
|
+
{{#if viewUrl}}<div style="margin-top:24px;text-align:center;"><a href="{{viewUrl}}" style="display:inline-block;background:#5B4A9E;color:#fff;text-decoration:none;padding:10px 18px;border-radius:8px;font-weight:600;">{{viewLinkLabel}}</a></div>{{/if}}
|
|
7
|
+
{{> footer}}
|
package/dist/shop/types.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { Language } from '../types/languages.js';
|
|
2
|
+
import type { shopOrdersTable } from '../db-postgres/schema/shop/order.js';
|
|
2
3
|
export type Currency = 'PLN';
|
|
4
|
+
/**
|
|
5
|
+
* Public row type for a single shop order — mirrors the Drizzle `shop_orders`
|
|
6
|
+
* table. Use this in `ShopConfig.onOrderPaid` and similar hooks.
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export type Order = typeof shopOrdersTable.$inferSelect;
|
|
3
10
|
export type OrderStatus = 'new' | 'awaitingPayment' | 'paid' | 'preparing' | 'sent' | 'done' | 'cancelled' | 'paymentRejected' | 'refunded';
|
|
4
11
|
export type I18nText = {
|
|
5
12
|
[lang: string]: string;
|
|
@@ -27,11 +34,28 @@ export interface PaymentCreateContext {
|
|
|
27
34
|
customerIp?: string;
|
|
28
35
|
language?: string | null;
|
|
29
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Result of a non-invasive integration connectivity check. Returned by an
|
|
39
|
+
* adapter's optional `healthCheck()` — `ok` reflects reachability + auth, and
|
|
40
|
+
* `message` carries a human-readable detail (error text on failure, optional
|
|
41
|
+
* note on success). MUST NOT leak secrets.
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
44
|
+
export interface IntegrationHealthResult {
|
|
45
|
+
ok: boolean;
|
|
46
|
+
message?: string;
|
|
47
|
+
}
|
|
30
48
|
export interface PaymentAdapter {
|
|
31
49
|
id: string;
|
|
32
50
|
label: I18nText;
|
|
33
51
|
createPayment(order: OrderRef, ctx?: PaymentCreateContext): Promise<PaymentCreateResult>;
|
|
34
52
|
handleWebhook?(req: Request): Promise<PaymentEvent>;
|
|
53
|
+
/**
|
|
54
|
+
* Non-invasive connectivity check (e.g. an OAuth token fetch). Optional —
|
|
55
|
+
* adapters omitting this are reported as "health-check unsupported". MUST
|
|
56
|
+
* NOT create real transactions.
|
|
57
|
+
*/
|
|
58
|
+
healthCheck?(): Promise<IntegrationHealthResult>;
|
|
35
59
|
/**
|
|
36
60
|
* Poll the provider for the latest payment status. Used as a fallback
|
|
37
61
|
* when a webhook is lost. `providerRef` is the external id the adapter
|
|
@@ -323,6 +347,12 @@ export interface InvoicingAdapter {
|
|
|
323
347
|
issueWhen?: InvoiceIssuePolicy;
|
|
324
348
|
createInvoice(payload: InvoicePayload, ctx?: InvoiceContext): Promise<InvoiceCreateResult>;
|
|
325
349
|
send?(externalId: string, ctx?: InvoiceContext): Promise<void>;
|
|
350
|
+
/**
|
|
351
|
+
* Non-invasive connectivity check (e.g. a read-only account lookup).
|
|
352
|
+
* Optional — adapters omitting this are reported as "health-check
|
|
353
|
+
* unsupported". MUST NOT issue real invoices.
|
|
354
|
+
*/
|
|
355
|
+
healthCheck?(): Promise<IntegrationHealthResult>;
|
|
326
356
|
}
|
|
327
357
|
export interface ShopConfig {
|
|
328
358
|
currency: Currency;
|
|
@@ -370,6 +400,39 @@ export interface ShopConfig {
|
|
|
370
400
|
* @public
|
|
371
401
|
*/
|
|
372
402
|
variantExpiry?: VariantExpiryConfig;
|
|
403
|
+
/**
|
|
404
|
+
* Optional shop email templates configuration. Templates are looked up
|
|
405
|
+
* first in the consumer project's `dir` (override), then in the bundled
|
|
406
|
+
* defaults. Files are named `<name>.<lang>.html` (per-lang) with a
|
|
407
|
+
* lang-agnostic project fallback (`<name>.html`). Handlebars syntax with
|
|
408
|
+
* partials in `_partials/<name>.<lang>.html`. CMS singletons accessible
|
|
409
|
+
* via `{{cms.<slug>.<field>}}` tokens (auto-fetched per template).
|
|
410
|
+
* @public
|
|
411
|
+
*/
|
|
412
|
+
emailTemplates?: {
|
|
413
|
+
/** Project directory holding overrides. Default `'src/emails/shop'`. */
|
|
414
|
+
dir?: string;
|
|
415
|
+
/** Throw on missing tokens / unknown CMS slugs. Default `false` (lenient → ""). */
|
|
416
|
+
strict?: boolean;
|
|
417
|
+
};
|
|
418
|
+
/**
|
|
419
|
+
* Optional callback fired after an order transitions to `paid`.
|
|
420
|
+
*
|
|
421
|
+
* Triggered inside `updateOrderStatus` only when the previous status was
|
|
422
|
+
* NOT `paid` and the new status is `paid` (transition guard) — so a manual
|
|
423
|
+
* `cancelled → paid` flip will fire, but a no-op `paid → paid` call will not.
|
|
424
|
+
* Runs AFTER `sendOrderStatusEmail` and `maybeIssueInvoiceForOrder`; receives
|
|
425
|
+
* the fresh order row (post-update snapshot, with the latest `consents` etc).
|
|
426
|
+
*
|
|
427
|
+
* Errors are caught and logged — the callback never blocks the webhook,
|
|
428
|
+
* the status write, or the invoice. Use for fire-and-forget side effects
|
|
429
|
+
* (admin notifications, CRM sync, slack ping). If you need strict
|
|
430
|
+
* once-per-order semantics across refund→repay cycles, persist your own
|
|
431
|
+
* idempotency flag in userland (the transition guard alone fires once per
|
|
432
|
+
* `paid` entry, which may not match "lifetime once" for refunded orders).
|
|
433
|
+
* @public
|
|
434
|
+
*/
|
|
435
|
+
onOrderPaid?: (order: Order) => Promise<void> | void;
|
|
373
436
|
}
|
|
374
437
|
export interface ResolvedShopConfig extends Omit<ShopConfig, 'variantLabel' | 'variantExpiry' | 'invoicing'> {
|
|
375
438
|
features: Required<ShopFeatures>;
|
|
@@ -12,4 +12,3 @@ export { enableHybridEditing } from './components/hybrid-context.js';
|
|
|
12
12
|
export { setPreferMp4 } from './components/video-context.js';
|
|
13
13
|
export { getLink, isImageFieldData, isVideoFieldData } from './utils/index.js';
|
|
14
14
|
export { structuredToHtml } from '../core/fields/structuredToHtml.js';
|
|
15
|
-
export { extractBlocks, extractInlineBlocks, extractText, extractMediaRefs } from '../core/server/fields/queryStructuredContent.js';
|
package/dist/sveltekit/index.js
CHANGED
|
@@ -12,4 +12,3 @@ export { enableHybridEditing } from './components/hybrid-context.js';
|
|
|
12
12
|
export { setPreferMp4 } from './components/video-context.js';
|
|
13
13
|
export { getLink, isImageFieldData, isVideoFieldData } from './utils/index.js';
|
|
14
14
|
export { structuredToHtml } from '../core/fields/structuredToHtml.js';
|
|
15
|
-
export { extractBlocks, extractInlineBlocks, extractText, extractMediaRefs } from '../core/server/fields/queryStructuredContent.js';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { includioCMS } from './handle.js';
|
|
2
2
|
export { cmsLayoutLoad } from './layout.js';
|
|
3
3
|
export { resolveEntry, resolveEntries, countEntries, type ResolveEntryOptions, type ResolveEntriesOptions, type CountEntriesOptions, type PopulateConfig, type ResolveStatus } from '../../core/server/entries/operations/resolveEntry.js';
|
|
4
|
+
export { getMailer, sendMail } from '../../core/cms.js';
|
|
4
5
|
export { createFormSubmission } from '../../core/server/forms/submissions/operations/create.js';
|
|
5
6
|
export { parseFormDataForSubmission } from '../../core/server/forms/submissions/utils/parseMultipart.js';
|
|
6
7
|
export { createConsentLog } from '../../core/server/consentLogs/operations/create.js';
|
|
@@ -8,3 +9,4 @@ export { getPreviewEntry } from './preview.js';
|
|
|
8
9
|
export { createRestApiHandler } from '../../admin/api/rest/handler.js';
|
|
9
10
|
export { generateApiKey } from '../../admin/api/rest/middleware/generateApiKey.js';
|
|
10
11
|
export { createAdminApiHandler } from '../../admin/api/handler.js';
|
|
12
|
+
export { extractBlocks, extractInlineBlocks, extractText, extractMediaRefs } from '../../core/server/fields/queryStructuredContent.js';
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export { includioCMS } from './handle.js';
|
|
2
2
|
export { cmsLayoutLoad } from './layout.js';
|
|
3
3
|
export { resolveEntry, resolveEntries, countEntries } from '../../core/server/entries/operations/resolveEntry.js';
|
|
4
|
+
// Moved from `includio-cms/core` (0.34.x): keep on the server entry so the
|
|
5
|
+
// client-safe `core` barrel (`resolveSeo`) no longer drags in the server graph.
|
|
6
|
+
export { getMailer, sendMail } from '../../core/cms.js';
|
|
4
7
|
export { createFormSubmission } from '../../core/server/forms/submissions/operations/create.js';
|
|
5
8
|
export { parseFormDataForSubmission } from '../../core/server/forms/submissions/utils/parseMultipart.js';
|
|
6
9
|
export { createConsentLog } from '../../core/server/consentLogs/operations/create.js';
|
|
@@ -10,3 +13,4 @@ export { createRestApiHandler } from '../../admin/api/rest/handler.js';
|
|
|
10
13
|
export { generateApiKey } from '../../admin/api/rest/middleware/generateApiKey.js';
|
|
11
14
|
// Folded from `./admin/api/handler` (dropped as separate export in 0.26.1)
|
|
12
15
|
export { createAdminApiHandler } from '../../admin/api/handler.js';
|
|
16
|
+
export { extractBlocks, extractInlineBlocks, extractText, extractMediaRefs } from '../../core/server/fields/queryStructuredContent.js';
|