@mohasinac/appkit 4.14.0 → 4.16.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/dist/_internal/server/features/auctions/actions.js +2 -2
- package/dist/_internal/server/features/cart/service.d.ts +3 -3
- package/dist/_internal/server/features/checkout/actions.js +19 -0
- package/dist/_internal/server/features/checkout/source-context.d.ts +5 -0
- package/dist/_internal/server/features/checkout/source-context.js +157 -0
- package/dist/_internal/server/features/offers/adapters.d.ts +103 -0
- package/dist/_internal/server/features/offers/adapters.js +88 -0
- package/dist/_internal/server/features/offers/index.d.ts +2 -0
- package/dist/_internal/server/features/offers/index.js +1 -0
- package/dist/_internal/server/features/orders/adapters.js +24 -0
- package/dist/_internal/server/jobs/core/offerExpiry.js +10 -10
- package/dist/_internal/shared/actions/action-registry.d.ts +9 -2
- package/dist/_internal/shared/actions/action-registry.js +458 -7
- package/dist/_internal/shared/features/auctions/schema.d.ts +14 -2
- package/dist/_internal/shared/features/auctions/schema.js +13 -1
- package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
- package/dist/_internal/shared/features/cart/schema.d.ts +25 -20
- package/dist/_internal/shared/features/cart/schema.js +15 -0
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +22 -22
- package/dist/_internal/shared/features/orders/schema.d.ts +4 -4
- package/dist/_internal/shared/features/products/schema.d.ts +27 -27
- package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
- package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
- package/dist/_internal/shared/history/history.d.ts +22 -0
- package/dist/_internal/shared/history/history.js +41 -1
- package/dist/client.d.ts +13 -2
- package/dist/client.js +20 -2
- package/dist/constants/field-names.d.ts +8 -6
- package/dist/constants/field-names.js +48 -6
- package/dist/contracts/extend.d.ts +29 -0
- package/dist/contracts/extend.js +20 -0
- package/dist/contracts/index.d.ts +2 -1
- package/dist/contracts/index.js +2 -0
- package/dist/features/account/components/UserBidsView.js +12 -4
- package/dist/features/account/components/UserReturnsView.d.ts +3 -0
- package/dist/features/account/components/UserReturnsView.js +17 -1
- package/dist/features/admin/components/AdminAuditLogView.js +1 -0
- package/dist/features/admin/components/AdminBidsView.js +15 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +65 -33
- package/dist/features/admin/components/AdminFeatureEditorView.js +35 -6
- package/dist/features/admin/components/AdminNavEditorView.js +15 -5
- package/dist/features/admin/components/AdminNewsletterView.js +18 -2
- package/dist/features/admin/components/AdminOffersView.js +62 -18
- package/dist/features/admin/components/AdminPaymentMethodsView.js +24 -4
- package/dist/features/admin/components/AdminScammerEditorView.d.ts +2 -1
- package/dist/features/admin/components/AdminScammerEditorView.js +3 -13
- package/dist/features/admin/components/AdminScammersView.js +3 -8
- package/dist/features/admin/components/AdminSectionsView.js +32 -23
- package/dist/features/admin/components/AdminSessionsView.js +17 -2
- package/dist/features/admin/components/AdminViewCards.js +24 -17
- package/dist/features/admin/components/DataListingView.js +15 -1
- package/dist/features/admin/components/DataTable.js +7 -17
- package/dist/features/admin/components/ViewAuditLogEntryModal.js +1 -0
- package/dist/features/admin/components/sections/adminSectionsTypes.d.ts +4 -2
- package/dist/features/admin/components/sections/adminSectionsTypes.js +45 -27
- package/dist/features/admin/constants/filter-tabs.d.ts +3 -0
- package/dist/features/admin/constants/filter-tabs.js +1 -0
- package/dist/features/admin/schemas/homepage-section-form.d.ts +22 -0
- package/dist/features/admin/schemas/homepage-section-form.js +68 -0
- package/dist/features/admin/schemas/nav-item-form.d.ts +74 -0
- package/dist/features/admin/schemas/nav-item-form.js +72 -0
- package/dist/features/auctions/components/AuctionBidsTable.d.ts +10 -1
- package/dist/features/auctions/components/AuctionBidsTable.js +22 -4
- package/dist/features/auctions/components/bid-detail-fields.d.ts +29 -0
- package/dist/features/auctions/components/bid-detail-fields.js +103 -0
- package/dist/features/auctions/schemas/index.d.ts +14 -14
- package/dist/features/audit-log/schemas/firestore.d.ts +14 -0
- package/dist/features/audit-log/schemas/firestore.js +7 -0
- package/dist/features/auth/schemas/index.d.ts +2 -2
- package/dist/features/blog/actions/blog-actions.d.ts +7 -7
- package/dist/features/blog/schemas/blog-form.d.ts +80 -0
- package/dist/features/blog/schemas/blog-form.js +100 -0
- package/dist/features/blog/schemas/index.d.ts +10 -10
- package/dist/features/cart/schemas/index.d.ts +3 -3
- package/dist/features/catalogue/schemas/validation.d.ts +12 -12
- package/dist/features/categories/schemas/index.d.ts +16 -16
- package/dist/features/collections/schemas/index.d.ts +8 -8
- package/dist/features/events/components/AdminEventEditorView.js +32 -54
- package/dist/features/events/components/AdminEventEntriesView.js +22 -3
- package/dist/features/events/schemas/event-form.d.ts +113 -0
- package/dist/features/events/schemas/event-form.js +82 -0
- package/dist/features/events/schemas/index.d.ts +2 -2
- package/dist/features/faq/actions/faq-actions.d.ts +6 -6
- package/dist/features/faq/schemas/index.d.ts +6 -6
- package/dist/features/grouped/schemas/grouped-listing-form.d.ts +101 -0
- package/dist/features/grouped/schemas/grouped-listing-form.js +89 -0
- package/dist/features/grouped/schemas/index.d.ts +4 -4
- package/dist/features/homepage/actions/homepage-section-actions.d.ts +11 -3
- package/dist/features/homepage/actions/homepage-section-actions.js +33 -1
- package/dist/features/homepage/schemas/index.d.ts +71 -40
- package/dist/features/homepage/schemas/index.js +24 -0
- package/dist/features/messages/schemas/index.d.ts +2 -2
- package/dist/features/orders/components/OrderStatusTimeline.d.ts +5 -1
- package/dist/features/orders/components/OrderStatusTimeline.js +39 -20
- package/dist/features/orders/repository/orders.repository.js +5 -0
- package/dist/features/orders/schemas/firestore.d.ts +36 -1
- package/dist/features/orders/schemas/index.d.ts +24 -24
- package/dist/features/orders/types/index.d.ts +35 -7
- package/dist/features/pre-orders/schemas/index.d.ts +4 -4
- package/dist/features/products/components/GroupSettingsPanel.js +5 -1
- package/dist/features/products/schemas/index.d.ts +38 -38
- package/dist/features/products/schemas/product-features.validators.d.ts +75 -2
- package/dist/features/products/schemas/product-features.validators.js +63 -0
- package/dist/features/promotions/schemas/index.d.ts +14 -14
- package/dist/features/reviews/schemas/index.d.ts +14 -14
- package/dist/features/scams/constants/scammer-status-display.d.ts +28 -0
- package/dist/features/scams/constants/scammer-status-display.js +79 -0
- package/dist/features/scams/schemas/index.d.ts +2 -2
- package/dist/features/search/schemas/index.d.ts +2 -2
- package/dist/features/seller/actions/offer-actions.js +32 -2
- package/dist/features/seller/components/OfferPhaseTimeline.d.ts +59 -0
- package/dist/features/seller/components/OfferPhaseTimeline.js +107 -0
- package/dist/features/seller/components/SellerBidsView.js +11 -4
- package/dist/features/seller/components/SellerCouponEditorView.js +17 -17
- package/dist/features/seller/components/SellerOffersView.js +2 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +38 -14
- package/dist/features/seller/repository/offer.repository.d.ts +69 -8
- package/dist/features/seller/repository/offer.repository.js +127 -23
- package/dist/features/seller/schemas/coupon-form.d.ts +72 -0
- package/dist/features/seller/schemas/coupon-form.js +116 -0
- package/dist/features/seller/schemas/firestore.d.ts +56 -2
- package/dist/features/seller/schemas/firestore.js +27 -0
- package/dist/features/seller/schemas/index.d.ts +2 -2
- package/dist/features/seller/schemas/offer-forms.d.ts +21 -0
- package/dist/features/seller/schemas/offer-forms.js +21 -0
- package/dist/features/shell/build-sections.d.ts +63 -0
- package/dist/features/shell/build-sections.js +207 -0
- package/dist/features/shell/field-ui-meta.d.ts +115 -0
- package/dist/features/shell/field-ui-meta.js +324 -0
- package/dist/features/shell/index.d.ts +4 -0
- package/dist/features/shell/index.js +2 -0
- package/dist/features/shipments/schemas/validation.d.ts +22 -22
- package/dist/features/status-history/components/StatusTimeline.d.ts +74 -0
- package/dist/features/status-history/components/StatusTimeline.js +46 -0
- package/dist/features/status-history/index.d.ts +11 -0
- package/dist/features/status-history/index.js +10 -0
- package/dist/features/store-extensions/components/ReviewDecisionModal.d.ts +27 -0
- package/dist/features/store-extensions/components/ReviewDecisionModal.js +82 -0
- package/dist/features/store-extensions/index.d.ts +12 -0
- package/dist/features/store-extensions/index.js +12 -0
- package/dist/features/store-extensions/schemas/admin-notification-form.d.ts +36 -0
- package/dist/features/store-extensions/schemas/admin-notification-form.js +114 -0
- package/dist/features/store-extensions/schemas/analytics-forms.d.ts +57 -0
- package/dist/features/store-extensions/schemas/analytics-forms.js +158 -0
- package/dist/features/store-extensions/schemas/custom-role-form.d.ts +85 -0
- package/dist/features/store-extensions/schemas/custom-role-form.js +118 -0
- package/dist/features/store-extensions/schemas/firestore.d.ts +21 -1
- package/dist/features/store-extensions/schemas/firestore.js +60 -0
- package/dist/features/store-extensions/schemas/google-config-form.d.ts +53 -0
- package/dist/features/store-extensions/schemas/google-config-form.js +105 -0
- package/dist/features/store-extensions/schemas/item-request-create-form.d.ts +28 -0
- package/dist/features/store-extensions/schemas/item-request-create-form.js +80 -0
- package/dist/features/store-extensions/schemas/listing-template-form.d.ts +78 -0
- package/dist/features/store-extensions/schemas/listing-template-form.js +87 -0
- package/dist/features/store-extensions/schemas/moderation-review-form.d.ts +49 -0
- package/dist/features/store-extensions/schemas/moderation-review-form.js +101 -0
- package/dist/features/store-extensions/schemas/payout-method-form.d.ts +153 -0
- package/dist/features/store-extensions/schemas/payout-method-form.js +155 -0
- package/dist/features/store-extensions/schemas/report-create-form.d.ts +25 -0
- package/dist/features/store-extensions/schemas/report-create-form.js +79 -0
- package/dist/features/store-extensions/schemas/report-review-form.d.ts +47 -0
- package/dist/features/store-extensions/schemas/report-review-form.js +111 -0
- package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +148 -0
- package/dist/features/store-extensions/schemas/shipping-config-form.js +104 -0
- package/dist/features/store-extensions/schemas/store-category-form.d.ts +54 -0
- package/dist/features/store-extensions/schemas/store-category-form.js +73 -0
- package/dist/features/stores/schemas/index.d.ts +4 -4
- package/dist/features/support/schemas/index.d.ts +4 -4
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +350 -0
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.js +12 -2
- package/dist/next/routing/route-map.d.ts +16 -2
- package/dist/next/routing/route-map.js +9 -2
- package/dist/schemas/firestore-value.d.ts +8 -0
- package/dist/schemas/firestore-value.js +44 -0
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/index.js +1 -1
- package/dist/schemas/registry.d.ts +734 -101
- package/dist/schemas/registry.js +64 -1
- package/dist/seed/bids-seed-data.js +30 -0
- package/dist/seed/offers-seed-data.js +212 -7
- package/dist/seed/orders-seed-data.js +336 -1
- package/dist/seed/products-auctions-seed-data.js +41 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +5 -0
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/DataTable.js +4 -2
- package/dist/ui/columns/build-columns.d.ts +6 -1
- package/dist/ui/columns/build-columns.js +53 -0
- package/dist/ui/columns/cell-renderers.d.ts +68 -0
- package/dist/ui/columns/cell-renderers.js +111 -0
- package/dist/ui/columns/column-renderers.d.ts +6 -1
- package/dist/ui/columns/column-renderers.js +6 -1
- package/dist/ui/columns/index.d.ts +2 -1
- package/dist/ui/columns/index.js +5 -1
- package/dist/ui/components/Button.js +27 -2
- package/dist/ui/components/RecordDetailModal.d.ts +10 -1
- package/dist/ui/components/RecordDetailModal.js +2 -2
- package/dist/ui/icons/icon-registry.d.ts +65 -0
- package/dist/ui/icons/icon-registry.js +115 -0
- package/package.json +2 -1
- package/scripts/node-esm-loader.mjs +65 -0
- package/scripts/seed-cli-loader.mjs +6 -49
- package/scripts/seed-cli.mjs +1 -1
|
@@ -6,23 +6,13 @@ import { useQueryClient } from "@tanstack/react-query";
|
|
|
6
6
|
import { Button, Code, Div, FormActions, HorizontalRule, Label, Row, Select, SideDrawer, Span, Stack, Text, Textarea, useToast } from "../../../ui";
|
|
7
7
|
import { apiClient } from "../../../http";
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
|
+
import { ScammerStatusValues, } from "../../scams/schemas/firestore";
|
|
10
|
+
import { SCAMMER_STATUS_BADGE, SCAMMER_STATUS_LABEL, SCAMMER_STATUS_OPTIONS, } from "../../scams/constants/scammer-status-display";
|
|
9
11
|
const __P = {
|
|
10
12
|
p3: "p-[var(--appkit-space-3)]",
|
|
11
13
|
p4: "p-[var(--appkit-space-4)]",
|
|
12
14
|
};
|
|
13
15
|
const CLS_SECTION_LABEL = "text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] uppercase tracking-wide";
|
|
14
|
-
const STATUS_OPTIONS = [
|
|
15
|
-
{ label: "Pending review", value: "pending_review" },
|
|
16
|
-
{ label: "Verified", value: "verified" },
|
|
17
|
-
{ label: "Rejected", value: "rejected" },
|
|
18
|
-
{ label: "Removed", value: "removed" },
|
|
19
|
-
];
|
|
20
|
-
const STATUS_COLOR = {
|
|
21
|
-
pending_review: "bg-warning-surface text-warning",
|
|
22
|
-
verified: "bg-success-surface text-success",
|
|
23
|
-
rejected: "bg-error-surface text-error",
|
|
24
|
-
removed: "bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text-muted)] bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text-muted)]",
|
|
25
|
-
};
|
|
26
16
|
export function AdminScammerEditorView({ open, onClose, scammerId, displayNames = [], scamType, description, phones = [], upiIds = [], currentStatus, verificationNote, reportedBy, reportedByAnon, }) {
|
|
27
17
|
const queryClient = useQueryClient();
|
|
28
18
|
const { showToast } = useToast();
|
|
@@ -63,5 +53,5 @@ export function AdminScammerEditorView({ open, onClose, scammerId, displayNames
|
|
|
63
53
|
showToast(err?.message ?? "Failed to delete profile.", "error");
|
|
64
54
|
},
|
|
65
55
|
});
|
|
66
|
-
return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: displayNames.length > 0 ? displayNames[0] : "Scammer Profile", children: _jsxs(Stack, { className: `${__P.p4}`, gap: "md", children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Span, { size: "xs", weight: "medium", className: `inline-flex ${
|
|
56
|
+
return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: displayNames.length > 0 ? displayNames[0] : "Scammer Profile", children: _jsxs(Stack, { className: `${__P.p4}`, gap: "md", children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Span, { size: "xs", weight: "medium", className: `inline-flex ${SCAMMER_STATUS_BADGE[currentStatus ?? ScammerStatusValues.PENDING_REVIEW]}`, rounded: "full", padding: "pill-sm-tall", children: SCAMMER_STATUS_LABEL[currentStatus ?? ScammerStatusValues.PENDING_REVIEW] }), scamType && (_jsx(Span, { size: "xs", rounded: "full", padding: "pill-sm-tall", surface: "subtle", color: "muted", children: scamType.replace(/_/g, " ") }))] }), displayNames.length > 0 && (_jsxs(Div, { children: [_jsx(Text, { className: "mb-1 tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Names / Aliases" }), _jsx(Row, { wrap: true, gap: "xs", children: displayNames.map((name, i) => (_jsx(Span, { size: "xs", rounded: "full", surface: "subtle", padding: "pill-sm", children: name }, i))) })] })), (phones.length > 0 || upiIds.length > 0) && (_jsxs(Div, { className: `${__P.p3}`, rounded: "lg", surface: "muted", border: "default", children: [phones.length > 0 && (_jsxs(Div, { className: "mb-2", children: [_jsx(Text, { className: CLS_SECTION_LABEL, children: "Phone numbers" }), _jsx(Row, { wrap: true, gap: "xs", children: phones.map((p, i) => (_jsx(Code, { size: "xs", rounded: "default", padding: "sm", surface: "subtle", children: p }, i))) })] })), upiIds.length > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { className: CLS_SECTION_LABEL, children: "UPI IDs" }), _jsx(Row, { wrap: true, gap: "xs", children: upiIds.map((u, i) => (_jsx(Code, { size: "xs", rounded: "default", padding: "sm", surface: "subtle", children: u }, i))) })] }))] })), description && (_jsxs(Div, { className: `${__P.p3}`, rounded: "lg", surface: "muted", border: "default", children: [_jsx(Text, { className: CLS_SECTION_LABEL, children: "Description" }), _jsx(Text, { className: "whitespace-pre-wrap", color: "primary", size: "sm", children: description })] })), _jsxs(Div, { textSize: "xs", color: "muted", children: ["Reported by:", " ", _jsx(Span, { weight: "medium", color: "muted", children: reportedByAnon ? "Anonymous" : (reportedBy ?? "Unknown") })] }), _jsx(HorizontalRule, { tone: "subtle", spacing: "comfortable" }), _jsx(Select, { label: "Status", options: SCAMMER_STATUS_OPTIONS, value: status, onValueChange: setStatus }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { className: "uppercase tracking-wide", color: "muted", size: "xs", weight: "semibold", children: "Verification note (internal)" }), _jsx(Textarea, { value: note, onChange: (e) => setNote(e.target.value), rows: 3, placeholder: "e.g. Verified via 3 independent reports\u2026" })] }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "danger", size: "sm", disabled: !scammerId, isLoading: deleteMutation.isPending, onClick: () => deleteMutation.mutate(), children: "Delete" }), _jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "button", isLoading: updateMutation.isPending, disabled: !scammerId || updateMutation.isPending, onClick: () => updateMutation.mutate(), children: "Save changes" })] })] }) }));
|
|
67
57
|
}
|
|
@@ -12,12 +12,7 @@ import { ADMIN_SCAMMER_STATUS_TABS } from "../constants/filter-tabs";
|
|
|
12
12
|
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
|
|
13
13
|
import { DataListingView } from "./DataListingView";
|
|
14
14
|
import { AdminScammerEditorView } from "./AdminScammerEditorView";
|
|
15
|
-
|
|
16
|
-
pending_review: "bg-warning-surface text-warning",
|
|
17
|
-
verified: "bg-success-surface text-success",
|
|
18
|
-
rejected: "bg-error-surface text-error",
|
|
19
|
-
removed: "bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text-muted)] bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text-muted)]",
|
|
20
|
-
};
|
|
15
|
+
import { SCAMMER_STATUS_BADGE, SCAMMER_STATUS_LABEL, toScammerStatus, } from "../../scams/constants/scammer-status-display";
|
|
21
16
|
const SCAMMER_COLUMNS = [
|
|
22
17
|
{
|
|
23
18
|
key: "primary",
|
|
@@ -28,7 +23,7 @@ const SCAMMER_COLUMNS = [
|
|
|
28
23
|
key: "status",
|
|
29
24
|
header: "Status",
|
|
30
25
|
className: "w-36",
|
|
31
|
-
render: (row) => (_jsx(Span, { className: `inline-flex ${
|
|
26
|
+
render: (row) => (_jsx(Span, { className: `inline-flex ${SCAMMER_STATUS_BADGE[row.status]}`, size: "xs", weight: "medium", rounded: "full", padding: "pill-sm-tall", children: SCAMMER_STATUS_LABEL[row.status] })),
|
|
32
27
|
},
|
|
33
28
|
{
|
|
34
29
|
key: "updatedAt",
|
|
@@ -72,7 +67,7 @@ export function AdminScammersView({ children, ...props }) {
|
|
|
72
67
|
]
|
|
73
68
|
.filter(Boolean)
|
|
74
69
|
.join(" · "),
|
|
75
|
-
status:
|
|
70
|
+
status: toScammerStatus(item.status),
|
|
76
71
|
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
77
72
|
_raw: item,
|
|
78
73
|
})),
|
|
@@ -7,6 +7,8 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
7
7
|
import { ProductInlineSelect } from "../../seller/components/ProductInlineSelect";
|
|
8
8
|
import { CategoryInlineSelect } from "../../seller/components/CategoryInlineSelect";
|
|
9
9
|
import { Button, Checkbox, Div, Form, FormActions, Input, Modal, Row, Select, Stack, Text, Textarea, useToast } from "../../../ui";
|
|
10
|
+
import { FormErrorSummary, applyZodIssues } from "../../../ui/forms";
|
|
11
|
+
import { homepageSectionFormSchema } from "../schemas/homepage-section-form";
|
|
10
12
|
import { apiClient } from "../../../http";
|
|
11
13
|
import { ADMIN_ENDPOINTS } from "../../../constants";
|
|
12
14
|
import { ROUTES } from "../../../next/routing/route-map";
|
|
@@ -954,27 +956,34 @@ export function AdminSectionsView({ children }) {
|
|
|
954
956
|
}));
|
|
955
957
|
const hasReorderChanges = reorderDraft.some((item) => sectionOrderMap.get(item.id) !== item.order);
|
|
956
958
|
const canUndoReorderChanges = reorderUndoStack.length > 0;
|
|
957
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { children: [_jsx(Text, { size: "base", weight: "semibold", color: "primary", children: "Homepage Sections" }), _jsx(Text, { size: "sm", color: "muted", children: "Manage homepage sections and their display order" })] }), _jsx(Row, { align: "center", gap: "sm", children: _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => setIsModalOpen(true), children: "Manage Sections" }) })] }), errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No sections found" })] }), _jsxs(Stack, { className: `mt-4 ${__P.p4}`, gap: "3", rounded: "xl", surface: "default", border: "default", children: [_jsxs(Row, { align: "center", justify: "between", gap: "3", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Reorder Sections" }), _jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: reindexDraft, disabled: reorderSections.isPending || reorderDraft.length === 0, children: "Reindex 1..N" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: undoReorderChange, disabled: reorderSections.isPending || !canUndoReorderChanges, children: "Undo unsaved" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: resetToServerOrder, disabled: reorderSections.isPending || !hasReorderChanges, children: "Reset to server" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => reorderSections.mutate(), disabled: !hasReorderChanges || reorderSections.isPending, children: reorderSections.isPending ? "Saving order..." : "Save order" })] })] }), reorderDraft.length === 0 ? (_jsx(Text, { size: "sm", color: "muted", children: "No sections to reorder." })) : (_jsx(Stack, { gap: "sm", children: reorderDraft.map((item, index) => (_jsxs(Div, { layout: "grid", gap: "2", align: "center", className: "grid-cols-[auto_1fr_auto_auto_auto]", rounded: "md", padding: "xs", border: "default", draggable: true, onDragStart: () => setDragIndex(index), onDragOver: (event) => event.preventDefault(), onDrop: () => handleReorderDrop(index), onDragEnd: () => setDragIndex(null), children: [_jsx(Text, { size: "sm", weight: "semibold", color: "muted", children: "\u2261" }), _jsxs(Text, { size: "sm", color: "primary", children: [item.type, " #", item.order] }), _jsx(Input, { type: "number", min: 1, max: reorderDraft.length, value: String(item.order), onChange: (event) => updateReorderItemOrder(index, Number(event.target.value) || 1), className: "w-24" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => moveReorderItem(index, -1), disabled: index === 0, children: "Up" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => moveReorderItem(index, 1), disabled: index === reorderDraft.length - 1, children: "Down" })] }, `reorder-${item.id}`))) }))] }), _jsx(Modal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), title: "Manage Homepage Section", size: "lg", children:
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
959
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { children: [_jsx(Text, { size: "base", weight: "semibold", color: "primary", children: "Homepage Sections" }), _jsx(Text, { size: "sm", color: "muted", children: "Manage homepage sections and their display order" })] }), _jsx(Row, { align: "center", gap: "sm", children: _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => setIsModalOpen(true), children: "Manage Sections" }) })] }), errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No sections found" })] }), _jsxs(Stack, { className: `mt-4 ${__P.p4}`, gap: "3", rounded: "xl", surface: "default", border: "default", children: [_jsxs(Row, { align: "center", justify: "between", gap: "3", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Reorder Sections" }), _jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: reindexDraft, disabled: reorderSections.isPending || reorderDraft.length === 0, children: "Reindex 1..N" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: undoReorderChange, disabled: reorderSections.isPending || !canUndoReorderChanges, children: "Undo unsaved" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: resetToServerOrder, disabled: reorderSections.isPending || !hasReorderChanges, children: "Reset to server" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => reorderSections.mutate(), disabled: !hasReorderChanges || reorderSections.isPending, children: reorderSections.isPending ? "Saving order..." : "Save order" })] })] }), reorderDraft.length === 0 ? (_jsx(Text, { size: "sm", color: "muted", children: "No sections to reorder." })) : (_jsx(Stack, { gap: "sm", children: reorderDraft.map((item, index) => (_jsxs(Div, { layout: "grid", gap: "2", align: "center", className: "grid-cols-[auto_1fr_auto_auto_auto]", rounded: "md", padding: "xs", border: "default", draggable: true, onDragStart: () => setDragIndex(index), onDragOver: (event) => event.preventDefault(), onDrop: () => handleReorderDrop(index), onDragEnd: () => setDragIndex(null), children: [_jsx(Text, { size: "sm", weight: "semibold", color: "muted", children: "\u2261" }), _jsxs(Text, { size: "sm", color: "primary", children: [item.type, " #", item.order] }), _jsx(Input, { type: "number", min: 1, max: reorderDraft.length, value: String(item.order), onChange: (event) => updateReorderItemOrder(index, Number(event.target.value) || 1), className: "w-24" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => moveReorderItem(index, -1), disabled: index === 0, children: "Up" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => moveReorderItem(index, 1), disabled: index === reorderDraft.length - 1, children: "Down" })] }, `reorder-${item.id}`))) }))] }), _jsx(Modal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), title: "Manage Homepage Section", size: "lg", children: _jsx(Form, { schema: homepageSectionFormSchema, onSubmit: (event) => event.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormErrorSummary, {}), _jsx(Select, { label: "Mode", value: mode, onValueChange: (value) => setMode(value), options: [
|
|
960
|
+
{ label: "Create new section", value: "create" },
|
|
961
|
+
{ label: "Edit existing section", value: "edit" },
|
|
962
|
+
] }), mode === "edit" ? (_jsx(Select, { label: "Section", value: selectedSectionId, onValueChange: setSelectedSectionId, placeholder: "Select section", options: sections.map((section) => ({
|
|
963
|
+
label: `${section.type} (#${section.order})`,
|
|
964
|
+
value: section.id,
|
|
965
|
+
})) })) : null, _jsx(Select, { label: "Section type", value: sectionType, onValueChange: (value) => {
|
|
966
|
+
setSectionType(value);
|
|
967
|
+
if (mode === "create") {
|
|
968
|
+
setSelectedSectionId("");
|
|
969
|
+
}
|
|
970
|
+
}, options: SECTION_TYPE_OPTIONS.map((type) => ({
|
|
971
|
+
label: type,
|
|
972
|
+
value: type,
|
|
973
|
+
})), disabled: mode === "edit" }), _jsx(Input, { label: "Order", type: "number", value: order, onChange: (event) => setOrder(event.target.value), placeholder: "Leave empty to auto-place" }), _jsx(Select, { label: "Enabled", value: enabled ? "true" : "false", onValueChange: (value) => setEnabled(value === "true"), options: [
|
|
974
|
+
{ label: "Enabled", value: "true" },
|
|
975
|
+
{ label: "Disabled", value: "false" },
|
|
976
|
+
] }), isTypedBuilder ? renderTypedBuilder() : null, _jsx(Textarea, { label: isTypedBuilder ? "Generated config (JSON preview)" : "Section config (JSON)", value: configJson, onChange: (event) => setConfigJson(event.target.value), rows: 10, readOnly: isTypedBuilder, helperText: isTypedBuilder
|
|
977
|
+
? "This JSON is generated from typed controls above."
|
|
978
|
+
: "Provide section config JSON for this section type." }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "outline", onClick: () => setIsModalOpen(false), children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", disabled: saveSection.isPending, onClick: () => {
|
|
979
|
+
clearErrors();
|
|
980
|
+
const parsed = homepageSectionFormSchema.safeParse({
|
|
981
|
+
sectionType, order, enabled, configJson,
|
|
982
|
+
});
|
|
983
|
+
if (!parsed.success) {
|
|
984
|
+
applyZodIssues(parsed.error.issues, setFieldError);
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
saveSection.mutate();
|
|
988
|
+
}, children: saveSection.isPending ? "Saving..." : mode === "create" ? "Create section" : "Update section" })] })] })) }) })] }));
|
|
980
989
|
}
|
|
@@ -5,7 +5,7 @@ import { SIEVE_OP, sieveFilter } from "@mohasinac/appkit/client";
|
|
|
5
5
|
import { sortBy } from "@mohasinac/appkit/client";
|
|
6
6
|
import React, { useState } from "react";
|
|
7
7
|
import { useQueryClient } from "@tanstack/react-query";
|
|
8
|
-
import { ConfirmDeleteModal, FilterChipGroup, ListingLayout, RowActionMenu, useToast } from "../../../ui";
|
|
8
|
+
import { ConfirmDeleteModal, FilterChipGroup, ListingLayout, RowActionMenu, useToast, RecordDetailModal, } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
10
|
import { ADMIN_BULK_ACTIONS, ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
|
|
11
11
|
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
|
|
@@ -20,6 +20,7 @@ function maskIp(ip) {
|
|
|
20
20
|
return ip.replace(/:\w+$/, ":*");
|
|
21
21
|
}
|
|
22
22
|
export function AdminSessionsView({ children, ...props }) {
|
|
23
|
+
const [detail, setDetail] = useState(null);
|
|
23
24
|
const queryClient = useQueryClient();
|
|
24
25
|
const { showToast } = useToast();
|
|
25
26
|
const [revokeTarget, setRevokeTarget] = useState(null);
|
|
@@ -53,6 +54,9 @@ export function AdminSessionsView({ children, ...props }) {
|
|
|
53
54
|
{ value: "lastActivity", label: "Least recent" },
|
|
54
55
|
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
55
56
|
],
|
|
57
|
+
// Opening a row is what makes the destructive actions beside it
|
|
58
|
+
// safe to offer at all.
|
|
59
|
+
onRowClick: (row) => setDetail(row),
|
|
56
60
|
mapRows: (response) => toRecordArray(response.sessions).map((item, index) => {
|
|
57
61
|
const deviceInfo = (item.deviceInfo ?? {});
|
|
58
62
|
const userLabel = toStringValue(item.user &&
|
|
@@ -60,6 +64,7 @@ export function AdminSessionsView({ children, ...props }) {
|
|
|
60
64
|
const ipMasked = maskIp(toStringValue(deviceInfo.ip, ""));
|
|
61
65
|
return {
|
|
62
66
|
id: toStringValue(item.id, `session-${index}`),
|
|
67
|
+
detail: item,
|
|
63
68
|
primary: userLabel,
|
|
64
69
|
secondary: [
|
|
65
70
|
toStringValue(deviceInfo.browser, "Unknown browser"),
|
|
@@ -94,7 +99,17 @@ export function AdminSessionsView({ children, ...props }) {
|
|
|
94
99
|
{ id: "true", label: "Active only" },
|
|
95
100
|
], value: pendingFilters.isActive || "", onChange: (v) => setPendingFilters((p) => ({ ...p, isActive: v })) })),
|
|
96
101
|
};
|
|
97
|
-
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(
|
|
102
|
+
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(RecordDetailModal, { isOpen: detail !== null, onClose: () => setDetail(null), title: toStringValue(detail?.detail?.userId, "Session"), fields: (() => {
|
|
103
|
+
const d = (detail?.detail ?? {});
|
|
104
|
+
return detail ? [
|
|
105
|
+
{ label: "User", value: toStringValue(d.userId, "—") },
|
|
106
|
+
{ label: "Active", value: d.isActive ? "Yes" : "No" },
|
|
107
|
+
{ label: "Last activity", value: toRelativeDate(d.lastActivity) },
|
|
108
|
+
{ label: "Expires", value: toRelativeDate(d.expiresAt) },
|
|
109
|
+
{ label: "Started", value: toRelativeDate(d.createdAt) },
|
|
110
|
+
{ label: "Session ID", value: detail?.id ?? "—" },
|
|
111
|
+
] : undefined;
|
|
112
|
+
})() }), _jsx(ConfirmDeleteModal, { isOpen: Boolean(revokeTarget), onClose: () => setRevokeTarget(null), onConfirm: () => {
|
|
98
113
|
if (revokeTarget)
|
|
99
114
|
revokeMutation.mutate(revokeTarget.id);
|
|
100
115
|
}, isDeleting: revokeMutation.isPending, confirmText: "Revoke", title: "Revoke session?", message: "This will sign the user out of this device immediately.", variant: "danger" })] }));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Checkbox, Div, Grid, Row, Span, Stack, Text } from "../../../ui";
|
|
4
|
-
import {
|
|
4
|
+
import { STATUS_TONE_CLASSES, renderStatusBadge } from "../../../ui/columns/cell-renderers";
|
|
5
5
|
import { MediaImage } from "../../media/MediaImage";
|
|
6
6
|
const __P = {
|
|
7
7
|
p3: "p-[var(--appkit-space-3)]",
|
|
@@ -9,26 +9,33 @@ const __P = {
|
|
|
9
9
|
const __O = {
|
|
10
10
|
hidden: "overflow-hidden",
|
|
11
11
|
};
|
|
12
|
-
const STATUS_TONE_CLASSES = {
|
|
13
|
-
success: "bg-success-surface text-success",
|
|
14
|
-
warning: "bg-warning-surface text-warning",
|
|
15
|
-
error: "bg-error-surface text-error",
|
|
16
|
-
info: "bg-info-surface text-info",
|
|
17
|
-
neutral: "bg-primary-50 text-primary-800 dark:bg-secondary-900/30 dark:text-secondary-300",
|
|
18
|
-
};
|
|
19
12
|
function RowAvatar({ image, alt, icon, size = "9" }) {
|
|
20
13
|
const dims = size === "12" ? "h-12 w-12" : "h-9 w-9";
|
|
21
14
|
return (_jsx(Div, { className: `relative ${dims} shrink-0`, overflow: "hidden", rounded: "full", children: _jsx(MediaImage, { src: image, alt: alt, size: "avatar", fallback: icon }) }));
|
|
22
15
|
}
|
|
16
|
+
/*
|
|
17
|
+
* Flag chips, all drawn from the shared tone map.
|
|
18
|
+
*
|
|
19
|
+
* Three defects lived in the previous literal list:
|
|
20
|
+
* - `isSold` carried TWO backgrounds and TWO inks on one element
|
|
21
|
+
* (`bg-…surface … bg-…surface-elevated …`). Tailwind emits all four and
|
|
22
|
+
* stylesheet order — not string order — decides the winner, so the painted
|
|
23
|
+
* colour was not what the list said. The identical bug was found and fixed
|
|
24
|
+
* on the scammer status badge the same week.
|
|
25
|
+
* - `isPromoted` was raw dual-theme purple (`bg-purple-100 … dark:bg-purple-900/30`),
|
|
26
|
+
* the hand-written theme branching that variants exist to absorb.
|
|
27
|
+
* - `featured` repeated its light classes under `dark:`, which is a no-op —
|
|
28
|
+
* `-surface` tokens already invert with the theme.
|
|
29
|
+
*/
|
|
23
30
|
const FLAG_BADGES = [
|
|
24
|
-
{ key: "featured", label: "Featured",
|
|
25
|
-
{ key: "isPromoted", label: "Promoted",
|
|
26
|
-
{ key: "isOnSale", label: "Sale",
|
|
27
|
-
|
|
31
|
+
{ key: "featured", label: "Featured", tone: "warning" },
|
|
32
|
+
{ key: "isPromoted", label: "Promoted", tone: "info" },
|
|
33
|
+
{ key: "isOnSale", label: "Sale", tone: "success" },
|
|
34
|
+
// Sold is the absence of availability, not a status outcome — neutral.
|
|
35
|
+
{ key: "isSold", label: "Sold", tone: "neutral" },
|
|
28
36
|
];
|
|
29
37
|
function StatusBadge({ status }) {
|
|
30
|
-
|
|
31
|
-
return (_jsx(Span, { size: "xs", weight: "medium", className: `inline-flex truncate max-w-[120px] ${STATUS_TONE_CLASSES[tone]}`, rounded: "full", padding: "pill-xs", children: status }));
|
|
38
|
+
return _jsx(_Fragment, { children: renderStatusBadge(status) });
|
|
32
39
|
}
|
|
33
40
|
function SkeletonCard({ view }) {
|
|
34
41
|
if (view === "list") {
|
|
@@ -47,14 +54,14 @@ function AdminCardItem({ row, view, selected, onToggleSelect, onRowClick, render
|
|
|
47
54
|
return (_jsxs(Row, { gap: "sm", className: [
|
|
48
55
|
"px-[var(--appkit-space-4)] py-[var(--appkit-space-3)] cursor-pointer transition-colors hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]/50",
|
|
49
56
|
selected ? "bg-primary-50/40 dark:bg-primary-900/10" : "",
|
|
50
|
-
].filter(Boolean).join(" "), onClick: handleClick, role: onRowClick ? "button" : undefined, children: [onToggleSelect && (_jsx(Div, { "data-no-row-click": true, className: "shrink-0", onClick: (e) => { e.stopPropagation(); onToggleSelect(row.id); }, children: _jsx(Checkbox, { bare: true, checked: selected, onChange: () => onToggleSelect(row.id), className: "h-4 w-4 rounded border-zinc-300 text-primary accent-primary cursor-pointer", "aria-label": `Select ${row.primary}` }) })), _jsx(RowAvatar, { image: row.image, alt: row.primary, icon: resourceIcon }), _jsxs(Stack, { gap: "none", className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", weight: "semibold", className: "truncate", color: "primary", children: row.primary }), _jsx(Text, { size: "xs", color: "muted", className: "truncate", children: row.secondary }), row.barcodeId && (_jsx(Text, { size: "xs", color: "faint", className: "truncate font-mono", children: row.barcodeId }))] }), flags.length > 0 && (_jsx(Row, { gap: "xs", className: "hidden sm:flex shrink-0", children: flags.map(({ key, label,
|
|
57
|
+
].filter(Boolean).join(" "), onClick: handleClick, role: onRowClick ? "button" : undefined, children: [onToggleSelect && (_jsx(Div, { "data-no-row-click": true, className: "shrink-0", onClick: (e) => { e.stopPropagation(); onToggleSelect(row.id); }, children: _jsx(Checkbox, { bare: true, checked: selected, onChange: () => onToggleSelect(row.id), className: "h-4 w-4 rounded border-zinc-300 text-primary accent-primary cursor-pointer", "aria-label": `Select ${row.primary}` }) })), _jsx(RowAvatar, { image: row.image, alt: row.primary, icon: resourceIcon }), _jsxs(Stack, { gap: "none", className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", weight: "semibold", className: "truncate", color: "primary", children: row.primary }), _jsx(Text, { size: "xs", color: "muted", className: "truncate", children: row.secondary }), row.barcodeId && (_jsx(Text, { size: "xs", color: "faint", className: "truncate font-mono", children: row.barcodeId }))] }), flags.length > 0 && (_jsx(Row, { gap: "xs", className: "hidden sm:flex shrink-0", children: flags.map(({ key, label, tone }) => (_jsx(Span, { padding: "pill-2xs", weight: "medium", className: `inline-flex text-[10px] ${STATUS_TONE_CLASSES[tone]}`, rounded: "full", children: label }, key))) })), _jsx(StatusBadge, { status: row.status }), _jsx(Span, { size: "xs", color: "muted", className: "hidden sm:block shrink-0 w-24", align: "end", children: row.updatedAt }), renderRowActions && (_jsx(Div, { "data-no-row-click": true, className: "shrink-0", onClick: (e) => e.stopPropagation(), children: renderRowActions(row) }))] }));
|
|
51
58
|
}
|
|
52
59
|
return (_jsxs(Div, { rounded: "xl", className: [
|
|
53
60
|
"border overflow-hidden cursor-pointer transition-all hover:shadow-md hover:-translate-y-0.5",
|
|
54
61
|
selected
|
|
55
62
|
? "border-primary ring-1 ring-primary/20 bg-primary-50/30 dark:bg-primary-900/10"
|
|
56
63
|
: "border-zinc-100 border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]",
|
|
57
|
-
].filter(Boolean).join(" "), onClick: handleClick, role: onRowClick ? "button" : undefined, children: [onToggleSelect && (_jsxs(Row, { gap: "xs", "data-no-row-click": true, paddingY: "t-sm", padding: "x-sm", onClick: (e) => { e.stopPropagation(); onToggleSelect(row.id); }, children: [_jsx(Checkbox, { bare: true, checked: selected, onChange: () => onToggleSelect(row.id), className: "h-4 w-4 rounded border-zinc-300 text-primary accent-primary cursor-pointer", "aria-label": `Select ${row.primary}` }), flags.length > 0 && (_jsx(Row, { gap: "xs", wrap: true, children: flags.map(({ key, label,
|
|
64
|
+
].filter(Boolean).join(" "), onClick: handleClick, role: onRowClick ? "button" : undefined, children: [onToggleSelect && (_jsxs(Row, { gap: "xs", "data-no-row-click": true, paddingY: "t-sm", padding: "x-sm", onClick: (e) => { e.stopPropagation(); onToggleSelect(row.id); }, children: [_jsx(Checkbox, { bare: true, checked: selected, onChange: () => onToggleSelect(row.id), className: "h-4 w-4 rounded border-zinc-300 text-primary accent-primary cursor-pointer", "aria-label": `Select ${row.primary}` }), flags.length > 0 && (_jsx(Row, { gap: "xs", wrap: true, children: flags.map(({ key, label, tone }) => (_jsx(Span, { padding: "pill-2xs", weight: "medium", className: `inline-flex text-[10px] ${STATUS_TONE_CLASSES[tone]}`, rounded: "full", children: label }, key))) }))] })), _jsxs(Stack, { gap: "xs", className: `${__P.p3}.5`, children: [_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(RowAvatar, { image: row.image, alt: row.primary, icon: resourceIcon, size: "12" }), _jsxs(Stack, { gap: "none", className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", weight: "semibold", className: "line-clamp-2 leading-snug", color: "primary", children: row.primary }), _jsx(Text, { size: "xs", color: "muted", className: "truncate", children: row.secondary })] })] }), row.barcodeId && (_jsx(Text, { size: "xs", color: "faint", className: "truncate font-mono", children: row.barcodeId })), _jsxs(Row, { justify: "between", gap: "xs", children: [_jsx(StatusBadge, { status: row.status }), _jsx(Span, { color: "muted", className: "text-[11px] shrink-0", children: row.updatedAt })] }), renderRowActions && (_jsx(Row, { "data-no-row-click": true, justify: "end", gap: "xs", className: "border-t border-[var(--appkit-color-border)] -mx-[var(--appkit-space-3)] -mb-[var(--appkit-space-3)] mt-[var(--appkit-space-1)] px-[var(--appkit-space-3)] py-[var(--appkit-space-2)]", onClick: (e) => e.stopPropagation(), children: renderRowActions(row) }))] })] }));
|
|
58
65
|
}
|
|
59
66
|
export function AdminViewCards({ rows, view, isLoading, emptyLabel = "No items found", onRowClick, selectedIdSet, onToggleSelect, renderRowActions, resourceIcon, }) {
|
|
60
67
|
if (isLoading) {
|
|
@@ -7,7 +7,21 @@ import { useAdminListing } from "../hooks/useAdminListing";
|
|
|
7
7
|
import { getResourceIcon } from "../../../ui/columns/column-renderers";
|
|
8
8
|
import { AdminViewCards } from "./AdminViewCards";
|
|
9
9
|
import { DataTable } from "./DataTable";
|
|
10
|
+
import { useBreakpoint } from "../../../react/hooks/useBreakpoint";
|
|
10
11
|
export function DataListingView({ config, }) {
|
|
12
|
+
/*
|
|
13
|
+
* Below `md` the table view is suppressed entirely and the list/card view is
|
|
14
|
+
* rendered instead.
|
|
15
|
+
*
|
|
16
|
+
* No amount of per-column hiding rescues a grid at 320px — even a two-column
|
|
17
|
+
* table wraps its cells, so rows grow to different heights and stop being
|
|
18
|
+
* scannable. `TableColumn.priority` handles density from `md` upward; this
|
|
19
|
+
* handles the case below it, where the answer is "not a table at all".
|
|
20
|
+
*
|
|
21
|
+
* The user's stored preference is untouched, so widening the window brings
|
|
22
|
+
* their table straight back.
|
|
23
|
+
*/
|
|
24
|
+
const { isMobile } = useBreakpoint();
|
|
11
25
|
const effectiveInitialView = config.initialView ?? "list";
|
|
12
26
|
const listing = useAdminListing({
|
|
13
27
|
...config,
|
|
@@ -66,7 +80,7 @@ export function DataListingView({ config, }) {
|
|
|
66
80
|
: undefined;
|
|
67
81
|
return (_jsxs(Div, { className: config.className ?? "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: config.searchPlaceholder, onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || config.defaultSort, sortOptions: config.sortOptions, onSortChange: (v) => table.set("sort", v), showTableView: !config.hideTableView, toggles: config.toggles, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: config.primaryAction || config.toolbarExtra ? (_jsxs(Row, { align: "center", gap: "sm", children: [config.toolbarExtra, config.primaryAction && (_jsxs(Button, { gap: "sm", size: "sm", onClick: () => config.primaryAction.onClick({
|
|
68
82
|
openCreatePanel: panel.openCreatePanel,
|
|
69
|
-
}), children: [config.primaryAction.icon ?? _jsx(Plus, { className: "h-4 w-4" }), config.primaryAction.label] }))] })) : undefined }), config.renderAboveContent?.(), bulkActionItems && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActionItems })), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p), pageSize: pageSize, onPageSizeChange: setPageSize, paginationConfig: { showPageSizeSelector: true, pageSizeOptions: [10, 25, 50, 100] } }) }) })), _jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), view === "table" && !config.hideTableView ? (_jsx(DataTable, { columns: config.columns, rows: rows, resourceIcon: resourceIcon, isLoading: isLoading, emptyLabel: config.emptyLabel ?? `No ${config.title.toLowerCase()} found`, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next
|
|
83
|
+
}), children: [config.primaryAction.icon ?? _jsx(Plus, { className: "h-4 w-4" }), config.primaryAction.label] }))] })) : undefined }), config.renderAboveContent?.(), bulkActionItems && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActionItems })), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p), pageSize: pageSize, onPageSizeChange: setPageSize, paginationConfig: { showPageSizeSelector: true, pageSizeOptions: [10, 25, 50, 100] } }) }) })), _jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), view === "table" && !config.hideTableView && !isMobile ? (_jsx(DataTable, { columns: config.columns, rows: rows, resourceIcon: resourceIcon, isLoading: isLoading, emptyLabel: config.emptyLabel ?? `No ${config.title.toLowerCase()} found`, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next
|
|
70
84
|
? selection.setSelectedIds(rows.map((r) => r.id))
|
|
71
85
|
: selection.clearSelection(), rowHrefTemplate: config.rowHrefTemplate, onRowClick: config.onRowClick
|
|
72
86
|
? (row) => config.onRowClick(row, {
|
|
@@ -1,36 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { BaseListingCard, Button, Checkbox, Div, Row, Span, Stack, Table, Tbody, Td, Text, Th, Thead, Tr } from "../../../ui";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { renderStatusBadge, renderThumbnail } from "../../../ui/columns/cell-renderers";
|
|
5
|
+
import { resolveColumnPriority } from "../../../ui/columns/build-columns";
|
|
6
|
+
import { COLUMN_PRIORITY_CLASS } from "../../../contracts/extend";
|
|
6
7
|
import { useLongPress } from "../../../react/hooks/useLongPress";
|
|
7
8
|
const __O = {
|
|
8
9
|
hidden: "overflow-hidden",
|
|
9
10
|
xAuto: "overflow-x-auto",
|
|
10
11
|
};
|
|
11
|
-
const STATUS_TONE_CLASSES = {
|
|
12
|
-
success: "bg-success-surface text-success",
|
|
13
|
-
warning: "bg-warning-surface text-warning",
|
|
14
|
-
error: "bg-error-surface text-error",
|
|
15
|
-
info: "bg-info-surface text-info",
|
|
16
|
-
neutral: "bg-primary-50 text-primary-800 dark:bg-secondary-900/30 dark:text-secondary-300",
|
|
17
|
-
};
|
|
18
12
|
function buildDefaultColumns(resourceIcon) {
|
|
19
13
|
return [
|
|
20
14
|
{
|
|
21
15
|
key: "primary",
|
|
22
16
|
header: "Name",
|
|
23
|
-
render: (row) => (_jsxs(Row, { gap: "sm", align: "center", children: [
|
|
17
|
+
render: (row) => (_jsxs(Row, { gap: "sm", align: "center", children: [renderThumbnail(typeof row.image === "string" ? row.image : undefined, String(row.primary ?? ""), { rounded: "full", fallback: resourceIcon }), _jsxs(Stack, { gap: "none", className: "min-w-0", children: [_jsx(Text, { weight: "medium", color: "primary", className: "truncate", children: String(row.primary ?? "") }), row.secondary ? _jsx(Text, { size: "xs", color: "muted", className: "truncate", children: String(row.secondary) }) : null] })] })),
|
|
24
18
|
},
|
|
25
19
|
{
|
|
26
20
|
key: "status",
|
|
27
21
|
header: "Status",
|
|
28
22
|
className: "w-32",
|
|
29
|
-
render: (row) =>
|
|
30
|
-
const status = String(row.status ?? "—");
|
|
31
|
-
const tone = getStatusTone(status);
|
|
32
|
-
return (_jsx(Span, { size: "xs", weight: "medium", className: `inline-flex ${STATUS_TONE_CLASSES[tone]}`, rounded: "full", padding: "pill-sm-tall", children: status }));
|
|
33
|
-
},
|
|
23
|
+
render: (row) => renderStatusBadge(row.status == null ? null : String(row.status)),
|
|
34
24
|
},
|
|
35
25
|
{
|
|
36
26
|
key: "updatedAt",
|
|
@@ -54,7 +44,7 @@ function SelectableRow({ row, columns, isSelected, onToggle, renderRowActions, o
|
|
|
54
44
|
}
|
|
55
45
|
: undefined;
|
|
56
46
|
const isInteractive = Boolean(onRowClick ?? rowHref);
|
|
57
|
-
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, onTouchCancel: selectionEnabled && !isSelected ? longPress.onTouchCancel : undefined, className: `hover:bg-[var(--appkit-color-bg)] dark:hover:bg-[var(--appkit-color-bg)] ${isInteractive ? "cursor-pointer" : ""} ${isSelected ? "bg-primary/5 dark:bg-primary/10" : ""}`, border: "default", children: [selectionEnabled && (_jsx(Td, { padding: "xs-3", className: "relative w-10", 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, { color: "muted", className: col.className ?? ""
|
|
47
|
+
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, onTouchCancel: selectionEnabled && !isSelected ? longPress.onTouchCancel : undefined, className: `hover:bg-[var(--appkit-color-bg)] dark:hover:bg-[var(--appkit-color-bg)] ${isInteractive ? "cursor-pointer" : ""} ${isSelected ? "bg-primary/5 dark:bg-primary/10" : ""}`, border: "default", children: [selectionEnabled && (_jsx(Td, { padding: "xs-3", className: "relative w-10", 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, { color: "muted", className: `${COLUMN_PRIORITY_CLASS[resolveColumnPriority(col)]} ${col.className ?? ""}`, padding: "md", children: col.render
|
|
58
48
|
? col.render(row)
|
|
59
49
|
: String(row[col.key] ?? "") }, col.key))), renderRowActions && (_jsx(Td, { padding: "xs-3", onClick: (e) => e.stopPropagation(), children: renderRowActions(row) }))] }));
|
|
60
50
|
}
|
|
@@ -62,7 +52,7 @@ export function DataTable({ columns: columnsProp, rows, isLoading, sortKey, sort
|
|
|
62
52
|
const columns = (columnsProp ?? buildDefaultColumns(resourceIcon));
|
|
63
53
|
const selectionEnabled = Boolean(onToggleSelect);
|
|
64
54
|
const allRowsSelected = selectionEnabled && rows.length > 0 && rows.every((r) => selectedIds?.has(r.id));
|
|
65
|
-
return (_jsxs(Div, { surface: "card", className: `${__O.hidden}`, children: [_jsx(Div, { className: `${__O.xAuto}`, children: _jsxs(Table, { "data-testid": "data-table", size: "sm", children: [_jsx(Thead, { children: _jsxs(Tr, { className: "bg-[var(--appkit-color-bg)] dark:bg-[var(--appkit-color-surface-elevated)]", border: "default", children: [selectionEnabled && (_jsx(Th, { padding: "xs-3", scope: "col", className: "w-10", children: onToggleSelectAll && (_jsx(Checkbox, { bare: true, "data-testid": "select-all-checkbox", "aria-label": allRowsSelected ? "Deselect all" : "Select all", checked: allRowsSelected, onChange: () => onToggleSelectAll(!allRowsSelected), className: "h-4 w-4 rounded border-[var(--appkit-color-border)] accent-zinc-900 dark:accent-zinc-100" })) })), columns.map((col) => (_jsxs(Th, { scope: "col", onClick: col.sortable && onSort ? () => onSort(col.key) : undefined, className: `text-left text-[var(--appkit-color-text)]
|
|
55
|
+
return (_jsxs(Div, { surface: "card", className: `${__O.hidden}`, children: [_jsx(Div, { className: `${__O.xAuto}`, children: _jsxs(Table, { "data-testid": "data-table", size: "sm", children: [_jsx(Thead, { children: _jsxs(Tr, { className: "bg-[var(--appkit-color-bg)] dark:bg-[var(--appkit-color-surface-elevated)]", border: "default", children: [selectionEnabled && (_jsx(Th, { padding: "xs-3", scope: "col", className: "w-10", children: onToggleSelectAll && (_jsx(Checkbox, { bare: true, "data-testid": "select-all-checkbox", "aria-label": allRowsSelected ? "Deselect all" : "Select all", checked: allRowsSelected, onChange: () => onToggleSelectAll(!allRowsSelected), className: "h-4 w-4 rounded border-[var(--appkit-color-border)] accent-zinc-900 dark:accent-zinc-100" })) })), columns.map((col) => (_jsxs(Th, { scope: "col", onClick: col.sortable && onSort ? () => onSort(col.key) : undefined, className: `text-left text-[var(--appkit-color-text)] ${col.sortable && onSort ? "cursor-pointer select-none hover:text-primary" : ""} ${COLUMN_PRIORITY_CLASS[resolveColumnPriority(col)]} ${col.className ?? ""}`, padding: "md", weight: "semibold", children: [col.header, col.sortable && sortKey === col.key && (_jsx(Span, { className: "ml-1", children: sortDir === "asc" ? "↑" : "↓" }))] }, col.key))), renderRowActions && _jsx(Th, { padding: "xs-3", scope: "col", className: "w-12" })] }) }), _jsx(Tbody, { children: isLoading ? (Array.from({ length: 5 }).map((_, i) => (_jsxs(Tr, { border: "default", children: [selectionEnabled && _jsx(Td, { padding: "xs-3", className: "w-10" }), columns.map((col) => (_jsx(Td, { padding: "md", className: COLUMN_PRIORITY_CLASS[resolveColumnPriority(col)], children: _jsx(Div, { className: "h-4 w-full animate-pulse bg-neutral-200", rounded: "default" }) }, col.key)))] }, i)))) : rows.length === 0 ? (_jsx(Tr, { children: _jsx(Td, { colSpan: columns.length + (selectionEnabled ? 1 : 0) + (renderRowActions ? 1 : 0), align: "center", color: "faint", paddingX: "md", paddingY: "md", children: emptyLabel }) })) : (rows.map((row) => (_jsx(SelectableRow, { row: row, columns: columns, isSelected: selectedIds?.has(row.id) ?? false, onToggle: onToggleSelect, renderRowActions: renderRowActions, onRowClick: onRowClick, rowHref: typeof rowHrefTemplate === "function"
|
|
66
56
|
? rowHrefTemplate(row)
|
|
67
57
|
: rowHrefTemplate?.replace("{id}", encodeURIComponent(row.id)), selectionEnabled: selectionEnabled }, row.id)))) })] }) }), totalPages > 1 && onPageChange && (_jsx(Row, { className: "border-t border-neutral-200", padding: "inline", align: "center", justify: "end", gap: "sm", 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-[length:var(--appkit-text-xs)] font-medium transition ${p === currentPage ? "bg-neutral-900 text-white" : "text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface)] hover:bg-[var(--appkit-color-surface-elevated)]"}`, children: p }, p))) }))] }));
|
|
68
58
|
}
|
|
@@ -12,6 +12,7 @@ const ACTION_LABELS = {
|
|
|
12
12
|
checkout_bypass: "Admin checkout bypass",
|
|
13
13
|
coupon_update: "Coupon updated",
|
|
14
14
|
payout_mark_paid: "Payout marked paid",
|
|
15
|
+
offer_cancel: "Offer cancelled",
|
|
15
16
|
store_status_change: "Store status changed",
|
|
16
17
|
user_role_change: "User role changed",
|
|
17
18
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { FirestoreDocument } from "@mohasinac/appkit";
|
|
2
|
-
|
|
3
|
-
export
|
|
2
|
+
import type { SectionType as CanonicalSectionType } from "../../../homepage/schemas/firestore";
|
|
3
|
+
export declare const SECTION_TYPE_OPTIONS: CanonicalSectionType[];
|
|
4
|
+
/** Re-exported, NOT re-declared. There is one `SectionType` in this codebase. */
|
|
5
|
+
export type SectionType = CanonicalSectionType;
|
|
4
6
|
export interface SectionPatchPayload {
|
|
5
7
|
enabled: boolean;
|
|
6
8
|
order?: number;
|
|
@@ -1,32 +1,50 @@
|
|
|
1
1
|
import { ROUTES } from "../../../../next/routing/route-map";
|
|
2
2
|
const LABEL_VERIFIED_SELLERS = "Verified Sellers";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
3
|
+
/*
|
|
4
|
+
* The admin dropdown's section types.
|
|
5
|
+
*
|
|
6
|
+
* 🛑 This used to be a bare `as const` array with its OWN `SectionType` derived
|
|
7
|
+
* from it — a second declaration shadowing the real `SectionType` in
|
|
8
|
+
* `features/homepage/schemas/firestore.ts`, which is how the two came to
|
|
9
|
+
* disagree with the create route (Recurrent Root Cause #36's shape, one union
|
|
10
|
+
* over). The route's own copy was missing `featured-bundles`, `prize-draws`,
|
|
11
|
+
* `event-raffles` and `collection-cards`, so choosing any of those four in this
|
|
12
|
+
* dropdown produced a 400 on save — four of twenty-five section types could be
|
|
13
|
+
* configured in full and never created.
|
|
14
|
+
*
|
|
15
|
+
* It is now a `Record<CanonicalSectionType, true>`, so a type added to the
|
|
16
|
+
* union and not offered here is a COMPILE error, and an entry here that is not
|
|
17
|
+
* a real section type is too. Declaration order is preserved because the array
|
|
18
|
+
* is derived from this map's keys, and that order is the dropdown's order.
|
|
19
|
+
*/
|
|
20
|
+
const SECTION_TYPE_PRESENCE = {
|
|
21
|
+
welcome: true,
|
|
22
|
+
stats: true,
|
|
23
|
+
"trust-indicators": true,
|
|
24
|
+
categories: true,
|
|
25
|
+
brands: true,
|
|
26
|
+
products: true,
|
|
27
|
+
"pre-orders": true,
|
|
28
|
+
auctions: true,
|
|
29
|
+
banner: true,
|
|
30
|
+
features: true,
|
|
31
|
+
reviews: true,
|
|
32
|
+
"whatsapp-community": true,
|
|
33
|
+
faq: true,
|
|
34
|
+
"blog-articles": true,
|
|
35
|
+
newsletter: true,
|
|
36
|
+
stores: true,
|
|
37
|
+
events: true,
|
|
38
|
+
"social-feed": true,
|
|
39
|
+
carousel: true,
|
|
40
|
+
"custom-cards": true,
|
|
41
|
+
"google-reviews": true,
|
|
42
|
+
"featured-bundles": true,
|
|
43
|
+
"prize-draws": true,
|
|
44
|
+
"event-raffles": true,
|
|
45
|
+
"collection-cards": true,
|
|
46
|
+
};
|
|
47
|
+
export const SECTION_TYPE_OPTIONS = Object.keys(SECTION_TYPE_PRESENCE);
|
|
30
48
|
export const DEFAULT_PRODUCTS_BUILDER = {
|
|
31
49
|
title: "Featured Products",
|
|
32
50
|
subtitle: "",
|
|
@@ -188,6 +188,9 @@ export declare const ADMIN_AUDIT_LOG_ACTION_TABS: readonly [{
|
|
|
188
188
|
}, {
|
|
189
189
|
readonly id: "payout_mark_paid";
|
|
190
190
|
readonly label: "Payout paid";
|
|
191
|
+
}, {
|
|
192
|
+
readonly id: "offer_cancel";
|
|
193
|
+
readonly label: "Offer cancelled";
|
|
191
194
|
}, {
|
|
192
195
|
readonly id: "store_status_change";
|
|
193
196
|
readonly label: "Store status";
|
|
@@ -105,6 +105,7 @@ export const ADMIN_AUDIT_LOG_ACTION_TABS = [
|
|
|
105
105
|
{ id: "checkout_bypass", label: "Checkout bypass" },
|
|
106
106
|
{ id: "coupon_update", label: "Coupon update" },
|
|
107
107
|
{ id: "payout_mark_paid", label: "Payout paid" },
|
|
108
|
+
{ id: "offer_cancel", label: "Offer cancelled" },
|
|
108
109
|
{ id: "store_status_change", label: "Store status" },
|
|
109
110
|
{ id: "user_role_change", label: "Role change" },
|
|
110
111
|
];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
|
|
4
|
+
* by schema instance and every zod wrapper returns a new one.
|
|
5
|
+
*/
|
|
6
|
+
export declare const homepageSectionFormSchema: z.ZodObject<{
|
|
7
|
+
sectionType: z.ZodString;
|
|
8
|
+
order: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
enabled: z.ZodBoolean;
|
|
10
|
+
configJson: z.ZodEffects<z.ZodString, string, string>;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
sectionType: string;
|
|
14
|
+
configJson: string;
|
|
15
|
+
order?: number | undefined;
|
|
16
|
+
}, {
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
sectionType: string;
|
|
19
|
+
configJson: string;
|
|
20
|
+
order?: number | undefined;
|
|
21
|
+
}>;
|
|
22
|
+
export type HomepageSectionFormValues = z.infer<typeof homepageSectionFormSchema>;
|