@mohasinac/appkit 2.7.23 → 2.7.25
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/dist/features/admin/components/DataTable.js +2 -2
- package/dist/features/auth/consent-otp.js +8 -8
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/permissions/constants.js +2 -0
- package/dist/features/before-after/messages/en.json +7 -7
- package/dist/features/collections/messages/en.json +9 -9
- package/dist/features/consultation/messages/en.json +24 -24
- package/dist/features/contact/email.js +179 -179
- package/dist/features/corporate/messages/en.json +25 -25
- package/dist/features/loyalty/messages/en.json +18 -18
- package/dist/features/media/messages/en.json +63 -63
- package/dist/features/pre-orders/messages/en.json +12 -12
- package/dist/features/whatsapp-bot/messages/en.json +6 -6
- package/dist/styles.css +21 -21
- package/dist/tokens/tokens.css +436 -436
- package/dist/ui/components/ActiveFilterChips.style.css +66 -66
- package/dist/ui/components/AvatarDisplay.style.css +46 -46
- package/dist/ui/components/BackgroundRenderer.style.css +31 -31
- package/dist/ui/components/Breadcrumb.style.css +60 -60
- package/dist/ui/components/BulkActionBar.js +1 -1
- package/dist/ui/components/CountdownDisplay.js +1 -1
- package/dist/ui/components/CountdownDisplay.style.css +4 -4
- package/dist/ui/components/Div.style.css +6 -6
- package/dist/ui/components/EmptyState.js +1 -1
- package/dist/ui/components/EmptyState.style.css +28 -28
- package/dist/ui/components/FlowDiagram.style.css +77 -77
- package/dist/ui/components/FormGrid.style.css +32 -32
- package/dist/ui/components/ImageGallery.style.css +163 -163
- package/dist/ui/components/ImageLightbox.style.css +58 -58
- package/dist/ui/components/ItemRow.style.css +33 -33
- package/dist/ui/components/Layout.style.css +229 -229
- package/dist/ui/components/ListingToolbar.js +1 -1
- package/dist/ui/components/Menu.style.css +6 -6
- package/dist/ui/components/NavigationLoader.style.css +19 -19
- package/dist/ui/components/ResponsiveView.style.css +69 -69
- package/dist/ui/components/RoleBadge.style.css +7 -7
- package/dist/ui/components/SectionTabs.style.css +164 -164
- package/dist/ui/components/Semantic.style.css +14 -14
- package/dist/ui/components/SiteLogo.js +2 -2
- package/dist/ui/components/SkipToMain.style.css +34 -34
- package/dist/ui/components/SortDropdown.style.css +12 -12
- package/dist/ui/components/StatusBadge.style.css +5 -5
- package/dist/ui/components/TablePagination.js +2 -2
- package/dist/ui/components/Toast.js +2 -2
- package/dist/ui/components/UnsavedChangesModal.style.css +33 -33
- package/dist/ui/components/ViewToggle.style.css +50 -50
- package/dist/ui/components/index.style.css +78 -78
- package/package.json +1 -1
|
@@ -35,7 +35,7 @@ function SelectableRow({ row, columns, isSelected, onToggle, renderRowActions, o
|
|
|
35
35
|
}
|
|
36
36
|
: undefined;
|
|
37
37
|
const isInteractive = Boolean(onRowClick ?? rowHref);
|
|
38
|
-
return (_jsxs("tr", { onClick: handleClick, onKeyDown: handleKeyDown, role: isInteractive ? "link" : undefined, tabIndex: isInteractive ? 0 : undefined, onMouseDown: selectionEnabled && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: selectionEnabled && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: selectionEnabled && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: selectionEnabled && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: selectionEnabled && !isSelected ? longPress.onTouchEnd : undefined, className: `border-b border-neutral-100 dark:border-slate-700 hover:bg-neutral-50 dark:hover:bg-slate-800 ${isInteractive ? "cursor-pointer" : ""} ${isSelected ? "bg-primary/5 dark:bg-primary/10" : ""}`, children: [selectionEnabled && (_jsx("td", { className: "relative w-10 px-2 py-3", onClick: (e) => e.stopPropagation(), children: _jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onToggle?.(row.id, !isSelected); }, label: isSelected ? "Deselect row" : "Select row", position: "top-1/2 left-2 -translate-y-1/2" }) })), columns.map((col) => (_jsx("td", { className: `px-4 py-3 text-neutral-700 dark:text-zinc-300 ${col.className ?? ""}`, children: col.render
|
|
38
|
+
return (_jsxs("tr", { "data-testid": "data-table-row", onClick: handleClick, onKeyDown: handleKeyDown, role: isInteractive ? "link" : undefined, tabIndex: isInteractive ? 0 : undefined, onMouseDown: selectionEnabled && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: selectionEnabled && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: selectionEnabled && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: selectionEnabled && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: selectionEnabled && !isSelected ? longPress.onTouchEnd : undefined, className: `border-b border-neutral-100 dark:border-slate-700 hover:bg-neutral-50 dark:hover:bg-slate-800 ${isInteractive ? "cursor-pointer" : ""} ${isSelected ? "bg-primary/5 dark:bg-primary/10" : ""}`, children: [selectionEnabled && (_jsx("td", { className: "relative w-10 px-2 py-3", onClick: (e) => e.stopPropagation(), children: _jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onToggle?.(row.id, !isSelected); }, label: isSelected ? "Deselect row" : "Select row", position: "top-1/2 left-2 -translate-y-1/2", "data-testid": "row-checkbox" }) })), columns.map((col) => (_jsx("td", { className: `px-4 py-3 text-neutral-700 dark:text-zinc-300 ${col.className ?? ""}`, children: col.render
|
|
39
39
|
? col.render(row)
|
|
40
40
|
: String(row[col.key] ?? "") }, col.key))), renderRowActions && (_jsx("td", { className: "px-2 py-3", onClick: (e) => e.stopPropagation(), children: renderRowActions(row) }))] }));
|
|
41
41
|
}
|
|
@@ -43,5 +43,5 @@ export function DataTable({ columns: columnsProp, rows, isLoading, sortKey, sort
|
|
|
43
43
|
const columns = (columnsProp ?? DEFAULT_COLUMNS);
|
|
44
44
|
const selectionEnabled = Boolean(onToggleSelect);
|
|
45
45
|
const allRowsSelected = selectionEnabled && rows.length > 0 && rows.every((r) => selectedIds?.has(r.id));
|
|
46
|
-
return (_jsxs(Div, { className: "overflow-hidden rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900", children: [_jsx(Div, { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b border-neutral-200 dark:border-slate-700 bg-neutral-50 dark:bg-slate-800", children: [selectionEnabled && (_jsx("th", { scope: "col", className: "w-10 px-2 py-3", children: onToggleSelectAll && (_jsx("input", { type: "checkbox", "aria-label": allRowsSelected ? "Deselect all" : "Select all", checked: allRowsSelected, onChange: () => onToggleSelectAll(!allRowsSelected), className: "h-4 w-4 rounded border-zinc-300 dark:border-slate-600 accent-zinc-900 dark:accent-zinc-100" })) })), columns.map((col) => (_jsxs("th", { scope: "col", onClick: col.sortable && onSort ? () => onSort(col.key) : undefined, className: `px-4 py-3 text-left font-semibold text-neutral-900 dark:text-zinc-100 ${col.sortable && onSort ? "cursor-pointer select-none hover:text-primary" : ""} ${col.className ?? ""}`, children: [col.header, col.sortable && sortKey === col.key && (_jsx(Span, { className: "ml-1", children: sortDir === "asc" ? "↑" : "↓" }))] }, col.key))), renderRowActions && _jsx("th", { scope: "col", className: "w-12 px-2 py-3" })] }) }), _jsx("tbody", { children: isLoading ? (Array.from({ length: 5 }).map((_, i) => (_jsxs("tr", { className: "border-b border-neutral-100 dark:border-slate-700", children: [selectionEnabled && _jsx("td", { className: "w-10 px-2 py-3" }), columns.map((col) => (_jsx("td", { className: "px-4 py-3", children: _jsx(Div, { className: "h-4 w-full animate-pulse rounded bg-neutral-200 dark:bg-slate-700" }) }, col.key)))] }, i)))) : rows.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: columns.length + (selectionEnabled ? 1 : 0) + (renderRowActions ? 1 : 0), className: "px-4 py-12 text-center text-neutral-500 dark:text-zinc-400", children: emptyLabel }) })) : (rows.map((row) => (_jsx(SelectableRow, { row: row, columns: columns, isSelected: selectedIds?.has(row.id) ?? false, onToggle: onToggleSelect, renderRowActions: renderRowActions, onRowClick: onRowClick, rowHref: getRowHref?.(row), selectionEnabled: selectionEnabled }, row.id)))) })] }) }), totalPages > 1 && onPageChange && (_jsx(Div, { className: "flex items-center justify-end gap-2 border-t border-neutral-200 dark:border-slate-700 px-4 py-3", children: Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (_jsx(Button, { onClick: () => onPageChange(p), variant: p === currentPage ? "primary" : "ghost", size: "sm", className: `h-8 w-8 rounded text-xs font-medium transition ${p === currentPage ? "bg-neutral-900 text-white" : "text-neutral-600 dark:text-zinc-300 hover:bg-neutral-100 dark:hover:bg-slate-800"}`, children: p }, p))) }))] }));
|
|
46
|
+
return (_jsxs(Div, { className: "overflow-hidden rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900", children: [_jsx(Div, { className: "overflow-x-auto", children: _jsxs("table", { "data-testid": "data-table", className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b border-neutral-200 dark:border-slate-700 bg-neutral-50 dark:bg-slate-800", children: [selectionEnabled && (_jsx("th", { scope: "col", className: "w-10 px-2 py-3", children: onToggleSelectAll && (_jsx("input", { type: "checkbox", "data-testid": "select-all-checkbox", "aria-label": allRowsSelected ? "Deselect all" : "Select all", checked: allRowsSelected, onChange: () => onToggleSelectAll(!allRowsSelected), className: "h-4 w-4 rounded border-zinc-300 dark:border-slate-600 accent-zinc-900 dark:accent-zinc-100" })) })), columns.map((col) => (_jsxs("th", { scope: "col", onClick: col.sortable && onSort ? () => onSort(col.key) : undefined, className: `px-4 py-3 text-left font-semibold text-neutral-900 dark:text-zinc-100 ${col.sortable && onSort ? "cursor-pointer select-none hover:text-primary" : ""} ${col.className ?? ""}`, children: [col.header, col.sortable && sortKey === col.key && (_jsx(Span, { className: "ml-1", children: sortDir === "asc" ? "↑" : "↓" }))] }, col.key))), renderRowActions && _jsx("th", { scope: "col", className: "w-12 px-2 py-3" })] }) }), _jsx("tbody", { children: isLoading ? (Array.from({ length: 5 }).map((_, i) => (_jsxs("tr", { className: "border-b border-neutral-100 dark:border-slate-700", children: [selectionEnabled && _jsx("td", { className: "w-10 px-2 py-3" }), columns.map((col) => (_jsx("td", { className: "px-4 py-3", children: _jsx(Div, { className: "h-4 w-full animate-pulse rounded bg-neutral-200 dark:bg-slate-700" }) }, col.key)))] }, i)))) : rows.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: columns.length + (selectionEnabled ? 1 : 0) + (renderRowActions ? 1 : 0), className: "px-4 py-12 text-center text-neutral-500 dark:text-zinc-400", children: emptyLabel }) })) : (rows.map((row) => (_jsx(SelectableRow, { row: row, columns: columns, isSelected: selectedIds?.has(row.id) ?? false, onToggle: onToggleSelect, renderRowActions: renderRowActions, onRowClick: onRowClick, rowHref: getRowHref?.(row), selectionEnabled: selectionEnabled }, row.id)))) })] }) }), totalPages > 1 && onPageChange && (_jsx(Div, { className: "flex items-center justify-end gap-2 border-t border-neutral-200 dark:border-slate-700 px-4 py-3", children: Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (_jsx(Button, { onClick: () => onPageChange(p), variant: p === currentPage ? "primary" : "ghost", size: "sm", className: `h-8 w-8 rounded text-xs font-medium transition ${p === currentPage ? "bg-neutral-900 text-white" : "text-neutral-600 dark:text-zinc-300 hover:bg-neutral-100 dark:hover:bg-slate-800"}`, children: p }, p))) }))] }));
|
|
47
47
|
}
|
|
@@ -106,14 +106,14 @@ export async function patchConsentOtp(uid, addressId, data) {
|
|
|
106
106
|
export function buildConsentOtpEmail(recipientName, code, siteName) {
|
|
107
107
|
return {
|
|
108
108
|
subject: `${siteName}: Consent verification code - shipping to ${recipientName}`,
|
|
109
|
-
html: `
|
|
110
|
-
<div style="font-family:sans-serif;max-width:480px;margin:0 auto;padding:24px">
|
|
111
|
-
<h2 style="margin-bottom:8px">Third-Party Shipping Consent</h2>
|
|
112
|
-
<p style="color:#555">You are about to place an order that will be shipped to <strong>${escapeHtml(recipientName)}</strong>.</p>
|
|
113
|
-
<p style="color:#555">Enter this code to confirm your consent:</p>
|
|
114
|
-
<div style="font-size:36px;font-weight:bold;letter-spacing:8px;text-align:center;margin:24px 0;padding:16px;background:#f3f4f6;border-radius:8px">${code}</div>
|
|
115
|
-
<p style="color:#888;font-size:12px">This code expires in ${CONSENT_OTP_EXPIRY_MINUTES} minutes. If you did not request this, please ignore this email.</p>
|
|
116
|
-
</div>
|
|
109
|
+
html: `
|
|
110
|
+
<div style="font-family:sans-serif;max-width:480px;margin:0 auto;padding:24px">
|
|
111
|
+
<h2 style="margin-bottom:8px">Third-Party Shipping Consent</h2>
|
|
112
|
+
<p style="color:#555">You are about to place an order that will be shipped to <strong>${escapeHtml(recipientName)}</strong>.</p>
|
|
113
|
+
<p style="color:#555">Enter this code to confirm your consent:</p>
|
|
114
|
+
<div style="font-size:36px;font-weight:bold;letter-spacing:8px;text-align:center;margin:24px 0;padding:16px;background:#f3f4f6;border-radius:8px">${code}</div>
|
|
115
|
+
<p style="color:#888;font-size:12px">This code expires in ${CONSENT_OTP_EXPIRY_MINUTES} minutes. If you did not request this, please ignore this email.</p>
|
|
116
|
+
</div>
|
|
117
117
|
`,
|
|
118
118
|
};
|
|
119
119
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Permission naming: admin:resource:action
|
|
12
12
|
* Ban action naming: plain verb phrase — write_reviews, place_bids, etc.
|
|
13
13
|
*/
|
|
14
|
-
export type Permission = "admin:dashboard:view" | "admin:users:read" | "admin:users:write" | "admin:users:delete" | "admin:user-bans:read" | "admin:user-bans:write" | "admin:products:read" | "admin:products:write" | "admin:products:delete" | "admin:orders:read" | "admin:orders:write" | "admin:returns:read" | "admin:returns:write" | "admin:stores:read" | "admin:stores:write" | "admin:store-addresses:read" | "admin:analytics:view" | "admin:payouts:read" | "admin:payouts:write" | "admin:categories:read" | "admin:categories:write" | "admin:categories:delete" | "admin:brands:read" | "admin:brands:write" | "admin:brands:delete" | "admin:coupons:read" | "admin:coupons:write" | "admin:coupons:delete" | "admin:deals:read" | "admin:deals:write" | "admin:featured:read" | "admin:featured:write" | "admin:reviews:read" | "admin:reviews:write" | "admin:reviews:delete" | "admin:blog:read" | "admin:blog:write" | "admin:blog:delete" | "admin:blog:publish" | "admin:bids:read" | "admin:bids:write" | "admin:media:read" | "admin:media:write" | "admin:media:delete" | "admin:site:read" | "admin:site:write" | "admin:navigation:read" | "admin:navigation:write" | "admin:sections:read" | "admin:sections:write" | "admin:carousel:read" | "admin:carousel:write" | "admin:carousel:delete" | "admin:ads:read" | "admin:ads:write" | "admin:ads:delete" | "admin:faqs:read" | "admin:faqs:write" | "admin:faqs:delete" | "admin:newsletter:read" | "admin:newsletter:write" | "admin:contact:read" | "admin:events:read" | "admin:events:write" | "admin:events:delete" | "admin:event-entries:read" | "admin:event-entries:write" | "admin:support-tickets:read" | "admin:support-tickets:write" | "admin:support-tickets:assign" | "admin:support-tickets:close" | "admin:scammers:read" | "admin:scammers:write" | "admin:scammers:verify" | "admin:scammers:delete" | "admin:sessions:read" | "admin:sessions:delete" | "admin:notifications:read" | "admin:notifications:write" | "admin:carts:read" | "admin:wishlists:read" | "admin:feature-flags:read" | "admin:feature-flags:write" | "admin:copilot:view" | "admin:team:read" | "admin:team:write";
|
|
14
|
+
export type Permission = "admin:dashboard:view" | "admin:users:read" | "admin:users:write" | "admin:users:delete" | "admin:user-bans:read" | "admin:user-bans:write" | "admin:products:read" | "admin:products:write" | "admin:products:delete" | "admin:orders:read" | "admin:orders:write" | "admin:returns:read" | "admin:returns:write" | "admin:stores:read" | "admin:stores:write" | "admin:store-addresses:read" | "admin:analytics:view" | "admin:payouts:read" | "admin:payouts:write" | "admin:categories:read" | "admin:categories:write" | "admin:categories:delete" | "admin:brands:read" | "admin:brands:write" | "admin:brands:delete" | "admin:coupons:read" | "admin:coupons:write" | "admin:coupons:delete" | "admin:deals:read" | "admin:deals:write" | "admin:featured:read" | "admin:featured:write" | "admin:reviews:read" | "admin:reviews:write" | "admin:reviews:delete" | "admin:blog:read" | "admin:blog:write" | "admin:blog:delete" | "admin:blog:publish" | "admin:bids:read" | "admin:bids:write" | "admin:media:read" | "admin:media:write" | "admin:media:delete" | "admin:site:read" | "admin:site:write" | "admin:settings:write" | "admin:navigation:read" | "admin:navigation:write" | "admin:sections:read" | "admin:sections:write" | "admin:carousel:read" | "admin:carousel:write" | "admin:carousel:delete" | "admin:ads:read" | "admin:ads:write" | "admin:ads:delete" | "admin:faqs:read" | "admin:faqs:write" | "admin:faqs:delete" | "admin:newsletter:read" | "admin:newsletter:write" | "admin:contact:read" | "admin:events:read" | "admin:events:write" | "admin:events:delete" | "admin:event-entries:read" | "admin:event-entries:write" | "admin:support-tickets:read" | "admin:support-tickets:write" | "admin:support-tickets:assign" | "admin:support-tickets:close" | "admin:scammers:read" | "admin:scammers:write" | "admin:scammers:verify" | "admin:scammers:delete" | "admin:sessions:read" | "admin:sessions:delete" | "admin:notifications:read" | "admin:notifications:write" | "admin:carts:read" | "admin:wishlists:read" | "admin:feature-flags:read" | "admin:feature-flags:write" | "admin:copilot:view" | "admin:team:read" | "admin:team:write";
|
|
15
15
|
/**
|
|
16
16
|
* Granular user actions that can be soft-banned individually.
|
|
17
17
|
* A user may have multiple simultaneous soft bans of different types.
|
|
@@ -112,6 +112,7 @@ export const PERMISSION_GROUPS = {
|
|
|
112
112
|
"admin:dashboard:view",
|
|
113
113
|
"admin:site:read",
|
|
114
114
|
"admin:site:write",
|
|
115
|
+
"admin:settings:write",
|
|
115
116
|
"admin:navigation:read",
|
|
116
117
|
"admin:navigation:write",
|
|
117
118
|
"admin:sections:read",
|
|
@@ -268,6 +269,7 @@ export const ROUTE_PERMISSION_MAP = {
|
|
|
268
269
|
"/admin/bids": "admin:bids:read",
|
|
269
270
|
"/admin/media": "admin:media:read",
|
|
270
271
|
"/admin/site": "admin:site:read",
|
|
272
|
+
"/admin/settings": "admin:settings:write",
|
|
271
273
|
"/admin/navigation": "admin:navigation:read",
|
|
272
274
|
"/admin/sections": "admin:sections:read",
|
|
273
275
|
"/admin/carousel": "admin:carousel:read",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
{
|
|
2
|
-
"title": "Real Results",
|
|
3
|
-
"subtitle": "See the transformation with our Ayurvedic products",
|
|
4
|
-
"labelBefore": "Before",
|
|
5
|
-
"labelAfter": "After",
|
|
6
|
-
"weeks": "{count} weeks",
|
|
7
|
-
"noItems": "No transformation stories yet"
|
|
1
|
+
{
|
|
2
|
+
"title": "Real Results",
|
|
3
|
+
"subtitle": "See the transformation with our Ayurvedic products",
|
|
4
|
+
"labelBefore": "Before",
|
|
5
|
+
"labelAfter": "After",
|
|
6
|
+
"weeks": "{count} weeks",
|
|
7
|
+
"noItems": "No transformation stories yet"
|
|
8
8
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
{
|
|
2
|
-
"collections": {
|
|
3
|
-
"title": "Collections",
|
|
4
|
-
"subtitle": "Shop by collection",
|
|
5
|
-
"viewAll": "View all collections",
|
|
6
|
-
"items": "{count} items",
|
|
7
|
-
"empty": "No collections found.",
|
|
8
|
-
"notFound": "Collection not found."
|
|
9
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"collections": {
|
|
3
|
+
"title": "Collections",
|
|
4
|
+
"subtitle": "Shop by collection",
|
|
5
|
+
"viewAll": "View all collections",
|
|
6
|
+
"items": "{count} items",
|
|
7
|
+
"empty": "No collections found.",
|
|
8
|
+
"notFound": "Collection not found."
|
|
9
|
+
}
|
|
10
10
|
}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
{
|
|
2
|
-
"title": "Book a Consultation",
|
|
3
|
-
"subtitle": "Get personalised Ayurvedic guidance",
|
|
4
|
-
"form": {
|
|
5
|
-
"name": "Full name",
|
|
6
|
-
"email": "Email address",
|
|
7
|
-
"phone": "Phone number",
|
|
8
|
-
"preferredDate": "Preferred date",
|
|
9
|
-
"preferredTime": "Preferred time",
|
|
10
|
-
"mode": "Consultation mode",
|
|
11
|
-
"modeRemote": "Remote (Video Call)",
|
|
12
|
-
"modeInPerson": "In-Person",
|
|
13
|
-
"message": "Additional notes (optional)",
|
|
14
|
-
"submit": "Book Consultation",
|
|
15
|
-
"submitting": "Booking..."
|
|
16
|
-
},
|
|
17
|
-
"status": {
|
|
18
|
-
"pending": "Pending",
|
|
19
|
-
"confirmed": "Confirmed",
|
|
20
|
-
"completed": "Completed",
|
|
21
|
-
"cancelled": "Cancelled"
|
|
22
|
-
},
|
|
23
|
-
"success": "Your consultation has been booked. We\u2019ll confirm shortly.",
|
|
24
|
-
"error": "Failed to book consultation. Please try again."
|
|
1
|
+
{
|
|
2
|
+
"title": "Book a Consultation",
|
|
3
|
+
"subtitle": "Get personalised Ayurvedic guidance",
|
|
4
|
+
"form": {
|
|
5
|
+
"name": "Full name",
|
|
6
|
+
"email": "Email address",
|
|
7
|
+
"phone": "Phone number",
|
|
8
|
+
"preferredDate": "Preferred date",
|
|
9
|
+
"preferredTime": "Preferred time",
|
|
10
|
+
"mode": "Consultation mode",
|
|
11
|
+
"modeRemote": "Remote (Video Call)",
|
|
12
|
+
"modeInPerson": "In-Person",
|
|
13
|
+
"message": "Additional notes (optional)",
|
|
14
|
+
"submit": "Book Consultation",
|
|
15
|
+
"submitting": "Booking..."
|
|
16
|
+
},
|
|
17
|
+
"status": {
|
|
18
|
+
"pending": "Pending",
|
|
19
|
+
"confirmed": "Confirmed",
|
|
20
|
+
"completed": "Completed",
|
|
21
|
+
"cancelled": "Cancelled"
|
|
22
|
+
},
|
|
23
|
+
"success": "Your consultation has been booked. We\u2019ll confirm shortly.",
|
|
24
|
+
"error": "Failed to book consultation. Please try again."
|
|
25
25
|
}
|
|
@@ -81,34 +81,34 @@ export async function sendOrderConfirmationEmail(params) {
|
|
|
81
81
|
const orderUrl = `${siteUrl}/user/orders/view/${orderId}`;
|
|
82
82
|
const formattedTotal = formatCurrency(totalPrice, currency);
|
|
83
83
|
const itemsHtml = items && items.length > 1
|
|
84
|
-
? `
|
|
85
|
-
<table width="100%" cellpadding="8" cellspacing="0" border="0" style="border-collapse: collapse; margin-bottom: 8px;">
|
|
86
|
-
<thead>
|
|
87
|
-
<tr style="background-color: #e9ecef;">
|
|
88
|
-
<th style="text-align: left; font-size: 12px; font-weight: 600; color: #666; text-transform: uppercase; letter-spacing: 0.05em; padding: 8px 12px;">Product</th>
|
|
89
|
-
<th style="text-align: center; font-size: 12px; font-weight: 600; color: #666; text-transform: uppercase; letter-spacing: 0.05em; padding: 8px 12px;">Qty</th>
|
|
90
|
-
<th style="text-align: right; font-size: 12px; font-weight: 600; color: #666; text-transform: uppercase; letter-spacing: 0.05em; padding: 8px 12px;">Amount</th>
|
|
91
|
-
</tr>
|
|
92
|
-
</thead>
|
|
93
|
-
<tbody>
|
|
84
|
+
? `
|
|
85
|
+
<table width="100%" cellpadding="8" cellspacing="0" border="0" style="border-collapse: collapse; margin-bottom: 8px;">
|
|
86
|
+
<thead>
|
|
87
|
+
<tr style="background-color: #e9ecef;">
|
|
88
|
+
<th style="text-align: left; font-size: 12px; font-weight: 600; color: #666; text-transform: uppercase; letter-spacing: 0.05em; padding: 8px 12px;">Product</th>
|
|
89
|
+
<th style="text-align: center; font-size: 12px; font-weight: 600; color: #666; text-transform: uppercase; letter-spacing: 0.05em; padding: 8px 12px;">Qty</th>
|
|
90
|
+
<th style="text-align: right; font-size: 12px; font-weight: 600; color: #666; text-transform: uppercase; letter-spacing: 0.05em; padding: 8px 12px;">Amount</th>
|
|
91
|
+
</tr>
|
|
92
|
+
</thead>
|
|
93
|
+
<tbody>
|
|
94
94
|
${items
|
|
95
|
-
.map((item, index) => `
|
|
96
|
-
<tr style="background-color: ${index % 2 === 0 ? "#ffffff" : "#f8f9fa"}; border-top: 1px solid #e9ecef;">
|
|
97
|
-
<td style="font-size: 14px; color: #333; padding: 8px 12px;">${item.productTitle}</td>
|
|
98
|
-
<td style="font-size: 14px; color: #333; text-align: center; padding: 8px 12px;">${item.quantity}</td>
|
|
99
|
-
<td style="font-size: 14px; color: #333; text-align: right; padding: 8px 12px;">${formatCurrency(item.totalPrice, currency)}</td>
|
|
95
|
+
.map((item, index) => `
|
|
96
|
+
<tr style="background-color: ${index % 2 === 0 ? "#ffffff" : "#f8f9fa"}; border-top: 1px solid #e9ecef;">
|
|
97
|
+
<td style="font-size: 14px; color: #333; padding: 8px 12px;">${item.productTitle}</td>
|
|
98
|
+
<td style="font-size: 14px; color: #333; text-align: center; padding: 8px 12px;">${item.quantity}</td>
|
|
99
|
+
<td style="font-size: 14px; color: #333; text-align: right; padding: 8px 12px;">${formatCurrency(item.totalPrice, currency)}</td>
|
|
100
100
|
</tr>`)
|
|
101
|
-
.join("")}
|
|
102
|
-
</tbody>
|
|
101
|
+
.join("")}
|
|
102
|
+
</tbody>
|
|
103
103
|
</table>`
|
|
104
|
-
: `
|
|
105
|
-
<tr>
|
|
106
|
-
<td style="color: #666; font-size: 14px;">Product</td>
|
|
107
|
-
<td style="color: #333; font-size: 14px;">${productTitle}</td>
|
|
108
|
-
</tr>
|
|
109
|
-
<tr>
|
|
110
|
-
<td style="color: #666; font-size: 14px;">Quantity</td>
|
|
111
|
-
<td style="color: #333; font-size: 14px;">${quantity}</td>
|
|
104
|
+
: `
|
|
105
|
+
<tr>
|
|
106
|
+
<td style="color: #666; font-size: 14px;">Product</td>
|
|
107
|
+
<td style="color: #333; font-size: 14px;">${productTitle}</td>
|
|
108
|
+
</tr>
|
|
109
|
+
<tr>
|
|
110
|
+
<td style="color: #666; font-size: 14px;">Quantity</td>
|
|
111
|
+
<td style="color: #333; font-size: 14px;">${quantity}</td>
|
|
112
112
|
</tr>`;
|
|
113
113
|
const itemsText = items && items.length > 1
|
|
114
114
|
? items
|
|
@@ -119,88 +119,88 @@ export async function sendOrderConfirmationEmail(params) {
|
|
|
119
119
|
const { data, error } = await sendConfiguredEmail({
|
|
120
120
|
to,
|
|
121
121
|
subject: `Order Confirmed — ${orderId}`,
|
|
122
|
-
html: `
|
|
123
|
-
<!DOCTYPE html>
|
|
124
|
-
<html>
|
|
125
|
-
<head>
|
|
126
|
-
<meta charset="utf-8">
|
|
127
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
128
|
-
<title>Order Confirmation</title>
|
|
129
|
-
</head>
|
|
130
|
-
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;">
|
|
131
|
-
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color: #f5f5f5; padding: 40px 0;">
|
|
132
|
-
<tr>
|
|
133
|
-
<td align="center">
|
|
134
|
-
<table width="600" cellpadding="0" cellspacing="0" border="0" style="background-color: #ffffff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
|
|
135
|
-
<tr>
|
|
136
|
-
<td style="background: linear-gradient(135deg, #10b981 0%, #059669 100%); padding: 40px; text-align: center;">
|
|
137
|
-
<p style="font-size: 48px; margin: 0 0 12px;">✅</p>
|
|
138
|
-
<h1 style="color: #ffffff; margin: 0; font-size: 28px; font-weight: 600;">Order Confirmed!</h1>
|
|
139
|
-
</td>
|
|
140
|
-
</tr>
|
|
141
|
-
<tr>
|
|
142
|
-
<td style="padding: 40px;">
|
|
143
|
-
<p style="color: #333; font-size: 16px; line-height: 1.6; margin: 0 0 20px;">Hi ${userName},</p>
|
|
144
|
-
<p style="color: #333; font-size: 16px; line-height: 1.6; margin: 0 0 24px;">
|
|
145
|
-
Thank you for your order! We've received it and will process it shortly.
|
|
146
|
-
</p>
|
|
147
|
-
<table width="100%" cellpadding="12" cellspacing="0" border="0" style="background-color: #f8f9fa; border-radius: 8px; margin-bottom: 24px;">
|
|
148
|
-
<tr>
|
|
149
|
-
<td style="color: #666; font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; border-bottom: 1px solid #e9ecef;" colspan="2">Order Details</td>
|
|
150
|
-
</tr>
|
|
151
|
-
<tr>
|
|
152
|
-
<td style="color: #666; font-size: 14px; width: 40%;">Order ID</td>
|
|
153
|
-
<td style="color: #333; font-size: 14px; font-weight: 600;">${orderId}</td>
|
|
154
|
-
</tr>
|
|
122
|
+
html: `
|
|
123
|
+
<!DOCTYPE html>
|
|
124
|
+
<html>
|
|
125
|
+
<head>
|
|
126
|
+
<meta charset="utf-8">
|
|
127
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
128
|
+
<title>Order Confirmation</title>
|
|
129
|
+
</head>
|
|
130
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;">
|
|
131
|
+
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color: #f5f5f5; padding: 40px 0;">
|
|
132
|
+
<tr>
|
|
133
|
+
<td align="center">
|
|
134
|
+
<table width="600" cellpadding="0" cellspacing="0" border="0" style="background-color: #ffffff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
|
|
135
|
+
<tr>
|
|
136
|
+
<td style="background: linear-gradient(135deg, #10b981 0%, #059669 100%); padding: 40px; text-align: center;">
|
|
137
|
+
<p style="font-size: 48px; margin: 0 0 12px;">✅</p>
|
|
138
|
+
<h1 style="color: #ffffff; margin: 0; font-size: 28px; font-weight: 600;">Order Confirmed!</h1>
|
|
139
|
+
</td>
|
|
140
|
+
</tr>
|
|
141
|
+
<tr>
|
|
142
|
+
<td style="padding: 40px;">
|
|
143
|
+
<p style="color: #333; font-size: 16px; line-height: 1.6; margin: 0 0 20px;">Hi ${userName},</p>
|
|
144
|
+
<p style="color: #333; font-size: 16px; line-height: 1.6; margin: 0 0 24px;">
|
|
145
|
+
Thank you for your order! We've received it and will process it shortly.
|
|
146
|
+
</p>
|
|
147
|
+
<table width="100%" cellpadding="12" cellspacing="0" border="0" style="background-color: #f8f9fa; border-radius: 8px; margin-bottom: 24px;">
|
|
148
|
+
<tr>
|
|
149
|
+
<td style="color: #666; font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; border-bottom: 1px solid #e9ecef;" colspan="2">Order Details</td>
|
|
150
|
+
</tr>
|
|
151
|
+
<tr>
|
|
152
|
+
<td style="color: #666; font-size: 14px; width: 40%;">Order ID</td>
|
|
153
|
+
<td style="color: #333; font-size: 14px; font-weight: 600;">${orderId}</td>
|
|
154
|
+
</tr>
|
|
155
155
|
${items && items.length > 1
|
|
156
|
-
? `
|
|
157
|
-
<tr>
|
|
158
|
-
<td style="color: #666; font-size: 14px; vertical-align: top;" colspan="2">
|
|
159
|
-
<div style="font-weight: 600; margin-bottom: 8px;">Items (${items.length})</div>
|
|
160
|
-
${itemsHtml}
|
|
161
|
-
</td>
|
|
156
|
+
? `
|
|
157
|
+
<tr>
|
|
158
|
+
<td style="color: #666; font-size: 14px; vertical-align: top;" colspan="2">
|
|
159
|
+
<div style="font-weight: 600; margin-bottom: 8px;">Items (${items.length})</div>
|
|
160
|
+
${itemsHtml}
|
|
161
|
+
</td>
|
|
162
162
|
</tr>`
|
|
163
|
-
: itemsHtml}
|
|
164
|
-
<tr>
|
|
165
|
-
<td style="color: #666; font-size: 14px;">Total</td>
|
|
166
|
-
<td style="color: #333; font-size: 14px; font-weight: 700;">${formattedTotal}</td>
|
|
167
|
-
</tr>
|
|
168
|
-
<tr>
|
|
169
|
-
<td style="color: #666; font-size: 14px;">Payment</td>
|
|
170
|
-
<td style="color: #333; font-size: 14px;">${paymentMethod === "cod" ? "Cash on Delivery" : "Online Payment"}</td>
|
|
171
|
-
</tr>
|
|
172
|
-
<tr>
|
|
173
|
-
<td style="color: #666; font-size: 14px; vertical-align: top;">Ship to</td>
|
|
174
|
-
<td style="color: #333; font-size: 14px;">${shippingAddress}</td>
|
|
175
|
-
</tr>
|
|
176
|
-
</table>
|
|
177
|
-
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="margin: 30px 0;">
|
|
178
|
-
<tr>
|
|
179
|
-
<td align="center">
|
|
180
|
-
<a href="${orderUrl}" style="display: inline-block; padding: 14px 40px; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: #ffffff; text-decoration: none; border-radius: 6px; font-size: 16px; font-weight: 600;">
|
|
181
|
-
View Order
|
|
182
|
-
</a>
|
|
183
|
-
</td>
|
|
184
|
-
</tr>
|
|
185
|
-
</table>
|
|
186
|
-
<p style="color: #999; font-size: 13px; line-height: 1.6; margin: 0;">
|
|
187
|
-
We'll send you another email when your order is shipped.
|
|
188
|
-
</p>
|
|
189
|
-
</td>
|
|
190
|
-
</tr>
|
|
191
|
-
<tr>
|
|
192
|
-
<td style="background-color: #f8f9fa; padding: 24px; text-align: center; border-top: 1px solid #eee;">
|
|
193
|
-
<p style="color: #999; font-size: 12px; margin: 0;">
|
|
194
|
-
© ${currentYear()} ${siteName}. All rights reserved.
|
|
195
|
-
</p>
|
|
196
|
-
</td>
|
|
197
|
-
</tr>
|
|
198
|
-
</table>
|
|
199
|
-
</td>
|
|
200
|
-
</tr>
|
|
201
|
-
</table>
|
|
202
|
-
</body>
|
|
203
|
-
</html>
|
|
163
|
+
: itemsHtml}
|
|
164
|
+
<tr>
|
|
165
|
+
<td style="color: #666; font-size: 14px;">Total</td>
|
|
166
|
+
<td style="color: #333; font-size: 14px; font-weight: 700;">${formattedTotal}</td>
|
|
167
|
+
</tr>
|
|
168
|
+
<tr>
|
|
169
|
+
<td style="color: #666; font-size: 14px;">Payment</td>
|
|
170
|
+
<td style="color: #333; font-size: 14px;">${paymentMethod === "cod" ? "Cash on Delivery" : "Online Payment"}</td>
|
|
171
|
+
</tr>
|
|
172
|
+
<tr>
|
|
173
|
+
<td style="color: #666; font-size: 14px; vertical-align: top;">Ship to</td>
|
|
174
|
+
<td style="color: #333; font-size: 14px;">${shippingAddress}</td>
|
|
175
|
+
</tr>
|
|
176
|
+
</table>
|
|
177
|
+
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="margin: 30px 0;">
|
|
178
|
+
<tr>
|
|
179
|
+
<td align="center">
|
|
180
|
+
<a href="${orderUrl}" style="display: inline-block; padding: 14px 40px; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: #ffffff; text-decoration: none; border-radius: 6px; font-size: 16px; font-weight: 600;">
|
|
181
|
+
View Order
|
|
182
|
+
</a>
|
|
183
|
+
</td>
|
|
184
|
+
</tr>
|
|
185
|
+
</table>
|
|
186
|
+
<p style="color: #999; font-size: 13px; line-height: 1.6; margin: 0;">
|
|
187
|
+
We'll send you another email when your order is shipped.
|
|
188
|
+
</p>
|
|
189
|
+
</td>
|
|
190
|
+
</tr>
|
|
191
|
+
<tr>
|
|
192
|
+
<td style="background-color: #f8f9fa; padding: 24px; text-align: center; border-top: 1px solid #eee;">
|
|
193
|
+
<p style="color: #999; font-size: 12px; margin: 0;">
|
|
194
|
+
© ${currentYear()} ${siteName}. All rights reserved.
|
|
195
|
+
</p>
|
|
196
|
+
</td>
|
|
197
|
+
</tr>
|
|
198
|
+
</table>
|
|
199
|
+
</td>
|
|
200
|
+
</tr>
|
|
201
|
+
</table>
|
|
202
|
+
</body>
|
|
203
|
+
</html>
|
|
204
204
|
`,
|
|
205
205
|
text: `Hi ${userName},\n\nYour order ${orderId} has been confirmed!\n\n${itemsText}\nTotal: ${formattedTotal}\nPayment: ${paymentMethod === "cod" ? "Cash on Delivery" : "Online Payment"}\nShip to: ${shippingAddress}\n\nView your order: ${orderUrl}\n\n© ${currentYear()} ${siteName}`,
|
|
206
206
|
});
|
|
@@ -224,27 +224,27 @@ export async function sendContactEmail(params) {
|
|
|
224
224
|
to: supportEmail,
|
|
225
225
|
replyTo: email,
|
|
226
226
|
subject: `[Contact] ${subject}`,
|
|
227
|
-
html: `
|
|
228
|
-
<!DOCTYPE html>
|
|
229
|
-
<html>
|
|
230
|
-
<head><meta charset="utf-8"><title>Contact Form</title></head>
|
|
231
|
-
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; padding: 40px 0;">
|
|
232
|
-
<table width="600" style="margin: 0 auto; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
|
|
233
|
-
<tr><td style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 32px; text-align: center;">
|
|
234
|
-
<h1 style="color: #fff; margin: 0; font-size: 24px;">New Contact Message</h1>
|
|
235
|
-
</td></tr>
|
|
236
|
-
<tr><td style="padding: 32px;">
|
|
237
|
-
<p><strong>From:</strong> ${name} <${email}></p>
|
|
238
|
-
<p><strong>Subject:</strong> ${subject}</p>
|
|
239
|
-
<hr style="border: none; border-top: 1px solid #eee; margin: 16px 0;">
|
|
240
|
-
<p style="white-space: pre-wrap;">${message}</p>
|
|
241
|
-
</td></tr>
|
|
242
|
-
<tr><td style="background: #f9f9f9; padding: 16px; text-align: center; color: #888; font-size: 12px;">
|
|
243
|
-
© ${currentYear()} ${siteName}
|
|
244
|
-
</td></tr>
|
|
245
|
-
</table>
|
|
246
|
-
</body>
|
|
247
|
-
</html>
|
|
227
|
+
html: `
|
|
228
|
+
<!DOCTYPE html>
|
|
229
|
+
<html>
|
|
230
|
+
<head><meta charset="utf-8"><title>Contact Form</title></head>
|
|
231
|
+
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; padding: 40px 0;">
|
|
232
|
+
<table width="600" style="margin: 0 auto; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
|
|
233
|
+
<tr><td style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 32px; text-align: center;">
|
|
234
|
+
<h1 style="color: #fff; margin: 0; font-size: 24px;">New Contact Message</h1>
|
|
235
|
+
</td></tr>
|
|
236
|
+
<tr><td style="padding: 32px;">
|
|
237
|
+
<p><strong>From:</strong> ${name} <${email}></p>
|
|
238
|
+
<p><strong>Subject:</strong> ${subject}</p>
|
|
239
|
+
<hr style="border: none; border-top: 1px solid #eee; margin: 16px 0;">
|
|
240
|
+
<p style="white-space: pre-wrap;">${message}</p>
|
|
241
|
+
</td></tr>
|
|
242
|
+
<tr><td style="background: #f9f9f9; padding: 16px; text-align: center; color: #888; font-size: 12px;">
|
|
243
|
+
© ${currentYear()} ${siteName}
|
|
244
|
+
</td></tr>
|
|
245
|
+
</table>
|
|
246
|
+
</body>
|
|
247
|
+
</html>
|
|
248
248
|
`,
|
|
249
249
|
text: `From: ${name} <${email}>\nSubject: ${subject}\n\n${message}`,
|
|
250
250
|
});
|
|
@@ -271,59 +271,59 @@ export async function sendSiteSettingsChangedEmail(params) {
|
|
|
271
271
|
const { data, error } = await sendConfiguredEmail({
|
|
272
272
|
to: adminEmails,
|
|
273
273
|
subject: `Site settings updated by ${changedByEmail}`,
|
|
274
|
-
html: `
|
|
275
|
-
<!DOCTYPE html>
|
|
276
|
-
<html>
|
|
277
|
-
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"></head>
|
|
278
|
-
<body style="margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;background-color:#f5f5f5;">
|
|
279
|
-
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:#f5f5f5;padding:40px 0;">
|
|
280
|
-
<tr><td align="center">
|
|
281
|
-
<table width="600" cellpadding="0" cellspacing="0" border="0" style="background-color:#ffffff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.1);">
|
|
282
|
-
<tr>
|
|
283
|
-
<td style="background:linear-gradient(135deg,#10b981 0%,#059669 100%);padding:40px;text-align:center;">
|
|
284
|
-
<p style="font-size:48px;margin:0 0 12px;">⚙️</p>
|
|
285
|
-
<h1 style="color:#ffffff;margin:0;font-size:24px;font-weight:600;">Site Settings Changed</h1>
|
|
286
|
-
<p style="color:#d1fae5;margin:8px 0 0;font-size:14px;">${timestamp}</p>
|
|
287
|
-
</td>
|
|
288
|
-
</tr>
|
|
289
|
-
<tr>
|
|
290
|
-
<td style="padding:40px;">
|
|
291
|
-
<p style="color:#333;font-size:16px;line-height:1.6;margin:0 0 20px;">
|
|
292
|
-
<strong>${changedByEmail}</strong> has updated the following site settings:
|
|
293
|
-
</p>
|
|
294
|
-
<table width="100%" cellpadding="12" cellspacing="0" border="0" style="background:#f8f9fa;border-radius:8px;margin:0 0 24px;">
|
|
295
|
-
<tr>
|
|
296
|
-
<td style="color:#6b7280;font-size:13px;font-weight:600;text-transform:uppercase;">Changed Fields</td>
|
|
297
|
-
<td style="color:#111827;font-size:14px;">${changedFields.map((field) => `<code style="background:#e5e7eb;padding:2px 6px;border-radius:3px;">${field}</code>`).join(" ")}</td>
|
|
298
|
-
</tr>
|
|
299
|
-
<tr>
|
|
300
|
-
<td style="color:#6b7280;font-size:13px;font-weight:600;text-transform:uppercase;">Changed By</td>
|
|
301
|
-
<td style="color:#111827;font-size:14px;">${changedByEmail}</td>
|
|
302
|
-
</tr>
|
|
303
|
-
<tr>
|
|
304
|
-
<td style="color:#6b7280;font-size:13px;font-weight:600;text-transform:uppercase;">Timestamp</td>
|
|
305
|
-
<td style="color:#111827;font-size:14px;">${timestamp}</td>
|
|
306
|
-
</tr>
|
|
307
|
-
</table>
|
|
308
|
-
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:24px 0;">
|
|
309
|
-
<tr><td align="center">
|
|
310
|
-
<a href="${settingsUrl}" style="display:inline-block;padding:14px 40px;background:linear-gradient(135deg,#10b981 0%,#059669 100%);color:#ffffff;text-decoration:none;border-radius:6px;font-size:15px;font-weight:600;">
|
|
311
|
-
View Settings
|
|
312
|
-
</a>
|
|
313
|
-
</td></tr>
|
|
314
|
-
</table>
|
|
315
|
-
</td>
|
|
316
|
-
</tr>
|
|
317
|
-
<tr>
|
|
318
|
-
<td style="background-color:#f8f9fa;padding:24px;text-align:center;border-top:1px solid #eeeeee;">
|
|
319
|
-
<p style="color:#9ca3af;font-size:12px;margin:0;">© ${currentYear()} ${siteName}. Automated notification — do not reply.</p>
|
|
320
|
-
</td>
|
|
321
|
-
</tr>
|
|
322
|
-
</table>
|
|
323
|
-
</td></tr>
|
|
324
|
-
</table>
|
|
325
|
-
</body>
|
|
326
|
-
</html>
|
|
274
|
+
html: `
|
|
275
|
+
<!DOCTYPE html>
|
|
276
|
+
<html>
|
|
277
|
+
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"></head>
|
|
278
|
+
<body style="margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;background-color:#f5f5f5;">
|
|
279
|
+
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:#f5f5f5;padding:40px 0;">
|
|
280
|
+
<tr><td align="center">
|
|
281
|
+
<table width="600" cellpadding="0" cellspacing="0" border="0" style="background-color:#ffffff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.1);">
|
|
282
|
+
<tr>
|
|
283
|
+
<td style="background:linear-gradient(135deg,#10b981 0%,#059669 100%);padding:40px;text-align:center;">
|
|
284
|
+
<p style="font-size:48px;margin:0 0 12px;">⚙️</p>
|
|
285
|
+
<h1 style="color:#ffffff;margin:0;font-size:24px;font-weight:600;">Site Settings Changed</h1>
|
|
286
|
+
<p style="color:#d1fae5;margin:8px 0 0;font-size:14px;">${timestamp}</p>
|
|
287
|
+
</td>
|
|
288
|
+
</tr>
|
|
289
|
+
<tr>
|
|
290
|
+
<td style="padding:40px;">
|
|
291
|
+
<p style="color:#333;font-size:16px;line-height:1.6;margin:0 0 20px;">
|
|
292
|
+
<strong>${changedByEmail}</strong> has updated the following site settings:
|
|
293
|
+
</p>
|
|
294
|
+
<table width="100%" cellpadding="12" cellspacing="0" border="0" style="background:#f8f9fa;border-radius:8px;margin:0 0 24px;">
|
|
295
|
+
<tr>
|
|
296
|
+
<td style="color:#6b7280;font-size:13px;font-weight:600;text-transform:uppercase;">Changed Fields</td>
|
|
297
|
+
<td style="color:#111827;font-size:14px;">${changedFields.map((field) => `<code style="background:#e5e7eb;padding:2px 6px;border-radius:3px;">${field}</code>`).join(" ")}</td>
|
|
298
|
+
</tr>
|
|
299
|
+
<tr>
|
|
300
|
+
<td style="color:#6b7280;font-size:13px;font-weight:600;text-transform:uppercase;">Changed By</td>
|
|
301
|
+
<td style="color:#111827;font-size:14px;">${changedByEmail}</td>
|
|
302
|
+
</tr>
|
|
303
|
+
<tr>
|
|
304
|
+
<td style="color:#6b7280;font-size:13px;font-weight:600;text-transform:uppercase;">Timestamp</td>
|
|
305
|
+
<td style="color:#111827;font-size:14px;">${timestamp}</td>
|
|
306
|
+
</tr>
|
|
307
|
+
</table>
|
|
308
|
+
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:24px 0;">
|
|
309
|
+
<tr><td align="center">
|
|
310
|
+
<a href="${settingsUrl}" style="display:inline-block;padding:14px 40px;background:linear-gradient(135deg,#10b981 0%,#059669 100%);color:#ffffff;text-decoration:none;border-radius:6px;font-size:15px;font-weight:600;">
|
|
311
|
+
View Settings
|
|
312
|
+
</a>
|
|
313
|
+
</td></tr>
|
|
314
|
+
</table>
|
|
315
|
+
</td>
|
|
316
|
+
</tr>
|
|
317
|
+
<tr>
|
|
318
|
+
<td style="background-color:#f8f9fa;padding:24px;text-align:center;border-top:1px solid #eeeeee;">
|
|
319
|
+
<p style="color:#9ca3af;font-size:12px;margin:0;">© ${currentYear()} ${siteName}. Automated notification — do not reply.</p>
|
|
320
|
+
</td>
|
|
321
|
+
</tr>
|
|
322
|
+
</table>
|
|
323
|
+
</td></tr>
|
|
324
|
+
</table>
|
|
325
|
+
</body>
|
|
326
|
+
</html>
|
|
327
327
|
`,
|
|
328
328
|
text: `Site settings updated by ${changedByEmail}\n\nChanged fields: ${changedFields.join(", ")}\n\nView at: ${settingsUrl}`,
|
|
329
329
|
});
|