@infuro/cms-core 1.0.34 → 1.0.35

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 (158) hide show
  1. package/README.md +13 -0
  2. package/dist/OrderTrackPage-W3J6WIG2.js +426 -0
  3. package/dist/OrderTrackPage-Z6FCJQYY.cjs +428 -0
  4. package/dist/admin.cjs +34329 -20923
  5. package/dist/admin.d.cts +186 -9
  6. package/dist/admin.d.ts +186 -9
  7. package/dist/admin.js +34140 -20829
  8. package/dist/api.cjs +120 -17515
  9. package/dist/api.d.cts +633 -3
  10. package/dist/api.d.ts +633 -3
  11. package/dist/api.js +14 -17528
  12. package/dist/auth.cjs +192 -1034
  13. package/dist/auth.d.cts +194 -51
  14. package/dist/auth.d.ts +194 -51
  15. package/dist/auth.js +4 -966
  16. package/dist/chunk-2XE4X74C.js +29602 -0
  17. package/dist/chunk-3DZRIZOD.cjs +52 -0
  18. package/dist/chunk-3KPNTQBV.cjs +620 -0
  19. package/dist/chunk-46GKADYE.cjs +84 -0
  20. package/dist/chunk-4TRPKBKO.js +589 -0
  21. package/dist/chunk-5ELSW2UP.js +150 -0
  22. package/dist/chunk-6KPQWRFW.js +171 -0
  23. package/dist/chunk-7NMT3GBR.cjs +29 -0
  24. package/dist/chunk-7YIBMQDR.cjs +86 -0
  25. package/dist/chunk-BGJAV67S.cjs +29710 -0
  26. package/dist/chunk-CNTSLED2.cjs +559 -0
  27. package/dist/chunk-CRFV5WJK.js +16 -0
  28. package/dist/chunk-D5FBNKQM.cjs +104 -0
  29. package/dist/chunk-EF2JBPDM.js +114 -0
  30. package/dist/chunk-EQPITOP6.js +543 -0
  31. package/dist/chunk-GO7PPYNU.js +76 -0
  32. package/dist/chunk-HF3L2GIT.cjs +194 -0
  33. package/dist/chunk-J7XHLOLO.js +830 -0
  34. package/dist/chunk-JBRR5UYL.js +87 -0
  35. package/dist/chunk-JC6DLWTE.js +21 -0
  36. package/dist/chunk-JHF4YYRC.cjs +188 -0
  37. package/dist/chunk-JXF23MPG.js +25 -0
  38. package/dist/chunk-MQBT33IV.js +48 -0
  39. package/dist/chunk-N44BJZ4C.cjs +123 -0
  40. package/dist/chunk-OOTLNH63.cjs +18 -0
  41. package/dist/chunk-PAMVINEX.cjs +577 -0
  42. package/dist/chunk-PNOKNSG2.cjs +222 -0
  43. package/dist/chunk-QM733NUK.js +186 -0
  44. package/dist/chunk-R73BKDRO.cjs +90 -0
  45. package/dist/chunk-SHUYVCID.js +4 -0
  46. package/dist/chunk-SYBOCAWB.js +97 -0
  47. package/dist/chunk-TDV6EJHV.js +192 -0
  48. package/dist/chunk-TFUOHI37.cjs +839 -0
  49. package/dist/chunk-TIAEXJLI.cjs +182 -0
  50. package/dist/chunk-UPLVMVRX.cjs +23 -0
  51. package/dist/chunk-USNT2KNT.cjs +6 -0
  52. package/dist/chunk-WEMDMVHQ.js +216 -0
  53. package/dist/chunk-XDSNZUW7.js +564 -0
  54. package/dist/chunk-XQ3QUHWR.cjs +83 -0
  55. package/dist/chunk-XW5ATPRW.cjs +155 -0
  56. package/dist/chunk-ZQ4X6OAU.js +82 -0
  57. package/dist/chunk-ZSVCP6N3.js +83 -0
  58. package/dist/cli.cjs +103 -84
  59. package/dist/cli.js +62 -27
  60. package/dist/email-FKR4FXFI.js +5 -0
  61. package/dist/email-KVKVPNCL.cjs +58 -0
  62. package/dist/email-queue-CGZBVVAS.js +2 -0
  63. package/dist/email-queue-TPZWLTIV.cjs +23 -0
  64. package/dist/erp-config-enabled-CUMQ6N2V.cjs +11 -0
  65. package/dist/erp-config-enabled-T37X7T34.js +2 -0
  66. package/dist/erp-order-invoice-3YISSOWW.js +3 -0
  67. package/dist/erp-order-invoice-ZR5F2KTA.cjs +12 -0
  68. package/dist/event-order-defaults-6E543227.js +2 -0
  69. package/dist/event-order-defaults-DUGDVGJI.cjs +39 -0
  70. package/dist/event-order-notifications-5GSAOYVS.cjs +27 -0
  71. package/dist/event-order-notifications-IIIF23D5.js +10 -0
  72. package/dist/hooks.cjs +259 -132
  73. package/dist/hooks.d.cts +42 -1
  74. package/dist/hooks.d.ts +42 -1
  75. package/dist/hooks.js +234 -91
  76. package/dist/index.cjs +3884 -22307
  77. package/dist/index.d.cts +2203 -772
  78. package/dist/index.d.ts +2203 -772
  79. package/dist/index.js +2871 -22064
  80. package/dist/interpolate-WAKPN56R.cjs +11 -0
  81. package/dist/interpolate-WDUWDF3I.js +2 -0
  82. package/dist/layout-36UFTHC5.cjs +11 -0
  83. package/dist/layout-AOBN26XG.js +2 -0
  84. package/dist/migrations/1779400000000-ConvertStatusToEnum.ts +142 -0
  85. package/dist/migrations/1779400000000-SeedDefaultCurrencies.ts +33 -0
  86. package/dist/migrations/1779500000000-VendorRolesAndPermissions.ts +102 -0
  87. package/dist/migrations/1779600000000-VendorRegistrationProfile.ts +52 -0
  88. package/dist/migrations/1779700000000-CollectionIsCatalog.ts +22 -0
  89. package/dist/migrations/1779710000000-CollectionPlatformCatalog.ts +60 -0
  90. package/dist/migrations/1779720000000-ProductCategoryPlatformCatalog.ts +157 -0
  91. package/dist/migrations/1779800000000-EventsAndEventProducts.ts +173 -0
  92. package/dist/migrations/1779810000000-BackfillVendorRoleEventsPermissions.ts +38 -0
  93. package/dist/migrations/1779820000000-FixProductsCategoryForeignKey.ts +70 -0
  94. package/dist/migrations/1779830000000-ProductTitleColumn.ts +32 -0
  95. package/dist/migrations/1779840000000-EventAboutAndVisibilityFields.ts +34 -0
  96. package/dist/migrations/1779850000000-EventCityCountrySplit.ts +43 -0
  97. package/dist/migrations/1779860000000-CreateCombos.ts +140 -0
  98. package/dist/migrations/1780100000000-OrderAttendeesEventId.ts +55 -0
  99. package/dist/migrations/1780110000000-EventContactFormId.ts +49 -0
  100. package/dist/migrations/1780130000000-ProductVariants.ts +54 -0
  101. package/dist/migrations/1780140000000-EventManagementEnhancements.ts +44 -0
  102. package/dist/migrations/1780160000000-EventRegistrationGstProductContactForm.ts +79 -0
  103. package/dist/migrations/1780170000000-ComboCurrencyPrices.ts +20 -0
  104. package/dist/migrations/1781110000000-VendorOwnerCombosPermission.ts +38 -0
  105. package/dist/migrations/1781120000000-RefundPoliciesAndRequests.ts +225 -0
  106. package/dist/migrations/1781130000000-CreateAttendees.ts +44 -0
  107. package/dist/migrations/1781140000000-EventOrderNotifications.ts +29 -0
  108. package/dist/migrations/1781150000000-EventNamedLists.ts +28 -0
  109. package/dist/migrations/1781160000000-RefundPolicyEventTiers.ts +27 -0
  110. package/dist/migrations/1781170000000-EventRefundPolicy.ts +42 -0
  111. package/dist/migrations/1781180000000-RemoveCartItemsUniqueConstraint.ts +15 -0
  112. package/dist/migrations/1781190000000-RenameProductTaxesToProductConfig.ts +84 -0
  113. package/dist/migrations/1781200000000-VendorOwnerProductConfigPermissions.ts +51 -0
  114. package/dist/migrations/1781210000000-VendorRoleProductConfigPermissions.ts +46 -0
  115. package/dist/migrations/helpers/vendor-role-seed.ts +243 -0
  116. package/dist/paid-order-erp-3USEG6WL.js +3 -0
  117. package/dist/paid-order-erp-FSQ3GSXE.cjs +12 -0
  118. package/dist/pg-boss-service-4XF2TQ2E.js +2 -0
  119. package/dist/pg-boss-service-L6MTF4EQ.cjs +15 -0
  120. package/dist/razorpay-KLO2KNCB.js +49 -0
  121. package/dist/razorpay-U23LGY72.cjs +56 -0
  122. package/dist/rbac-debug-db-D7ME7QUY.cjs +35 -0
  123. package/dist/rbac-debug-db-MTCTVNNM.js +33 -0
  124. package/dist/sms-defaults-2NL24H57.js +2 -0
  125. package/dist/sms-defaults-KCOPKQBA.cjs +19 -0
  126. package/dist/stripe-4FD27QVJ.js +51 -0
  127. package/dist/stripe-VU6OEG2D.cjs +57 -0
  128. package/dist/theme.cjs +31 -59
  129. package/dist/theme.d.cts +78 -3
  130. package/dist/theme.d.ts +78 -3
  131. package/dist/theme.js +23 -20
  132. package/dist/vendor-invite-status-77O7ZQ3U.cjs +19 -0
  133. package/dist/vendor-invite-status-PWJ3DE76.js +2 -0
  134. package/dist/whatsapp-queue-3DXA4MXU.js +2 -0
  135. package/dist/whatsapp-queue-KG65MXD5.cjs +15 -0
  136. package/package.json +147 -142
  137. package/dist/admin.cjs.map +0 -1
  138. package/dist/admin.js.map +0 -1
  139. package/dist/api.cjs.map +0 -1
  140. package/dist/api.js.map +0 -1
  141. package/dist/auth.cjs.map +0 -1
  142. package/dist/auth.js.map +0 -1
  143. package/dist/cli.cjs.map +0 -1
  144. package/dist/cli.js.map +0 -1
  145. package/dist/config-DJ5CmQvS.d.cts +0 -8
  146. package/dist/config-DJ5CmQvS.d.ts +0 -8
  147. package/dist/hooks.cjs.map +0 -1
  148. package/dist/hooks.js.map +0 -1
  149. package/dist/index-Bf5GO8fu.d.cts +0 -583
  150. package/dist/index-DOiJuMQA.d.ts +0 -583
  151. package/dist/index.cjs.map +0 -1
  152. package/dist/index.js.map +0 -1
  153. package/dist/theme.cjs.map +0 -1
  154. package/dist/theme.js.map +0 -1
  155. package/dist/types-DCVaXlPV.d.cts +0 -78
  156. package/dist/types-DCVaXlPV.d.ts +0 -78
  157. package/dist/vendor-scope-DTYhJk_I.d.cts +0 -129
  158. package/dist/vendor-scope-DTYhJk_I.d.ts +0 -129
package/README.md CHANGED
@@ -708,6 +708,19 @@ The CSS variables are injected by the admin layout at runtime. Your website's ow
708
708
  | `GOOGLE_ANALYTICS_PRIVATE_KEY` | If analytics | GA service account key |
709
709
  | `GOOGLE_ANALYTICS_CLIENT_EMAIL` | If analytics | GA service account email |
710
710
  | `GOOGLE_ANALYTICS_VIEW_ID` | If analytics | GA property/view ID |
711
+ | `WHATSAPP_ACCESS_TOKEN` | If WhatsApp notifications | Meta Cloud API permanent/system user token |
712
+ | `WHATSAPP_PHONE_NUMBER_ID` | If WhatsApp notifications | WhatsApp Business phone number ID from Meta developer console |
713
+ | `WHATSAPP_API_VERSION` | No | Graph API version (default `v21.0`) |
714
+
715
+ ### Event order notifications
716
+
717
+ When an order is created (storefront checkout or admin **Place order**) and it includes products linked to an event via **event_products**, the core can notify the customer on enabled channels:
718
+
719
+ 1. **Platform admin** — Plugins → **Email** and **WhatsApp**: configure global templates (`GET/PUT /api/message-templates/event-order`). WhatsApp requires a [Meta-approved template](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates) name matching `externalTemplateRef` (default `event_order_placed`).
720
+ 2. **Vendor** — Event edit → **Order notifications**: enable notifications, pick **Email** / **WhatsApp**, optionally override templates. API: `GET/PUT /api/events/:id/notifications`, test via `POST /api/events/:id/notifications/test`.
721
+ 3. **Consumer app** — Register `whatsappPlugin()` and `emailPlugin()` in `getCms()`, plus optional `queuePlugin` for async sends. Call `dispatchEventOrderNotifications(orderId, deps)` is wired automatically from storefront checkout and admin order create.
722
+
723
+ Template variables: `{{eventName}}`, `{{orderNumber}}`, `{{customerName}}`, `{{customerEmail}}`, `{{customerPhone}}`, `{{orderTotal}}`, `{{currency}}`, `{{venue}}`, `{{startDate}}`, `{{productNames}}`.
711
724
 
712
725
  ## Development
713
726
 
@@ -0,0 +1,426 @@
1
+ import { __name } from './chunk-SHUYVCID.js';
2
+
3
+ // src/admin/pages/OrderTrackPage.ts
4
+ function renderOrderTrackingPage(order) {
5
+ const contact = order.contact;
6
+ const items = order.items ?? [];
7
+ const currency = String(order.currency ?? "INR");
8
+ function formatMoney(amount) {
9
+ return new Intl.NumberFormat("en-IN", {
10
+ style: "currency",
11
+ currency,
12
+ minimumFractionDigits: 2
13
+ }).format(Number(amount));
14
+ }
15
+ __name(formatMoney, "formatMoney");
16
+ function formatDate(dateStr) {
17
+ return new Date(dateStr).toLocaleDateString("en-IN", {
18
+ year: "numeric",
19
+ month: "long",
20
+ day: "numeric"
21
+ });
22
+ }
23
+ __name(formatDate, "formatDate");
24
+ const statusConfig = {
25
+ pending: {
26
+ label: "Order Placed",
27
+ color: "#f59e0b",
28
+ step: 1
29
+ },
30
+ confirmed: {
31
+ label: "Confirmed",
32
+ color: "#3b82f6",
33
+ step: 2
34
+ },
35
+ processing: {
36
+ label: "Processing",
37
+ color: "#8b5cf6",
38
+ step: 3
39
+ },
40
+ completed: {
41
+ label: "Completed",
42
+ color: "#10b981",
43
+ step: 4
44
+ },
45
+ cancelled: {
46
+ label: "Cancelled",
47
+ color: "#ef4444",
48
+ step: 0
49
+ }
50
+ };
51
+ const status = String(order.status ?? "pending");
52
+ const sc = statusConfig[status] ?? {
53
+ label: status,
54
+ color: "#6b7280",
55
+ step: 1
56
+ };
57
+ const isCancelled = status === "cancelled";
58
+ const steps = [
59
+ {
60
+ label: "Order Placed",
61
+ icon: "\u{1F4CB}"
62
+ },
63
+ {
64
+ label: "Confirmed",
65
+ icon: "\u2705"
66
+ },
67
+ {
68
+ label: "Processing",
69
+ icon: "\u2699\uFE0F"
70
+ },
71
+ {
72
+ label: "Completed",
73
+ icon: "\u{1F389}"
74
+ }
75
+ ];
76
+ const stepsHtml = isCancelled ? `<div class="cancelled-banner">Order Cancelled</div>` : steps.map((s, i) => {
77
+ const stepNum = i + 1;
78
+ const isActive = sc.step === stepNum;
79
+ const isDone = sc.step > stepNum;
80
+ return `
81
+ <div class="step ${isDone ? "done" : ""} ${isActive ? "active" : ""}">
82
+ <div class="step-circle">${isDone ? "\u2713" : s.icon}</div>
83
+ <div class="step-label">${s.label}</div>
84
+ </div>
85
+ ${stepNum < 4 ? `<div class="step-line ${sc.step > stepNum ? "done" : ""}"></div>` : ""}
86
+ `;
87
+ }).join("");
88
+ const itemsHtml = items.map((item) => {
89
+ const name = item.product?.collection?.name ?? item.product?.name ?? `Product #${item.id}`;
90
+ const sku = item.product?.sku ?? "\u2014";
91
+ return `
92
+ <div class="item-row">
93
+ <div class="item-avatar">${name.charAt(0).toUpperCase()}</div>
94
+ <div class="item-info">
95
+ <div class="item-name">${name}</div>
96
+ <div class="item-sku">SKU: ${sku}</div>
97
+ </div>
98
+ <div class="item-qty">\xD7${item.quantity}</div>
99
+ <div class="item-price">${formatMoney(Number(item.unitPrice))}</div>
100
+ </div>
101
+ `;
102
+ }).join("");
103
+ const discountRow = Number(order.discount) > 0 ? `<div class="summary-row discount"><span>Discount</span><span>\u2212${formatMoney(Number(order.discount))}</span></div>` : "";
104
+ const taxRow = Number(order.tax) > 0 ? `<div class="summary-row"><span>Tax</span><span>${formatMoney(Number(order.tax))}</span></div>` : "";
105
+ return `<!DOCTYPE html>
106
+ <html lang="en">
107
+ <head>
108
+ <meta charset="UTF-8">
109
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
110
+ <title>Order ${order.orderNumber} \u2014 Tracking</title>
111
+ <link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&family=DM+Sans:wght@400;500;600&display=swap" rel="stylesheet">
112
+ <style>
113
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
114
+
115
+ :root {
116
+ --accent: ${sc.color};
117
+ --bg: #f8f7f4;
118
+ --card: #ffffff;
119
+ --text: #1a1a2e;
120
+ --muted: #6b7280;
121
+ --border: #e5e7eb;
122
+ --radius: 16px;
123
+ }
124
+
125
+ body {
126
+ font-family: 'DM Sans', sans-serif;
127
+ background: var(--bg);
128
+ color: var(--text);
129
+ min-height: 100vh;
130
+ padding: 24px 16px 48px;
131
+ }
132
+
133
+ .container { max-width: 640px; margin: 0 auto; }
134
+
135
+ /* Header */
136
+ .header {
137
+ text-align: center;
138
+ margin-bottom: 32px;
139
+ animation: fadeDown 0.5s ease both;
140
+ }
141
+ .header-eyebrow {
142
+ font-size: 12px;
143
+ font-weight: 600;
144
+ letter-spacing: 0.15em;
145
+ text-transform: uppercase;
146
+ color: var(--accent);
147
+ margin-bottom: 8px;
148
+ }
149
+ .header h1 {
150
+ font-family: 'Playfair Display', serif;
151
+ font-size: clamp(26px, 6vw, 36px);
152
+ color: var(--text);
153
+ line-height: 1.2;
154
+ }
155
+ .header-meta {
156
+ font-size: 13px;
157
+ color: var(--muted);
158
+ margin-top: 6px;
159
+ }
160
+
161
+ /* Status badge */
162
+ .status-badge {
163
+ display: inline-flex;
164
+ align-items: center;
165
+ gap: 6px;
166
+ background: color-mix(in srgb, var(--accent) 12%, transparent);
167
+ color: var(--accent);
168
+ border: 1.5px solid color-mix(in srgb, var(--accent) 30%, transparent);
169
+ border-radius: 100px;
170
+ padding: 5px 14px;
171
+ font-size: 13px;
172
+ font-weight: 600;
173
+ margin-top: 14px;
174
+ }
175
+ .status-dot {
176
+ width: 7px; height: 7px;
177
+ border-radius: 50%;
178
+ background: var(--accent);
179
+ animation: pulse 2s ease infinite;
180
+ }
181
+
182
+ /* Card */
183
+ .card {
184
+ background: var(--card);
185
+ border-radius: var(--radius);
186
+ border: 1px solid var(--border);
187
+ overflow: hidden;
188
+ margin-bottom: 16px;
189
+ animation: fadeUp 0.5s ease both;
190
+ }
191
+ .card:nth-child(2) { animation-delay: 0.08s; }
192
+ .card:nth-child(3) { animation-delay: 0.16s; }
193
+ .card:nth-child(4) { animation-delay: 0.24s; }
194
+
195
+ .card-header {
196
+ display: flex; align-items: center; gap: 10px;
197
+ padding: 16px 20px;
198
+ border-bottom: 1px solid var(--border);
199
+ background: #fafafa;
200
+ }
201
+ .card-header-icon {
202
+ width: 32px; height: 32px;
203
+ border-radius: 8px;
204
+ background: color-mix(in srgb, var(--accent) 12%, transparent);
205
+ display: flex; align-items: center; justify-content: center;
206
+ font-size: 15px;
207
+ }
208
+ .card-title {
209
+ font-size: 13px;
210
+ font-weight: 600;
211
+ letter-spacing: 0.05em;
212
+ text-transform: uppercase;
213
+ color: var(--muted);
214
+ }
215
+ .card-body { padding: 20px; }
216
+
217
+ /* Progress tracker */
218
+ .progress-track {
219
+ display: flex;
220
+ align-items: flex-start;
221
+ justify-content: space-between;
222
+ padding: 8px 0;
223
+ overflow-x: auto;
224
+ }
225
+ .step {
226
+ display: flex; flex-direction: column; align-items: center;
227
+ gap: 8px; min-width: 60px;
228
+ }
229
+ .step-circle {
230
+ width: 44px; height: 44px;
231
+ border-radius: 50%;
232
+ border: 2px solid var(--border);
233
+ background: white;
234
+ display: flex; align-items: center; justify-content: center;
235
+ font-size: 18px;
236
+ color: var(--muted);
237
+ transition: all 0.3s;
238
+ }
239
+ .step.done .step-circle {
240
+ background: color-mix(in srgb, var(--accent) 15%, white);
241
+ border-color: var(--accent);
242
+ color: var(--accent);
243
+ font-size: 16px;
244
+ font-weight: 700;
245
+ }
246
+ .step.active .step-circle {
247
+ background: var(--accent);
248
+ border-color: var(--accent);
249
+ color: white;
250
+ box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 20%, transparent);
251
+ }
252
+ .step-label {
253
+ font-size: 11px; font-weight: 500; color: var(--muted);
254
+ text-align: center; line-height: 1.3;
255
+ }
256
+ .step.active .step-label, .step.done .step-label { color: var(--accent); font-weight: 600; }
257
+ .step-line {
258
+ flex: 1; height: 2px;
259
+ background: var(--border);
260
+ margin-top: 22px; min-width: 20px;
261
+ transition: background 0.3s;
262
+ }
263
+ .step-line.done { background: var(--accent); }
264
+ .cancelled-banner {
265
+ text-align: center; padding: 16px;
266
+ background: #fef2f2; color: #ef4444;
267
+ border-radius: 10px;
268
+ font-weight: 600; font-size: 15px;
269
+ }
270
+
271
+ /* Info grid */
272
+ .info-grid {
273
+ display: grid;
274
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
275
+ gap: 16px;
276
+ }
277
+ .info-item dt {
278
+ font-size: 11px; font-weight: 600;
279
+ text-transform: uppercase; letter-spacing: 0.08em;
280
+ color: var(--muted); margin-bottom: 4px;
281
+ }
282
+ .info-item dd { font-size: 14px; font-weight: 500; color: var(--text); }
283
+
284
+ /* Customer */
285
+ .customer-row {
286
+ display: flex; align-items: center; gap: 14px;
287
+ }
288
+ .customer-avatar {
289
+ width: 46px; height: 46px; border-radius: 50%;
290
+ background: color-mix(in srgb, var(--accent) 15%, #f3f4f6);
291
+ color: var(--accent);
292
+ display: flex; align-items: center; justify-content: center;
293
+ font-family: 'Playfair Display', serif;
294
+ font-size: 18px; font-weight: 700; flex-shrink: 0;
295
+ }
296
+ .customer-name { font-size: 15px; font-weight: 600; }
297
+ .customer-email { font-size: 13px; color: var(--muted); margin-top: 2px; }
298
+
299
+ /* Items */
300
+ .item-row {
301
+ display: flex; align-items: center; gap: 12px;
302
+ padding: 12px 0;
303
+ border-bottom: 1px solid var(--border);
304
+ }
305
+ .item-row:last-child { border-bottom: none; padding-bottom: 0; }
306
+ .item-row:first-child { padding-top: 0; }
307
+ .item-avatar {
308
+ width: 40px; height: 40px; border-radius: 10px;
309
+ background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 20%, #e5e7eb), color-mix(in srgb, var(--accent) 5%, #f9fafb));
310
+ color: var(--accent);
311
+ display: flex; align-items: center; justify-content: center;
312
+ font-size: 16px; font-weight: 700; flex-shrink: 0;
313
+ }
314
+ .item-info { flex: 1; min-width: 0; }
315
+ .item-name { font-size: 14px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
316
+ .item-sku { font-size: 12px; color: var(--muted); margin-top: 2px; }
317
+ .item-qty { font-size: 13px; color: var(--muted); min-width: 28px; text-align: center; }
318
+ .item-price { font-size: 14px; font-weight: 600; min-width: 80px; text-align: right; }
319
+
320
+ /* Summary */
321
+ .summary-row {
322
+ display: flex; justify-content: space-between;
323
+ font-size: 14px; padding: 7px 0; color: var(--muted);
324
+ }
325
+ .summary-row.discount { color: #10b981; }
326
+ .summary-row.total {
327
+ font-size: 17px; font-weight: 700; color: var(--text);
328
+ border-top: 2px solid var(--border); margin-top: 6px; padding-top: 14px;
329
+ }
330
+
331
+ /* Footer */
332
+ .footer {
333
+ text-align: center; margin-top: 32px;
334
+ font-size: 12px; color: #9ca3af;
335
+ animation: fadeUp 0.5s ease 0.4s both;
336
+ }
337
+
338
+ /* Animations */
339
+ @keyframes fadeDown {
340
+ from { opacity: 0; transform: translateY(-16px); }
341
+ to { opacity: 1; transform: translateY(0); }
342
+ }
343
+ @keyframes fadeUp {
344
+ from { opacity: 0; transform: translateY(16px); }
345
+ to { opacity: 1; transform: translateY(0); }
346
+ }
347
+ @keyframes pulse {
348
+ 0%, 100% { opacity: 1; }
349
+ 50% { opacity: 0.4; }
350
+ }
351
+ </style>
352
+ </head>
353
+ <body>
354
+ <div class="container">
355
+
356
+ <!-- Header -->
357
+ <div class="header">
358
+ <div class="header-eyebrow">Order Tracking</div>
359
+ <h1>${String(order.orderNumber)}</h1>
360
+ <div class="header-meta">Placed on ${formatDate(String(order.createdAt))}</div>
361
+ <div><span class="status-badge"><span class="status-dot"></span>${sc.label}</span></div>
362
+ </div>
363
+
364
+ <!-- Progress -->
365
+ <div class="card">
366
+ <div class="card-header">
367
+ <div class="card-header-icon">\u{1F69A}</div>
368
+ <div class="card-title">Order Progress</div>
369
+ </div>
370
+ <div class="card-body">
371
+ <div class="progress-track">${stepsHtml}</div>
372
+ </div>
373
+ </div>
374
+
375
+ ${contact ? `
376
+ <!-- Customer -->
377
+ <div class="card">
378
+ <div class="card-header">
379
+ <div class="card-header-icon">\u{1F464}</div>
380
+ <div class="card-title">Customer</div>
381
+ </div>
382
+ <div class="card-body">
383
+ <div class="customer-row">
384
+ <div class="customer-avatar">${String(contact.name ?? "?").charAt(0).toUpperCase()}</div>
385
+ <div>
386
+ <div class="customer-name">${String(contact.name ?? "\u2014")}</div>
387
+ <div class="customer-email">${String(contact.email ?? "\u2014")}</div>
388
+ ${contact.phone ? `<div class="customer-email">${String(contact.phone)}</div>` : ""}
389
+ </div>
390
+ </div>
391
+ </div>
392
+ </div>` : ""}
393
+
394
+ <!-- Items -->
395
+ <div class="card">
396
+ <div class="card-header">
397
+ <div class="card-header-icon">\u{1F4E6}</div>
398
+ <div class="card-title">Items (${items.length})</div>
399
+ </div>
400
+ <div class="card-body">
401
+ ${items.length === 0 ? '<p style="color:var(--muted);font-size:14px;">No items</p>' : itemsHtml}
402
+ </div>
403
+ </div>
404
+
405
+ <!-- Summary -->
406
+ <div class="card">
407
+ <div class="card-header">
408
+ <div class="card-header-icon">\u{1F9FE}</div>
409
+ <div class="card-title">Order Summary</div>
410
+ </div>
411
+ <div class="card-body">
412
+ <div class="summary-row"><span>Subtotal</span><span>${formatMoney(Number(order.subtotal))}</span></div>
413
+ ${discountRow}
414
+ ${taxRow}
415
+ <div class="summary-row total"><span>Total</span><span>${formatMoney(Number(order.total))}</span></div>
416
+ </div>
417
+ </div>
418
+
419
+ <div class="footer">Scan the QR code on your receipt to return to this page.</div>
420
+ </div>
421
+ </body>
422
+ </html>`;
423
+ }
424
+ __name(renderOrderTrackingPage, "renderOrderTrackingPage");
425
+
426
+ export { renderOrderTrackingPage };