@mohasinac/appkit 4.42.0 → 4.42.2
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/client/features/layout/DashboardLayoutClient.js +2 -30
- package/dist/_internal/server/features/offers/adapters.d.ts +29 -7
- package/dist/_internal/server/features/offers/adapters.js +14 -3
- package/dist/_internal/server/features/orders/adapters.js +54 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.js +1 -0
- package/dist/features/account/components/UserOrdersView.js +30 -2
- package/dist/features/account/hooks/useAddresses.d.ts +21 -6
- package/dist/features/account/hooks/useAddresses.js +1 -1
- package/dist/features/addresses/hooks/usePostalLookup.js +84 -12
- package/dist/features/admin/components/AdminCategoriesView.js +37 -2
- package/dist/features/admin/components/AdminOffersView.js +2 -2
- package/dist/features/admin/constants/filter-tabs.d.ts +25 -0
- package/dist/features/admin/constants/filter-tabs.js +30 -0
- package/dist/features/admin/hooks/useAdminListingData.d.ts +35 -0
- package/dist/features/admin/hooks/useAdminListingData.js +45 -9
- package/dist/features/cart/schemas/index.d.ts +15 -0
- package/dist/features/cart/schemas/index.js +2 -0
- package/dist/features/cart/types/index.d.ts +22 -0
- package/dist/features/categories/components/CategoriesIndexListing.js +19 -1
- package/dist/features/categories/components/CategoriesIndexPageView.js +14 -1
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +8 -0
- package/dist/features/classified/components/ClassifiedDetailPageView.js +1 -0
- package/dist/features/events/actions/event-actions.d.ts +13 -0
- package/dist/features/events/actions/event-actions.js +75 -1
- package/dist/features/events/repository/event-entry.repository.d.ts +17 -0
- package/dist/features/events/repository/event-entry.repository.js +36 -0
- package/dist/features/homepage/components/BrandsSection.js +1 -1
- package/dist/features/homepage/components/CustomerReviewsSection.js +1 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +1 -1
- package/dist/features/homepage/components/ShopByCategorySection.js +1 -1
- package/dist/features/layout/AppLayoutShell.js +2 -2
- package/dist/features/layout/BottomNavLayout.d.ts +9 -3
- package/dist/features/layout/BottomNavLayout.js +9 -3
- package/dist/features/media/upload/MediaUploadField.js +21 -3
- package/dist/features/orders/components/OrdersList.js +2 -1
- package/dist/features/orders/index.d.ts +1 -0
- package/dist/features/orders/index.js +1 -0
- package/dist/features/orders/types/index.d.ts +2 -0
- package/dist/features/orders/utils/order-ref.d.ts +45 -0
- package/dist/features/orders/utils/order-ref.js +58 -0
- package/dist/features/products/components/MakeOfferButton.d.ts +18 -1
- package/dist/features/products/components/MakeOfferButton.js +23 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +5 -0
- package/dist/features/products/components/ProductDetailPageView.js +3 -2
- package/dist/features/products/repository/products.repository.js +29 -2
- package/dist/features/seller/actions/offer-actions.js +32 -0
- package/dist/features/seller/components/QuickProductForm.js +15 -1
- package/dist/features/seller/components/SellerBidsView.js +3 -1
- package/dist/features/seller/components/SellerOffersView.js +2 -2
- package/dist/features/seller/components/SellerOrdersView.js +49 -6
- package/dist/features/seller/components/SellerProductsCards.js +23 -1
- package/dist/features/seller/components/SellerProductsView.js +27 -2
- package/dist/features/seller/hooks/useSellerListingData.d.ts +0 -1
- package/dist/features/seller/hooks/useSellerListingData.js +17 -19
- package/dist/features/shell/SectionForm.js +33 -2
- package/dist/features/tester/seed-data/_happy-path.d.ts +23 -0
- package/dist/features/tester/seed-data/_happy-path.js +710 -0
- package/dist/features/tester/seed-data/_money-flows.js +34 -23
- package/dist/features/tester/seed-data/authored/account-auth__profile-settings.js +22 -3
- package/dist/features/tester/seed-data/authored/addresses__address-filters.js +17 -12
- package/dist/features/tester/seed-data/authored/addresses__postal-lookup.js +7 -6
- package/dist/features/tester/seed-data/authored/addresses__postal-validation.js +10 -4
- package/dist/features/tester/seed-data/authored/addresses__state-picker.js +13 -8
- package/dist/features/tester/seed-data/authored/addresses__unban-request.js +49 -11
- package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.js +10 -6
- package/dist/features/tester/seed-data/authored/admin__bans-and-trust.js +71 -32
- package/dist/features/tester/seed-data/authored/admin__blog-faqs.js +80 -31
- package/dist/features/tester/seed-data/authored/admin__bug-hunter-rewards.js +62 -25
- package/dist/features/tester/seed-data/authored/admin__bulk-actions.js +61 -31
- package/dist/features/tester/seed-data/authored/admin__bundles.js +84 -37
- package/dist/features/tester/seed-data/authored/admin__buyer-data-admin.js +90 -57
- package/dist/features/tester/seed-data/authored/admin__catalog-listings.js +38 -26
- package/dist/features/tester/seed-data/authored/admin__category-brand-authoring.js +47 -28
- package/dist/features/tester/seed-data/authored/admin__classifieds-digitalcodes-live.js +118 -31
- package/dist/features/tester/seed-data/authored/admin__content-deletes.js +30 -24
- package/dist/features/tester/seed-data/authored/admin__content-marketing.js +83 -46
- package/dist/features/tester/seed-data/authored/admin__coupons.js +8 -8
- package/dist/features/tester/seed-data/authored/admin__events-raffles-spin.js +9 -9
- package/dist/features/tester/seed-data/authored/admin__firebase-function-effects.js +19 -4
- package/dist/features/tester/seed-data/authored/admin__media-watermark.js +77 -33
- package/dist/features/tester/seed-data/authored/admin__orders-fulfillment.js +5 -5
- package/dist/features/tester/seed-data/authored/admin__prize-draws-lotteries.js +29 -5
- package/dist/features/tester/seed-data/authored/admin__site-system.js +45 -23
- package/dist/features/tester/seed-data/authored/admin__uncovered-admin-pages.js +53 -28
- package/dist/features/tester/seed-data/authored/admin__users-trust.js +20 -20
- package/dist/features/tester/seed-data/authored/buying__bidding.js +19 -2
- package/dist/features/tester/seed-data/authored/buying__browsing-search.js +20 -5
- package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +28 -10
- package/dist/features/tester/seed-data/authored/buying__cart.js +10 -3
- package/dist/features/tester/seed-data/authored/buying__image-tile-layout.js +0 -16
- package/dist/features/tester/seed-data/authored/buying__my-orders.js +27 -5
- package/dist/features/tester/seed-data/authored/buying__offers.js +22 -7
- package/dist/features/tester/seed-data/authored/buying__order-detail-actions.js +3 -3
- package/dist/features/tester/seed-data/authored/buying__product-detail.js +35 -4
- package/dist/features/tester/seed-data/authored/buying__return-request.js +19 -6
- package/dist/features/tester/seed-data/authored/buying__reviews-pagination.js +36 -18
- package/dist/features/tester/seed-data/authored/buying__reviews.js +16 -1
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-navigation.js +8 -6
- package/dist/features/tester/seed-data/authored/buying__user-uncovered-pages.js +16 -1
- package/dist/features/tester/seed-data/authored/community-support__public-profile.js +3 -2
- package/dist/features/tester/seed-data/authored/community-support__support-tickets.js +4 -4
- package/dist/features/tester/seed-data/authored/content-discovery__blog.js +19 -2
- package/dist/features/tester/seed-data/authored/content-discovery__category-brand-relations.js +4 -4
- package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.js +5 -5
- package/dist/features/tester/seed-data/authored/content-discovery__coupons.js +41 -6
- package/dist/features/tester/seed-data/authored/content-discovery__event-detail-subroutes.js +63 -8
- package/dist/features/tester/seed-data/authored/content-discovery__event-participation.js +29 -29
- package/dist/features/tester/seed-data/authored/content-discovery__events.js +2 -2
- package/dist/features/tester/seed-data/authored/content-discovery__faq-help.js +23 -1
- package/dist/features/tester/seed-data/authored/content-discovery__notifications.js +6 -6
- package/dist/features/tester/seed-data/authored/content-discovery__search.js +1 -1
- package/dist/features/tester/seed-data/authored/content-discovery__store-reviews-aggregate.js +1 -1
- package/dist/features/tester/seed-data/authored/cta-layout__checkout-bottom-bar.js +31 -6
- package/dist/features/tester/seed-data/authored/cta-layout__dialog-footers.js +34 -10
- package/dist/features/tester/seed-data/authored/cta-layout__editor-action-bar.js +35 -8
- package/dist/features/tester/seed-data/authored/cta-layout__navbar-ctas.js +31 -9
- package/dist/features/tester/seed-data/authored/cta-layout__product-bottom-bar.js +29 -3
- package/dist/features/tester/seed-data/authored/design-ux__back-to-top-button.js +1 -1
- package/dist/features/tester/seed-data/authored/design-ux__carousel-arrow-bounds.js +10 -10
- package/dist/features/tester/seed-data/authored/design-ux__dashboard-layout.js +3 -3
- package/dist/features/tester/seed-data/authored/design-ux__footer-theme.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__form-validation-errors.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__general-design.js +4 -4
- package/dist/features/tester/seed-data/authored/design-ux__hand-mode-layout.js +4 -4
- package/dist/features/tester/seed-data/authored/design-ux__homepage-carousels.js +8 -8
- package/dist/features/tester/seed-data/authored/design-ux__status-badge-legibility.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__sticky-cta-bar.js +2 -2
- package/dist/features/tester/seed-data/authored/page-wiring__data-loss.js +39 -16
- package/dist/features/tester/seed-data/authored/page-wiring__detail-pages.js +43 -8
- package/dist/features/tester/seed-data/authored/page-wiring__drawer-pages.js +40 -14
- package/dist/features/tester/seed-data/authored/page-wiring__reachability.js +31 -14
- package/dist/features/tester/seed-data/authored/public-pages__auth-error-pages.js +7 -6
- package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.js +14 -5
- package/dist/features/tester/seed-data/authored/public-pages__core-listing-pages.js +3 -3
- package/dist/features/tester/seed-data/authored/public-pages__help-how-it-works.js +1 -1
- package/dist/features/tester/seed-data/authored/public-pages__help-scams-guides-subpages.js +4 -2
- package/dist/features/tester/seed-data/authored/public-pages__legal-policy-pages.js +3 -3
- package/dist/features/tester/seed-data/authored/public-pages__newly-wired-browse-indexes.js +5 -5
- package/dist/features/tester/seed-data/authored/public-pages__store-tabs.js +5 -5
- package/dist/features/tester/seed-data/authored/public-pages__stores-sellers-directories.js +3 -3
- package/dist/features/tester/seed-data/authored/search-and-nav__employee-permissions.js +45 -11
- package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +57 -17
- package/dist/features/tester/seed-data/authored/search-and-nav__settings-deep-links.js +36 -5
- package/dist/features/tester/seed-data/authored/search-and-nav__sidebar-search.js +29 -4
- package/dist/features/tester/seed-data/authored/selling__become-seller.js +23 -0
- package/dist/features/tester/seed-data/authored/selling__final-sale-authoring.js +14 -13
- package/dist/features/tester/seed-data/authored/selling__listing-a-product.js +14 -9
- package/dist/features/tester/seed-data/authored/selling__listing-edit-roundtrip.js +34 -7
- package/dist/features/tester/seed-data/authored/selling__listing-lifecycle.js +17 -7
- package/dist/features/tester/seed-data/authored/selling__listing-type-fields-roundtrip.js +9 -9
- package/dist/features/tester/seed-data/authored/selling__media-limits.js +9 -4
- package/dist/features/tester/seed-data/authored/selling__product-upload-details.js +3 -3
- package/dist/features/tester/seed-data/authored/selling__quick-add-minimum-details.js +3 -3
- package/dist/features/tester/seed-data/authored/selling__sectionised-forms.js +25 -1
- package/dist/features/tester/seed-data/authored/selling__seller-analytics-payouts.js +27 -6
- package/dist/features/tester/seed-data/authored/selling__seller-bids-bundles-filters.js +19 -1
- package/dist/features/tester/seed-data/authored/selling__seller-catalog-org.js +63 -22
- package/dist/features/tester/seed-data/authored/selling__seller-custom-brands.js +9 -5
- package/dist/features/tester/seed-data/authored/selling__seller-guide.js +7 -6
- package/dist/features/tester/seed-data/authored/selling__seller-listing-types.js +3 -2
- package/dist/features/tester/seed-data/authored/selling__seller-marketing-extras.js +18 -18
- package/dist/features/tester/seed-data/authored/selling__seller-ops-comms.js +13 -12
- package/dist/features/tester/seed-data/authored/selling__seller-orders-at-scale.js +19 -3
- package/dist/features/tester/seed-data/authored/selling__seller-orders.js +22 -4
- package/dist/features/tester/seed-data/authored/selling__seller-shipping-payouts-setup.js +20 -8
- package/dist/features/tester/seed-data/authored/selling__store-dashboard-navigation.js +10 -9
- package/dist/features/tester/seed-data/authored/selling__store-uncovered-pages.js +2 -2
- package/dist/features/tester/seed-data/authored/seo__canonical-and-host.js +5 -5
- package/dist/features/tester/seed-data/authored/seo__og-images.js +1 -1
- package/dist/features/tester/seed-data/authored/seo__page-metadata.js +7 -7
- package/dist/features/tester/seed-data/authored/seo__sitemap-and-robots.js +3 -3
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +126 -24
- package/dist/features/tester/utils/phases.d.ts +17 -0
- package/dist/features/tester/utils/phases.js +29 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/seo/json-ld.d.ts +23 -0
- package/dist/seo/json-ld.js +3 -2
- package/dist/server.d.ts +1 -0
- package/dist/server.js +2 -0
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/PaginatedSelect.js +0 -0
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Badge, Button, Checkbox, Div, Grid, Row, Stack, Text, TextLink } from "../../../ui";
|
|
4
4
|
import { MediaImage } from "../../media/MediaImage";
|
|
5
5
|
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../../features/products/constants/action-defs";
|
|
6
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
6
7
|
import { listingBadgeVariant } from "../../products/utils/listing-badge-variant";
|
|
7
8
|
const __P = {
|
|
8
9
|
p3: "p-[var(--appkit-space-3)]",
|
|
@@ -17,6 +18,27 @@ function buildHref(row) {
|
|
|
17
18
|
return `/pre-orders/${row.id}`;
|
|
18
19
|
return `/products/${row.id}`;
|
|
19
20
|
}
|
|
21
|
+
/*
|
|
22
|
+
* 🛑 THE DELETE BUTTONS BELOW CARRY `action`, AND THAT IS WHAT CONFIRMS THEM.
|
|
23
|
+
*
|
|
24
|
+
* They took their LABEL from `ROW_ACTION_META[ROW_ACTION_ID.DELETE]` and
|
|
25
|
+
* passed no `action` prop, so `<Button>`'s confirmation gate — which fires
|
|
26
|
+
* only when `action.confirmation` is set — never engaged. One click on a
|
|
27
|
+
* row's Delete permanently removed a PUBLISHED listing with no dialog of any
|
|
28
|
+
* kind; measured on a real seller account, the row was gone immediately and
|
|
29
|
+
* still gone after a reload.
|
|
30
|
+
*
|
|
31
|
+
* Using the registry for the label while bypassing it for the confirmation is
|
|
32
|
+
* exactly the split Rule #7 describes, on the one action where that rule says
|
|
33
|
+
* the cost is data loss. `ACTIONS.STORE["delete-listing"]` already carried the
|
|
34
|
+
* copy ("Delete listing? / This listing will be permanently removed. This
|
|
35
|
+
* action cannot be undone.") and the bulk bar already resolved it correctly;
|
|
36
|
+
* only the row path did not.
|
|
37
|
+
*
|
|
38
|
+
* `variant="ghost"` stays explicit so the row keeps its quiet styling: Button
|
|
39
|
+
* resolves the variant as `variant ?? kindDefault`, so the caller wins and the
|
|
40
|
+
* danger kind drives the dialog without turning the row button red.
|
|
41
|
+
*/
|
|
20
42
|
export function SellerProductsCards({ view, rows, isLoading, listingKind, selectedIds, toggle, onEdit, onDuplicate, onDelete, }) {
|
|
21
43
|
const Container = view === "grid" ? Grid : Stack;
|
|
22
44
|
const containerProps = view === "grid"
|
|
@@ -25,6 +47,6 @@ export function SellerProductsCards({ view, rows, isLoading, listingKind, select
|
|
|
25
47
|
return (_jsxs(Container, { ...containerProps, children: [rows.length === 0 && !isLoading && (_jsx(Text, { className: "text-[var(--appkit-color-text-muted)] col-span-full", size: "sm", children: listingKind !== "all" ? `No ${listingKind} listings found` : "No products listed yet" })), rows.map((row) => {
|
|
26
48
|
const isSelected = selectedIds.has(row.id);
|
|
27
49
|
const href = buildHref(row);
|
|
28
|
-
return view === "grid" ? (_jsxs(Div, { className: "group relative bg-[var(--appkit-color-surface)] overflow-hidden", shadow: "hover-md", rounded: "xl", border: isSelected ? "strong" : "default", children: [_jsx(Div, { className: "absolute top-2 left-2 z-10", children: _jsx(Checkbox, { bare: true, checked: isSelected, onChange: () => toggle(row.id), className: "h-4 w-4 rounded border-[var(--appkit-color-border)]", onClick: (e) => e.stopPropagation(), "aria-label": "Select" }) }), _jsxs(TextLink, { href: href, className: "block", children: [_jsx(Div, { className: "w-full aspect-square", children: _jsx(MediaImage, { src: row.imageUrl, alt: row.primary, size: "card" }) }), _jsxs(Stack, { className: `${__P.p3}`, gap: "xs", children: [_jsx(Text, { className: "line-clamp-1", size: "sm", weight: "medium", children: row.primary }), _jsxs(Row, { gap: "sm", children: [_jsx(Badge, { variant: listingBadgeVariant(row.listingKind), children: row.listingKind }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] line-clamp-1", size: "xs", children: row.secondary })] })] })] }), _jsxs(Row, { className: "border-t border-[var(--appkit-color-border)]", justify: "end", gap: "xs", padding: "xs", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); onEdit(row); }, "aria-label": "Edit", children: ROW_ACTION_META[ROW_ACTION_ID.EDIT].label }), _jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); onDuplicate(row); }, "aria-label": "Duplicate", children: ROW_ACTION_META[ROW_ACTION_ID.DUPLICATE].label }), onDelete && (_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); onDelete(row); }, "aria-label": "Delete", children: ROW_ACTION_META[ROW_ACTION_ID.DELETE].label }))] })] }, row.id)) : (_jsxs(Row, { className: "bg-[var(--appkit-color-surface)] hover:bg-[var(--appkit-color-surface-raised)]", align: "center", gap: "3", rounded: "lg", padding: "inlineSm", border: isSelected ? "strong" : "default", children: [_jsx(Checkbox, { bare: true, checked: isSelected, onChange: () => toggle(row.id), className: "h-4 w-4 rounded border-[var(--appkit-color-border)]", "aria-label": "Select" }), _jsx(Div, { className: `w-12 h-12 ${__O.hidden}`, rounded: "default", children: _jsx(MediaImage, { src: row.imageUrl, alt: row.primary, size: "thumbnail" }) }), _jsxs(TextLink, { href: href, className: "flex-1 min-w-0", children: [_jsx(Text, { className: "line-clamp-1", size: "sm", weight: "medium", children: row.primary }), _jsxs(Row, { gap: "sm", children: [_jsx(Badge, { variant: listingBadgeVariant(row.listingKind), children: row.listingKind }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] line-clamp-1", size: "xs", children: row.secondary })] })] }), _jsxs(Row, { className: "flex-shrink-0", gap: "xs", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => onEdit(row), "aria-label": "Edit", children: ROW_ACTION_META[ROW_ACTION_ID.EDIT].label }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => onDuplicate(row), "aria-label": "Duplicate", children: ROW_ACTION_META[ROW_ACTION_ID.DUPLICATE].label }), onDelete && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => onDelete(row), "aria-label": "Delete", children: ROW_ACTION_META[ROW_ACTION_ID.DELETE].label }))] })] }, row.id));
|
|
50
|
+
return view === "grid" ? (_jsxs(Div, { className: "group relative bg-[var(--appkit-color-surface)] overflow-hidden", shadow: "hover-md", rounded: "xl", border: isSelected ? "strong" : "default", children: [_jsx(Div, { className: "absolute top-2 left-2 z-10", children: _jsx(Checkbox, { bare: true, checked: isSelected, onChange: () => toggle(row.id), className: "h-4 w-4 rounded border-[var(--appkit-color-border)]", onClick: (e) => e.stopPropagation(), "aria-label": "Select" }) }), _jsxs(TextLink, { href: href, className: "block", children: [_jsx(Div, { className: "w-full aspect-square", children: _jsx(MediaImage, { src: row.imageUrl, alt: row.primary, size: "card" }) }), _jsxs(Stack, { className: `${__P.p3}`, gap: "xs", children: [_jsx(Text, { className: "line-clamp-1", size: "sm", weight: "medium", children: row.primary }), _jsxs(Row, { gap: "sm", children: [_jsx(Badge, { variant: listingBadgeVariant(row.listingKind), children: row.listingKind }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] line-clamp-1", size: "xs", children: row.secondary })] })] })] }), _jsxs(Row, { className: "border-t border-[var(--appkit-color-border)]", justify: "end", gap: "xs", padding: "xs", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); onEdit(row); }, "aria-label": "Edit", children: ROW_ACTION_META[ROW_ACTION_ID.EDIT].label }), _jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); onDuplicate(row); }, "aria-label": "Duplicate", children: ROW_ACTION_META[ROW_ACTION_ID.DUPLICATE].label }), onDelete && (_jsx(Button, { variant: "ghost", size: "sm", action: ACTIONS.STORE["delete-listing"], onClick: (e) => { e.stopPropagation(); onDelete(row); }, "aria-label": "Delete", children: ROW_ACTION_META[ROW_ACTION_ID.DELETE].label }))] })] }, row.id)) : (_jsxs(Row, { className: "bg-[var(--appkit-color-surface)] hover:bg-[var(--appkit-color-surface-raised)]", align: "center", gap: "3", rounded: "lg", padding: "inlineSm", border: isSelected ? "strong" : "default", children: [_jsx(Checkbox, { bare: true, checked: isSelected, onChange: () => toggle(row.id), className: "h-4 w-4 rounded border-[var(--appkit-color-border)]", "aria-label": "Select" }), _jsx(Div, { className: `w-12 h-12 ${__O.hidden}`, rounded: "default", children: _jsx(MediaImage, { src: row.imageUrl, alt: row.primary, size: "thumbnail" }) }), _jsxs(TextLink, { href: href, className: "flex-1 min-w-0", children: [_jsx(Text, { className: "line-clamp-1", size: "sm", weight: "medium", children: row.primary }), _jsxs(Row, { gap: "sm", children: [_jsx(Badge, { variant: listingBadgeVariant(row.listingKind), children: row.listingKind }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] line-clamp-1", size: "xs", children: row.secondary })] })] }), _jsxs(Row, { className: "flex-shrink-0", gap: "xs", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => onEdit(row), "aria-label": "Edit", children: ROW_ACTION_META[ROW_ACTION_ID.EDIT].label }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => onDuplicate(row), "aria-label": "Duplicate", children: ROW_ACTION_META[ROW_ACTION_ID.DUPLICATE].label }), onDelete && (_jsx(Button, { variant: "ghost", size: "sm", action: ACTIONS.STORE["delete-listing"], onClick: () => onDelete(row), "aria-label": "Delete", children: ROW_ACTION_META[ROW_ACTION_ID.DELETE].label }))] })] }, row.id));
|
|
29
51
|
})] }));
|
|
30
52
|
}
|
|
@@ -17,7 +17,9 @@ import { SELLER_PRODUCT_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
|
17
17
|
import { ROUTES } from "../../../constants";
|
|
18
18
|
import { normalizeListingType } from "../../products/utils/listing-type";
|
|
19
19
|
import { useAvailabilityScope } from "../../products/hooks/useAvailabilityScope";
|
|
20
|
-
import { toRecordArray,
|
|
20
|
+
import { toRecordArray, toStringValue } from "../hooks/useSellerListingData";
|
|
21
|
+
// Defining module (Root Cause #18) — the seller-side duplicate was deleted for drift.
|
|
22
|
+
import { toRelativeDate } from "../../admin/hooks/useAdminListingData";
|
|
21
23
|
import { useListingTypeFlags } from "../../../react/hooks/useListingTypeFlags";
|
|
22
24
|
import { ALL_LISTING_TYPES } from "../../../_internal/shared/listing-types/feature-flags";
|
|
23
25
|
import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
|
|
@@ -267,17 +269,40 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, }) {
|
|
|
267
269
|
* the UI simply never presented it.
|
|
268
270
|
*/
|
|
269
271
|
const statusLabel = toStringValue(item.status, "draft");
|
|
272
|
+
/*
|
|
273
|
+
* 🛑 PRICE AND STOCK GO IN THE VISIBLE LINE TOO — same reason as the
|
|
274
|
+
* status note above, one field over.
|
|
275
|
+
*
|
|
276
|
+
* `price` was already mapped onto the row and correct, and this view
|
|
277
|
+
* renders only `primary`, `secondary`, the type chip and the action
|
|
278
|
+
* buttons — so it was never displayed. Measured on a real seller
|
|
279
|
+
* account with 127 listings: ZERO rupee amounts and zero stock figures
|
|
280
|
+
* anywhere in the list. To learn what one of their own listings costs
|
|
281
|
+
* or how many are left, a seller had to open it.
|
|
282
|
+
*
|
|
283
|
+
* Those are the two numbers a seller scans for, so price leads.
|
|
284
|
+
* `stockQuantity` was not mapped at all and is added here.
|
|
285
|
+
*
|
|
286
|
+
* An auction keeps its own summary (reserve / bids / ends) and is not
|
|
287
|
+
* given a list price: for an auction the meaningful money is the
|
|
288
|
+
* reserve and the current bid, both already in `auctionSecondary`, and
|
|
289
|
+
* a "price" beside them would read as a third, competing figure.
|
|
290
|
+
*/
|
|
291
|
+
const stockRaw = typeof item.stockQuantity === "number" ? item.stockQuantity : null;
|
|
292
|
+
const priceLabel = kind !== "auction" && priceRaw ? `₹${priceRaw.toLocaleString("en-IN")}` : "";
|
|
293
|
+
const stockLabel = stockRaw === null ? "" : `${stockRaw} in stock`;
|
|
270
294
|
const secondaryBase = auctionSecondary || toStringValue(item.condition, "");
|
|
271
295
|
return {
|
|
272
296
|
id: toStringValue(item.id, `product-${index}`),
|
|
273
297
|
primary: toStringValue(item.title ?? item.name, "Untitled product"),
|
|
274
|
-
secondary: [secondaryBase, statusLabel].filter(Boolean).join(" · "),
|
|
298
|
+
secondary: [priceLabel, stockLabel, secondaryBase, statusLabel].filter(Boolean).join(" · "),
|
|
275
299
|
status: statusLabel,
|
|
276
300
|
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
277
301
|
imageUrl: toStringValue(item.mainImage ?? item.images?.[0], undefined),
|
|
278
302
|
image: toStringValue(item.mainImage ?? item.images?.[0], undefined),
|
|
279
303
|
listingKind: kind,
|
|
280
304
|
price: priceRaw ? `₹${priceRaw.toLocaleString("en-IN")}` : "—",
|
|
305
|
+
stock: stockLabel || "—",
|
|
281
306
|
physicalLocation: item.physicalLocation &&
|
|
282
307
|
typeof item.physicalLocation.zone === "string"
|
|
283
308
|
? item.physicalLocation
|
|
@@ -14,5 +14,4 @@ type UnknownRecord = Record<string, JsonValue>;
|
|
|
14
14
|
export declare function toRecordArray(value: unknown): UnknownRecord[];
|
|
15
15
|
export declare function toStringValue(value: unknown, fallback?: string): string;
|
|
16
16
|
export declare function toCurrency(value: unknown): string;
|
|
17
|
-
export declare function toRelativeDate(value: unknown): string;
|
|
18
17
|
export {};
|
|
@@ -18,25 +18,23 @@ export function toCurrency(value) {
|
|
|
18
18
|
}
|
|
19
19
|
return formatCurrency(value);
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return date.toLocaleDateString("en-IN", { year: "numeric", month: "short", day: "numeric" });
|
|
39
|
-
}
|
|
21
|
+
/*
|
|
22
|
+
* 🛑 `toRelativeDate` WAS HERE AND IS GONE. Import it from
|
|
23
|
+
* `features/admin/hooks/useAdminListingData` — the defining module now, and
|
|
24
|
+
* the one 44 files already use (Root Cause #18: import from where a symbol is
|
|
25
|
+
* defined, never from a convenience copy).
|
|
26
|
+
*
|
|
27
|
+
* This copy had drifted from that one and the drift was live: for a FUTURE
|
|
28
|
+
* date, `deltaMs` is negative, so `deltaMs < minute` matched and this version
|
|
29
|
+
* answered **"just now"** while the admin version answered **"1m ago"** — two
|
|
30
|
+
* different wrong answers to "when is this deadline", depending on which file
|
|
31
|
+
* a view happened to import from. The admin one is fixed to say "in 48h"; a
|
|
32
|
+
* second implementation is how that fix would have been half-applied.
|
|
33
|
+
*
|
|
34
|
+
* The three sibling helpers below are also duplicated there. They are left
|
|
35
|
+
* alone deliberately: none of them is wrong, and collapsing them is an import
|
|
36
|
+
* rewrite across both feature trees rather than a bug fix.
|
|
37
|
+
*/
|
|
40
38
|
function parseDate(value) {
|
|
41
39
|
if (value instanceof Date)
|
|
42
40
|
return value;
|
|
@@ -10,6 +10,9 @@ import { useFormBottomActions } from "../layout/hooks/useFormBottomActions";
|
|
|
10
10
|
import { useSectionState } from "../account/hooks/useCollapsedSections";
|
|
11
11
|
import { FormSchemaContext } from "./FormShell";
|
|
12
12
|
import { EASE_OUT_MS } from "../../tokens/motion";
|
|
13
|
+
/* Stable identity — a fresh `{}` per render would break any memo a section
|
|
14
|
+
* render does on its `errors` prop. */
|
|
15
|
+
const EMPTY_ERRORS = {};
|
|
13
16
|
/** Sections in render order: required first, original order preserved within each group. */
|
|
14
17
|
export function orderSections(sections) {
|
|
15
18
|
return [...sections.filter((s) => s.required), ...sections.filter((s) => !s.required)];
|
|
@@ -118,6 +121,27 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
|
|
|
118
121
|
* a caller renders it outside a provider, which is legitimate.
|
|
119
122
|
*/
|
|
120
123
|
const shellCtx = useContext(FormShellContext);
|
|
124
|
+
/*
|
|
125
|
+
* 🛑 Has the user ASKED to save yet?
|
|
126
|
+
*
|
|
127
|
+
* Root Cause #74 gated `FormErrorSummary` on this and stopped there. The
|
|
128
|
+
* per-section "N issues" badges below were never gated, and the eleven views
|
|
129
|
+
* that mount SectionForm all call `validate(draft)` from a MOUNT EFFECT —
|
|
130
|
+
* which that entry is explicit must STAY, because it is what keeps the errors
|
|
131
|
+
* current as the user fixes fields after a failed save. So `fieldErrors` is
|
|
132
|
+
* fully populated on first paint, and "Add New Address" opened reading
|
|
133
|
+
* "3 issues" and "4 issues" in red against a form nobody had typed into.
|
|
134
|
+
*
|
|
135
|
+
* Gate the DISPLAY, never the computation — deleting the mount-effect
|
|
136
|
+
* validate is the obvious repair and it is the wrong one.
|
|
137
|
+
*
|
|
138
|
+
* Local state rather than `shellCtx.submitAttempted` alone: SectionForm owns
|
|
139
|
+
* the submit button, so it always knows, whereas `shellCtx` is legitimately
|
|
140
|
+
* null when a caller renders it outside a provider (see above). Both are
|
|
141
|
+
* consulted, so a provider-level attempt from the pinned mobile bar counts too.
|
|
142
|
+
*/
|
|
143
|
+
const [attemptedHere, setAttemptedHere] = useState(false);
|
|
144
|
+
const submitAttempted = attemptedHere || shellCtx?.submitAttempted === true;
|
|
121
145
|
const [fieldErrors, setFieldErrors] = useState({});
|
|
122
146
|
const [uncontrolledOpenIds, setUncontrolledOpenIds] = useState(() => orderSections(sections).filter((s) => s.required).map((s) => s.id));
|
|
123
147
|
/*
|
|
@@ -251,6 +275,7 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
|
|
|
251
275
|
* identically whichever the user presses.
|
|
252
276
|
*/
|
|
253
277
|
const handleSubmit = useCallback(() => {
|
|
278
|
+
setAttemptedHere(true);
|
|
254
279
|
shellCtx?.markSubmitAttempted();
|
|
255
280
|
/*
|
|
256
281
|
* Reveal the FIRST offending section — only the first. Expanding all five
|
|
@@ -328,9 +353,15 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
|
|
|
328
353
|
* <button><div><span><div> — invalid, since a button may only
|
|
329
354
|
* contain phrasing content. Same variant props, legal element.
|
|
330
355
|
*/
|
|
331
|
-
_jsxs(Row, { as: "span", align: "center", gap: "xs", className: "min-w-0", children: [_jsx(Span, { weight: "semibold", size: "sm", children: section.heading ?? section.label }), isRequired && _jsx(Span, { size: "xs", color: "muted", children: "Required" })] }), isCollapsed: isCollapsed, collapsible: !isRequired, onToggle: () => toggle(section.id), keepMounted: section.keepMounted, renderHeaderExtra:
|
|
356
|
+
_jsxs(Row, { as: "span", align: "center", gap: "xs", className: "min-w-0", children: [_jsx(Span, { weight: "semibold", size: "sm", children: section.heading ?? section.label }), isRequired && _jsx(Span, { size: "xs", color: "muted", children: "Required" })] }), isCollapsed: isCollapsed, collapsible: !isRequired, onToggle: () => toggle(section.id), keepMounted: section.keepMounted, renderHeaderExtra:
|
|
357
|
+
/* Only after the user has tried to save — see `submitAttempted` above. */
|
|
358
|
+
submitAttempted && errorCount > 0
|
|
332
359
|
? () => (_jsxs(Badge, { variant: "danger", children: [errorCount, " ", errorCount === 1 ? "issue" : "issues"] }))
|
|
333
|
-
: undefined, children: section.render({
|
|
360
|
+
: undefined, children: section.render({
|
|
361
|
+
values,
|
|
362
|
+
onChange: handleFieldChange,
|
|
363
|
+
errors: submitAttempted ? fieldErrors : EMPTY_ERRORS,
|
|
364
|
+
}) }, section.id));
|
|
334
365
|
}), !hideActions && (
|
|
335
366
|
/*
|
|
336
367
|
* `justify="between"` so a destructive action sits opposite Save
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { TesterCaseRole } from "../schemas";
|
|
2
|
+
interface HappyPathCase {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
/** Every role the case affects; per-role detail goes inline in the expectations. */
|
|
6
|
+
roles: TesterCaseRole[];
|
|
7
|
+
startPage?: string;
|
|
8
|
+
steps: string[];
|
|
9
|
+
inputs?: Record<string, string | number | boolean>;
|
|
10
|
+
expectedBehaviour: string;
|
|
11
|
+
expectedUiState: string;
|
|
12
|
+
expectedData?: Record<string, string | number | boolean>;
|
|
13
|
+
endResult: string;
|
|
14
|
+
href?: string;
|
|
15
|
+
}
|
|
16
|
+
interface HappyPathPage {
|
|
17
|
+
pageKey: string;
|
|
18
|
+
pageLabel: string;
|
|
19
|
+
href?: string;
|
|
20
|
+
cases: HappyPathCase[];
|
|
21
|
+
}
|
|
22
|
+
export declare const happyPathPages: HappyPathPage[];
|
|
23
|
+
export {};
|