@mohasinac/appkit 4.60.1 → 4.62.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/.build-stamp.json +3 -3
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +19 -3
- package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +1 -1
- package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +1 -1
- package/dist/_internal/server/features/auctions/actions.d.ts +1 -1
- package/dist/_internal/server/features/auctions/actions.js +12 -1
- package/dist/_internal/server/features/blog/actions.d.ts +1 -1
- package/dist/_internal/server/features/blog/actions.js +12 -1
- package/dist/_internal/server/features/brands/actions.d.ts +1 -1
- package/dist/_internal/server/features/brands/actions.js +12 -1
- package/dist/_internal/server/features/cart/actions.d.ts +1 -1
- package/dist/_internal/server/features/cart/actions.js +12 -1
- package/dist/_internal/server/features/checkout/actions.js +0 -1
- package/dist/_internal/server/features/events/actions.d.ts +1 -1
- package/dist/_internal/server/features/events/actions.js +12 -1
- package/dist/_internal/server/features/gateway-events/redact.js +14 -2
- package/dist/_internal/server/features/history/actions.d.ts +1 -1
- package/dist/_internal/server/features/history/actions.js +12 -1
- package/dist/_internal/server/features/lottery/actions.d.ts +1 -1
- package/dist/_internal/server/features/lottery/actions.js +12 -1
- package/dist/_internal/server/features/orders/actions.d.ts +1 -1
- package/dist/_internal/server/features/orders/actions.js +12 -1
- package/dist/_internal/server/features/orders/cancellation-refund.js +0 -1
- package/dist/_internal/server/features/orders/service.js +31 -1
- package/dist/_internal/server/features/payouts/actions.d.ts +1 -1
- package/dist/_internal/server/features/payouts/actions.js +12 -1
- package/dist/_internal/server/features/points/award.js +9 -1
- package/dist/_internal/server/features/pre-orders/actions.d.ts +1 -1
- package/dist/_internal/server/features/pre-orders/actions.js +12 -1
- package/dist/_internal/server/features/prize-draws/actions.d.ts +1 -1
- package/dist/_internal/server/features/prize-draws/actions.js +12 -1
- package/dist/_internal/server/features/products/actions.d.ts +1 -1
- package/dist/_internal/server/features/products/actions.js +12 -1
- package/dist/_internal/server/features/promotions/actions.d.ts +1 -1
- package/dist/_internal/server/features/promotions/actions.js +12 -1
- package/dist/_internal/server/features/protection-claims/actions.d.ts +2 -2
- package/dist/_internal/server/features/protection-claims/actions.js +21 -12
- package/dist/_internal/server/features/raffle/actions.d.ts +1 -1
- package/dist/_internal/server/features/raffle/actions.js +12 -1
- package/dist/_internal/server/features/refunds/actions.d.ts +1 -3
- package/dist/_internal/server/features/refunds/actions.js +14 -2
- package/dist/_internal/server/features/reviews/actions.d.ts +1 -1
- package/dist/_internal/server/features/reviews/actions.js +12 -1
- package/dist/_internal/server/features/wishlist/actions.d.ts +1 -1
- package/dist/_internal/server/features/wishlist/actions.js +12 -1
- package/dist/_internal/server/jobs/core/obligationSweep.js +0 -1
- package/dist/_internal/server/jobs/core/paymentReconcile.js +93 -76
- package/dist/_internal/server/jobs/core/testDataCleanup.d.ts +1 -1
- package/dist/_internal/server/jobs/core/testDataCleanup.js +22 -1
- package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +16 -4
- package/dist/_internal/shared/actions/action-registry.js +149 -9
- package/dist/_internal/shared/features/promotions/schema.d.ts +66 -0
- package/dist/_internal/shared/features/promotions/schema.js +7 -0
- package/dist/_internal/shared/media/limits.d.ts +10 -0
- package/dist/_internal/shared/media/limits.js +10 -0
- package/dist/_internal/shared/obligations/deadlines.js +11 -1
- package/dist/_internal/shared/tester/window.d.ts +25 -0
- package/dist/_internal/shared/tester/window.js +66 -0
- package/dist/_internal/shared/types/action-result.js +15 -1
- package/dist/client/api/surface-error.js +16 -2
- package/dist/client/api/useApiMutation.js +17 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/constants/api-endpoints.d.ts +3 -0
- package/dist/constants/api-endpoints.js +1 -0
- package/dist/constants/field-names.d.ts +17 -0
- package/dist/constants/field-names.js +23 -0
- package/dist/core/server-action.js +19 -3
- package/dist/core/unit-of-work.js +15 -1
- package/dist/errors/error-mapping.js +26 -1
- package/dist/errors/firestore-codes.d.ts +37 -0
- package/dist/errors/firestore-codes.js +52 -0
- package/dist/errors/index.d.ts +0 -1
- package/dist/errors/index.js +12 -2
- package/dist/features/account/components/UserOffersPanel.js +5 -3
- package/dist/features/admin/components/AdminActionIndexView.js +100 -5
- package/dist/features/admin/components/AdminAddressClustersView.js +21 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +8 -8
- package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
- package/dist/features/admin/components/AdminCarouselView.js +96 -4
- package/dist/features/admin/components/AdminCouponEditorView.js +8 -0
- package/dist/features/admin/components/AdminFulfillmentView.js +3 -2
- package/dist/features/admin/components/AdminGatewayEventsView.js +2 -2
- package/dist/features/admin/components/AdminMediaView.js +36 -3
- package/dist/features/admin/components/AdminNavigationView.js +4 -1
- package/dist/features/admin/components/AdminOffersView.js +2 -1
- package/dist/features/admin/components/AdminPaymentClustersView.js +19 -2
- package/dist/features/admin/components/AdminProductEditorView.js +2 -2
- package/dist/features/admin/components/AdminSectionsView.d.ts +10 -0
- package/dist/features/admin/components/AdminSectionsView.js +486 -224
- package/dist/features/admin/components/DataListingView.js +2 -0
- package/dist/features/admin/components/ReferenceRecordEditor.js +1 -1
- package/dist/features/admin/components/analytics/AdminStatCard.js +4 -1
- package/dist/features/admin/hooks/useOptimisticRowFlag.d.ts +33 -0
- package/dist/features/admin/hooks/useOptimisticRowFlag.js +87 -0
- package/dist/features/admin/repository/notification.repository.d.ts +15 -0
- package/dist/features/admin/repository/notification.repository.js +32 -0
- package/dist/features/admin/repository/site-settings.repository.d.ts +2 -1
- package/dist/features/admin/repository/site-settings.repository.js +7 -0
- package/dist/features/admin/schemas/action-index-form.d.ts +34 -0
- package/dist/features/admin/schemas/action-index-form.js +115 -0
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +1 -1
- package/dist/features/auth/components/RegisterForm.js +2 -1
- package/dist/features/catalogue/actions/approve-catalogue-listing.d.ts +1 -1
- package/dist/features/catalogue/actions/approve-catalogue-listing.js +12 -1
- package/dist/features/catalogue/actions/list-from-catalogue.d.ts +1 -1
- package/dist/features/catalogue/actions/list-from-catalogue.js +12 -1
- package/dist/features/catalogue/actions/submit-for-approval.d.ts +1 -1
- package/dist/features/catalogue/actions/submit-for-approval.js +12 -1
- package/dist/features/catalogue/components/CatalogueItemEditorView.js +3 -3
- package/dist/features/categories/repository/price-observation.repository.d.ts +19 -0
- package/dist/features/categories/repository/price-observation.repository.js +21 -2
- package/dist/features/events/actions/event-actions.js +4 -1
- package/dist/features/events/components/AdminEventEditorView.js +79 -50
- package/dist/features/events/components/EventsListPageView.js +2 -1
- package/dist/features/events/schemas/firestore.d.ts +17 -0
- package/dist/features/events/schemas/firestore.js +1 -0
- package/dist/features/events/utils/sort-pinned.d.ts +33 -0
- package/dist/features/events/utils/sort-pinned.js +46 -0
- package/dist/features/gateway-events/repository/gateway-events.repository.js +3 -40
- package/dist/features/gateway-events/schemas/index.d.ts +2 -2
- package/dist/features/inventory/components/AdminStorageBoxesView.js +55 -39
- package/dist/features/inventory/components/ReceiveStationView.js +61 -17
- package/dist/features/inventory/repository/storage-boxes.repository.d.ts +3 -1
- package/dist/features/inventory/repository/storage-boxes.repository.js +36 -3
- package/dist/features/layout/hooks/useFormBottomActions.js +20 -1
- package/dist/features/obligations/repository/obligations.repository.js +10 -1
- package/dist/features/orders/components/EmiInstallmentsPanel.js +25 -7
- package/dist/features/orders/components/RefundRequestView.js +4 -4
- package/dist/features/orders/schemas/firestore.d.ts +19 -0
- package/dist/features/orders/schemas/firestore.js +19 -0
- package/dist/features/orders/schemas/index.d.ts +6 -6
- package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +1 -0
- package/dist/features/payment-cases/repository/payment-cases.repository.js +3 -14
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
- package/dist/features/products/components/GroupMemberPicker.js +4 -1
- package/dist/features/products/components/MarketplaceBundleCard.js +1 -1
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +1 -1
- package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
- package/dist/features/products/components/ProductDetailActions.js +2 -1
- package/dist/features/products/components/ProductForm.js +2 -2
- package/dist/features/products/components/ProductGrid.js +1 -1
- package/dist/features/promotions/repository/claimed-coupons.repository.js +31 -1
- package/dist/features/promotions/schemas/firestore.d.ts +22 -0
- package/dist/features/promotions/schemas/firestore.js +22 -0
- package/dist/features/promotions/schemas/index.d.ts +479 -6
- package/dist/features/promotions/schemas/index.js +153 -0
- package/dist/features/protection-claims/repository/protection-claims.repository.js +22 -15
- package/dist/features/seller/components/FulfillmentView.js +3 -2
- package/dist/features/seller/components/SellerAddressesView.js +32 -4
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +62 -3
- package/dist/features/seller/components/SellerBundlesView.js +53 -14
- package/dist/features/seller/components/SellerGroupedListingsView.js +23 -3
- package/dist/features/seller/components/SellerListingTypeView.js +56 -10
- package/dist/features/seller/components/SellerPayoutMethodsView.js +33 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +48 -9
- package/dist/features/seller/components/SellerProductsView.js +5 -1
- package/dist/features/seller/components/SellerShippingConfigsView.js +33 -1
- package/dist/features/seller/components/SellerShippingView.js +2 -1
- package/dist/features/seller/components/SellerStoreCategoriesView.js +33 -1
- package/dist/features/seller/components/SellerSupportView.js +69 -2
- package/dist/features/seller/schemas/coupon-form.d.ts +5 -0
- package/dist/features/seller/schemas/coupon-form.js +18 -0
- package/dist/features/shell/SectionForm.js +35 -9
- package/dist/features/shell/build-sections.js +14 -1
- package/dist/features/shipments/components/AdminShipmentLotItemsView.js +54 -1
- package/dist/features/shipments/components/AdminShipmentProjectionsView.js +3 -2
- package/dist/features/sticker-trace/components/StickerCommitPanel.js +2 -1
- package/dist/features/sticker-trace/components/StickerPrintPanel.js +2 -1
- package/dist/features/sticker-trace/components/StickerRetryPanel.js +24 -4
- package/dist/features/sticker-trace/components/StickerTraceStudio.js +2 -1
- package/dist/features/sticker-trace/server.d.ts +2 -0
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +3 -2
- package/dist/http/api-handler.js +4 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.js +6 -4
- package/dist/next/routing/route-map.d.ts +4 -2
- package/dist/next/routing/route-map.js +19 -1
- package/dist/schemas/registry.d.ts +15 -10
- package/dist/schemas/sieve.d.ts +3 -3
- package/dist/server.d.ts +1 -2
- package/dist/server.js +6 -4
- package/dist/styles.css +51 -0
- package/dist/ui/components/Checkbox.js +10 -2
- package/dist/ui/components/DateInput.js +8 -1
- package/dist/ui/components/Input.js +22 -2
- package/dist/ui/components/PaginatedSelect.d.ts +8 -19
- package/dist/ui/components/PaginatedSelect.js +0 -0
- package/dist/ui/components/Radio.js +8 -1
- package/dist/ui/components/Select.js +10 -2
- package/dist/ui/components/TagInput.d.ts +8 -1
- package/dist/ui/components/TagInput.js +4 -3
- package/dist/ui/components/Textarea.js +10 -2
- package/dist/ui/forms/FieldCheckbox.js +1 -1
- package/dist/ui/forms/FormErrorList.d.ts +0 -16
- package/dist/ui/forms/FormErrorList.js +34 -1
- package/dist/ui/forms/apply-zod-issues.d.ts +19 -1
- package/dist/ui/forms/apply-zod-issues.js +23 -1
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +1822 -1812
- package/dist/features/tester/utils/phases.d.ts +0 -48
- package/dist/features/tester/utils/phases.js +0 -139
|
@@ -325,6 +325,25 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
|
|
|
325
325
|
}
|
|
326
326
|
void onSubmit();
|
|
327
327
|
}, [shellCtx, onSubmit, erroringSectionIds, fieldErrors, openIds, setOpenIds, expandMode]);
|
|
328
|
+
/**
|
|
329
|
+
* Open a section if it is closed, then scroll to it.
|
|
330
|
+
*
|
|
331
|
+
* Named rather than inlined in the rail's `onClick`: inline it sat five
|
|
332
|
+
* braces deep inside the JSX, and it is the same open-then-scroll the
|
|
333
|
+
* submit handler above performs for the first erroring section — so the
|
|
334
|
+
* two now read as the same operation rather than two similar ones.
|
|
335
|
+
*
|
|
336
|
+
* 🛑 Opening is CONDITIONAL on purpose. In `single` mode an unconditional
|
|
337
|
+
* `setOpenIds([id])` would COLLAPSE the section you are already in when you
|
|
338
|
+
* click its own rail entry, so the rail would read as having closed the
|
|
339
|
+
* form.
|
|
340
|
+
*/
|
|
341
|
+
const openAndScrollTo = useCallback((id) => {
|
|
342
|
+
if (!openIds.includes(id)) {
|
|
343
|
+
setOpenIds(expandMode === "single" ? [id] : [...openIds, id]);
|
|
344
|
+
}
|
|
345
|
+
scrollToSection(id);
|
|
346
|
+
}, [openIds, setOpenIds, expandMode]);
|
|
328
347
|
useFormBottomActions({
|
|
329
348
|
onSubmit: handleSubmit,
|
|
330
349
|
onCancel,
|
|
@@ -408,21 +427,28 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
|
|
|
408
427
|
* comment syntax does not parse. That is the fourth time today; the rule is
|
|
409
428
|
* simply that JSX comments belong in CHILDREN position and nowhere else.
|
|
410
429
|
*/
|
|
411
|
-
const rail = showRail ? (_jsx(Div, { overflow: "auto",
|
|
430
|
+
const rail = showRail ? (_jsx(Div, { overflow: "auto",
|
|
431
|
+
/*
|
|
432
|
+
* <StickyToolbar> is a horizontal translucent bar: it ships `border-b`, a
|
|
433
|
+
* z-10 layer and the dismiss/re-expand CollapseStrip, and offers no
|
|
434
|
+
* `self-start`, `max-h` or overflow scrolling. Using it here would paint
|
|
435
|
+
* a bottom border under a 14rem sidebar and give it a horizontal bar's
|
|
436
|
+
* collapse affordance.
|
|
437
|
+
*
|
|
438
|
+
* 🛑 The marker below must stay on the line DIRECTLY above the className.
|
|
439
|
+
* Every one of these audits reads `lines[i - 1]` and nothing further, so
|
|
440
|
+
* a tidy explanation with the marker on its first line suppresses
|
|
441
|
+
* nothing while looking suppressed to a human (CLAUDE.md § Rule #5).
|
|
442
|
+
*/
|
|
443
|
+
// audit-raw-sticky-toolbar-ok: a vertical RAIL, not a toolbar — see above.
|
|
444
|
+
className: "hidden lg:block w-56 flex-shrink-0 sticky self-start top-[calc(var(--header-height,3.5rem)+1rem)] max-h-[calc(100vh-var(--header-height,3.5rem)-2rem)]", children: _jsx(Nav, { "aria-label": "Form sections", children: _jsx(Stack, { gap: "px", children: ordered.map((section) => {
|
|
412
445
|
const errorCount = errorCountFor(section);
|
|
413
446
|
const isOpen = section.required === true ||
|
|
414
447
|
openIds.includes(section.id) ||
|
|
415
448
|
forceOpenIds.includes(section.id);
|
|
416
449
|
return (_jsxs(Button, { type: "button",
|
|
417
450
|
/* `ghost`, not `bare` — UI_BUTTON has no `bare` variant. */
|
|
418
|
-
variant: "ghost", size: "sm", justify: "between", gap: "sm", className: `w-full text-left ${isOpen ? "appkit-section-rail__item--active" : ""}`, "aria-current": isOpen ? "true" : undefined, onClick: () => {
|
|
419
|
-
if (!openIds.includes(section.id)) {
|
|
420
|
-
setOpenIds(expandMode === "single"
|
|
421
|
-
? [section.id]
|
|
422
|
-
: [...openIds, section.id]);
|
|
423
|
-
}
|
|
424
|
-
scrollToSection(section.id);
|
|
425
|
-
}, children: [_jsx(Span, { size: "sm", color: isOpen ? "primary" : "muted", children: section.label }), errorCount > 0 && (_jsx(Span, { size: "xs", color: "error", weight: "semibold", children: errorCount }))] }, section.id));
|
|
451
|
+
variant: "ghost", size: "sm", justify: "between", gap: "sm", className: `w-full text-left ${isOpen ? "appkit-section-rail__item--active" : ""}`, "aria-current": isOpen ? "true" : undefined, onClick: () => openAndScrollTo(section.id), children: [_jsx(Span, { size: "sm", color: isOpen ? "primary" : "muted", children: section.label }), submitAttempted && errorCount > 0 && (_jsx(Span, { size: "xs", color: "error", weight: "semibold", children: errorCount }))] }, section.id));
|
|
426
452
|
}) }) }) })) : null;
|
|
427
453
|
const panels = (_jsxs(Stack, { gap: "md", children: [ordered.map((section) => {
|
|
428
454
|
const isRequired = section.required === true;
|
|
@@ -288,7 +288,20 @@ function renderField(field, ctx, opts) {
|
|
|
288
288
|
* case that most needs searching.
|
|
289
289
|
*/
|
|
290
290
|
if (options.length > PAGINATED_SELECT_THRESHOLD) {
|
|
291
|
-
|
|
291
|
+
/*
|
|
292
|
+
* 🛑 The error block here used to be hand-rolled, and was the only
|
|
293
|
+
* field in this generator whose error was not wired like the rest:
|
|
294
|
+
* a `<Text>` with no `id`, nothing pointing `aria-describedby` at it,
|
|
295
|
+
* no `aria-invalid` on the control, and — unlike every `Field*`
|
|
296
|
+
* branch below — the help text was still rendered underneath the
|
|
297
|
+
* error rather than replaced by it.
|
|
298
|
+
*
|
|
299
|
+
* `PaginatedSelect` now takes `name`/`error` and owns all of that,
|
|
300
|
+
* so hand it the error and delete the copy. This is the one branch
|
|
301
|
+
* that every schema-generated form shares, so the fix lands on all
|
|
302
|
+
* of them at once rather than per call site.
|
|
303
|
+
*/
|
|
304
|
+
return (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "sm", weight: "medium", color: "muted", children: [label, required ? " *" : ""] }), _jsx(PaginatedSelect, { name: name, error: ctx.errors[name], value: typeof value === "string" ? value : null, onChange: (v) => set(v ?? ""), options: options, ariaLabel: label, placeholder: `Select ${label.toLowerCase()}…` }), meta.help && !ctx.errors[name] && (_jsx(Text, { size: "xs", color: "muted", children: meta.help }))] }, name));
|
|
292
305
|
}
|
|
293
306
|
return (_jsx(FieldSelect, { name: name, label: label, hint: meta.help, required: required, value: typeof value === "string" ? value : "", options: options, onChange: set }, name));
|
|
294
307
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
|
+
import { z } from "zod";
|
|
4
5
|
import { useApiMutation } from "@mohasinac/appkit/client";
|
|
5
6
|
import { Alert, Badge, Button, Div, Heading, Modal, Pagination, Skeleton, Stack, Table, Thead, Tbody, Tr, Th, Td, Text, Textarea, } from "../../../ui/index.js";
|
|
6
7
|
import { apiClient } from "../../../http/index.js";
|
|
@@ -11,6 +12,7 @@ import { applyZodIssues } from "../../../ui/forms/apply-zod-issues.js";
|
|
|
11
12
|
import { ValidationError } from "../../../errors/validation-error.js";
|
|
12
13
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
13
14
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry.js";
|
|
15
|
+
import { QuickEditMenu } from "../../admin/components/QuickEditMenu.js";
|
|
14
16
|
import { useShipmentItems } from "../hooks/useShipments.js";
|
|
15
17
|
import { ShipmentItemLinkModal } from "./ShipmentItemLinkModal.js";
|
|
16
18
|
import { formatCurrency } from "../../../utils/number.formatter.js";
|
|
@@ -32,6 +34,30 @@ function parseBulkText(text) {
|
|
|
32
34
|
})
|
|
33
35
|
.filter((row) => row.title.length > 0);
|
|
34
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The three fields a lot item can be corrected on after import.
|
|
39
|
+
*
|
|
40
|
+
* Deliberately NOT the whole of `shipmentItemBaseSchema`: `isForSelfUse`
|
|
41
|
+
* decides whether `price` is required at all and flipping it from a
|
|
42
|
+
* three-field drawer would silently change what the row means to the
|
|
43
|
+
* projections, and `categorySlugs`/`brandSlug` belong to the link flow that
|
|
44
|
+
* already owns them.
|
|
45
|
+
*/
|
|
46
|
+
const shipmentItemQuickEditSchema = z.object({
|
|
47
|
+
title: z.string().trim().min(1, "Item title is required"),
|
|
48
|
+
quantity: z.coerce.number().int().min(1, "Quantity must be at least 1."),
|
|
49
|
+
price: z.coerce.number().min(0, "Price cannot be negative."),
|
|
50
|
+
});
|
|
51
|
+
const SHIPMENT_ITEM_QUICK_FIELDS = [
|
|
52
|
+
{ name: "title", label: "Title", type: "text", required: true },
|
|
53
|
+
{ name: "quantity", label: "Quantity", type: "number", required: true },
|
|
54
|
+
{
|
|
55
|
+
name: "price",
|
|
56
|
+
label: "Projected sale price",
|
|
57
|
+
type: "number",
|
|
58
|
+
helperText: "Feeds this lot's projected revenue, and every profit figure on the projections screen.",
|
|
59
|
+
},
|
|
60
|
+
];
|
|
35
61
|
export function AdminShipmentLotItemsView({ shipmentId, lotId }) {
|
|
36
62
|
const [page, setPage] = React.useState(1);
|
|
37
63
|
const [pageSize, setPageSize] = React.useState(50);
|
|
@@ -113,6 +139,19 @@ export function AdminShipmentLotItemsView({ shipmentId, lotId }) {
|
|
|
113
139
|
mutationFn: (itemId) => apiClient.delete(ADMIN_ENDPOINTS.SHIPMENT_LOT_ITEM_BY_ID(shipmentId, lotId, itemId)),
|
|
114
140
|
onSuccess: () => refetch(),
|
|
115
141
|
});
|
|
142
|
+
const editItemMutation = useApiMutation({
|
|
143
|
+
successMessage: "Item updated",
|
|
144
|
+
errorMessage: "Could not update that item.",
|
|
145
|
+
mutationFn: ({ itemId, values }) => {
|
|
146
|
+
const parsed = shipmentItemQuickEditSchema.parse(values);
|
|
147
|
+
return apiClient.patch(ADMIN_ENDPOINTS.SHIPMENT_LOT_ITEM_BY_ID(shipmentId, lotId, itemId), parsed);
|
|
148
|
+
},
|
|
149
|
+
onSuccess: () => refetch(),
|
|
150
|
+
});
|
|
151
|
+
/** Named so the row-action array stays a flat list of declarations. */
|
|
152
|
+
const saveItemEdit = React.useCallback(async (itemId, values) => {
|
|
153
|
+
await editItemMutation.mutateAsync({ itemId, values });
|
|
154
|
+
}, [editItemMutation]);
|
|
116
155
|
const unlinkItemMutation = useApiMutation({
|
|
117
156
|
successMessage: "Item unlinked",
|
|
118
157
|
mutationFn: (itemId) => apiClient.patch(ADMIN_ENDPOINTS.SHIPMENT_LOT_ITEM_BY_ID(shipmentId, lotId, itemId), {
|
|
@@ -134,7 +173,21 @@ export function AdminShipmentLotItemsView({ shipmentId, lotId }) {
|
|
|
134
173
|
return;
|
|
135
174
|
}
|
|
136
175
|
addItemMutation.mutate();
|
|
137
|
-
}, onValidationChange: () => itemForm.validate(itemDraft), schema: shipmentItemFormSchema, openIds: itemNav.openIds, onOpenChange: itemNav.setOpenIds, submitLabel: "Save Item", cancelLabel: "Cancel", onCancel: () => setShowAddItem(false), isLoading: addItemMutation.isPending })] })), isLoading ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Skeleton, { variant: "rectangular", height: "32px" }), _jsx(Skeleton, { variant: "rectangular", height: "32px" })] })) : items.length === 0 ? (_jsx(Text, { variant: "secondary", children: "No items tracked yet." })) : (_jsxs(Table, { children: [_jsx(Thead, { children: _jsxs(Tr, { children: [_jsx(Th, { children: "Title" }), _jsx(Th, { children: "Qty" }), _jsx(Th, { children: "Price" }), _jsx(Th, { children: "Status" }), _jsx(Th, {})] }) }), _jsx(Tbody, { children: items.map((item) => (_jsxs(Tr, { children: [_jsx(Td, { children: item.title }), _jsx(Td, { children: item.quantity }), _jsx(Td, { children: item.isForSelfUse ? "—" : formatCurrency(item.price ?? 0) }), _jsx(Td, { children: item.isForSelfUse ? (_jsx(Badge, { variant: "secondary", children: "Self use" })) : item.linkedProductId ? (_jsx(Badge, { variant: "success", children: "Linked" })) : (_jsx(Badge, { variant: "default", children: "Unlinked" })) }), _jsxs(Td, { className: "flex gap-[var(--appkit-space-2)]", children: [
|
|
176
|
+
}, onValidationChange: () => itemForm.validate(itemDraft), schema: shipmentItemFormSchema, openIds: itemNav.openIds, onOpenChange: itemNav.setOpenIds, submitLabel: "Save Item", cancelLabel: "Cancel", onCancel: () => setShowAddItem(false), isLoading: addItemMutation.isPending })] })), isLoading ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Skeleton, { variant: "rectangular", height: "32px" }), _jsx(Skeleton, { variant: "rectangular", height: "32px" })] })) : items.length === 0 ? (_jsx(Text, { variant: "secondary", children: "No items tracked yet." })) : (_jsxs(Table, { children: [_jsx(Thead, { children: _jsxs(Tr, { children: [_jsx(Th, { children: "Title" }), _jsx(Th, { children: "Qty" }), _jsx(Th, { children: "Price" }), _jsx(Th, { children: "Status" }), _jsx(Th, {})] }) }), _jsx(Tbody, { children: items.map((item) => (_jsxs(Tr, { children: [_jsx(Td, { children: item.title }), _jsx(Td, { children: item.quantity }), _jsx(Td, { children: item.isForSelfUse ? "—" : formatCurrency(item.price ?? 0) }), _jsx(Td, { children: item.isForSelfUse ? (_jsx(Badge, { variant: "secondary", children: "Self use" })) : item.linkedProductId ? (_jsx(Badge, { variant: "success", children: "Linked" })) : (_jsx(Badge, { variant: "default", children: "Unlinked" })) }), _jsxs(Td, { className: "flex gap-[var(--appkit-space-2)]", children: [_jsx(QuickEditMenu, { actions: [
|
|
177
|
+
{
|
|
178
|
+
label: "Edit item",
|
|
179
|
+
formTitle: `Edit "${item.title}"`,
|
|
180
|
+
schema: shipmentItemQuickEditSchema,
|
|
181
|
+
fields: SHIPMENT_ITEM_QUICK_FIELDS,
|
|
182
|
+
defaultValues: {
|
|
183
|
+
title: item.title,
|
|
184
|
+
quantity: item.quantity,
|
|
185
|
+
price: item.price ?? 0,
|
|
186
|
+
},
|
|
187
|
+
onSubmit: (values) => saveItemEdit(item.id, values),
|
|
188
|
+
submitLabel: "Save item",
|
|
189
|
+
},
|
|
190
|
+
] }), !item.isForSelfUse &&
|
|
138
191
|
(item.linkedProductId ? (_jsx(Button, { size: "sm", variant: "ghost", isLoading: unlinkItemMutation.isPending, onClick: () => unlinkItemMutation.mutate(item.id), children: "Unlink" })) : (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => setLinkTarget({ itemId: item.id, title: item.title }), children: "Create pre-order link" }))), _jsx(Button, { size: "sm", variant: "ghost", action: ACTIONS.SHIPMENT["delete"], isLoading: deleteItemMutation.isPending, onClick: () => deleteItemMutation.mutate(item.id), children: "Delete" })] })] }, item.id))) })] })), meta && meta.totalPages > 1 && (_jsx(Pagination, { currentPage: page, totalPages: meta.totalPages, onPageChange: setPage, pageSize: pageSize, onPageSizeChange: (size) => {
|
|
139
192
|
setPageSize(size);
|
|
140
193
|
setPage(1);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
|
-
import { Button, Div, Heading, Modal, Pagination, Select, Skeleton, Stack, Table, Thead, Tbody, Tr, Th, Td, Text } from "../../../ui/index.js";
|
|
4
|
+
import { Button, Div, Heading, Modal, Pagination, Select, Skeleton, Stack, Table, Thead, Tbody, Tr, Th, Td, Text, TextLink } from "../../../ui/index.js";
|
|
5
|
+
import { ROUTES } from "../../../next/routing/route-map.js";
|
|
5
6
|
import { useShipmentProjections, useShipmentItems } from "../hooks/useShipments.js";
|
|
6
7
|
import { ShipmentItemLinkModal } from "./ShipmentItemLinkModal.js";
|
|
7
8
|
import { formatCurrency } from "../../../utils/number.formatter.js";
|
|
@@ -25,7 +26,7 @@ export function AdminShipmentProjectionsView() {
|
|
|
25
26
|
const [sorts, setSorts] = React.useState(SORT_OPTIONS[0].value);
|
|
26
27
|
const { lots, meta, isLoading, refetch } = useShipmentProjections({ page, pageSize, sorts });
|
|
27
28
|
const [pickerLot, setPickerLot] = React.useState(null);
|
|
28
|
-
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { layout: "flex", align: "center", justify: "between", children: [_jsx(Heading, { level: 2, children: "Projections" }), _jsx(Select, { "aria-label": "Sort by", value: sorts, onValueChange: setSorts, options: SORT_OPTIONS })] }), isLoading ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Skeleton, { variant: "rectangular", height: "32px" }), _jsx(Skeleton, { variant: "rectangular", height: "32px" }), _jsx(Skeleton, { variant: "rectangular", height: "32px" })] })) : lots.length === 0 ? (_jsx(Text, { variant: "secondary", children: "No lots to project yet." })) : (_jsxs(Table, { children: [_jsx(Thead, { children: _jsxs(Tr, { children: [_jsx(Th, { children: "Lot" }), _jsx(Th, { children: "Items" }), _jsx(Th, { children: "Landed Cost" }), _jsx(Th, { children: "Projected Revenue" }), _jsx(Th, { children: "Projected Profit" }), _jsx(Th, {})] }) }), _jsx(Tbody, { children: lots.map((lot) => (_jsxs(Tr, { children: [_jsx(Td, { children: lot.lotName }), _jsx(Td, { children: lot.itemCount }), _jsx(Td, { children: formatCurrency(lot.totalLandedCost) }), _jsx(Td, { children: formatCurrency(lot.projectedRevenue) }), _jsx(Td, { className: lot.projectedProfit < 0 ? "text-error" : "text-success", children: formatCurrency(lot.projectedProfit) }), _jsx(Td, { children: _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setPickerLot(lot), children: "Create pre-order link" }) })] }, lot.id))) })] })), meta && meta.totalPages > 1 && (_jsx(Pagination, { currentPage: page, totalPages: meta.totalPages, onPageChange: setPage, pageSize: pageSize, onPageSizeChange: (size) => {
|
|
29
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { layout: "flex", align: "center", justify: "between", children: [_jsx(Heading, { level: 2, children: "Projections" }), _jsx(Select, { "aria-label": "Sort by", value: sorts, onValueChange: setSorts, options: SORT_OPTIONS })] }), isLoading ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Skeleton, { variant: "rectangular", height: "32px" }), _jsx(Skeleton, { variant: "rectangular", height: "32px" }), _jsx(Skeleton, { variant: "rectangular", height: "32px" })] })) : lots.length === 0 ? (_jsx(Text, { variant: "secondary", children: "No lots to project yet." })) : (_jsxs(Table, { children: [_jsx(Thead, { children: _jsxs(Tr, { children: [_jsx(Th, { children: "Lot" }), _jsx(Th, { children: "Items" }), _jsx(Th, { children: "Landed Cost" }), _jsx(Th, { children: "Projected Revenue" }), _jsx(Th, { children: "Projected Profit" }), _jsx(Th, {})] }) }), _jsx(Tbody, { children: lots.map((lot) => (_jsxs(Tr, { children: [_jsx(Td, { children: _jsx(TextLink, { href: String(ROUTES.ADMIN.SHIPMENT_LOT_ITEMS(lot.shipmentId, lot.id)), children: lot.lotName }) }), _jsx(Td, { children: lot.itemCount }), _jsx(Td, { children: formatCurrency(lot.totalLandedCost) }), _jsx(Td, { children: formatCurrency(lot.projectedRevenue) }), _jsx(Td, { className: lot.projectedProfit < 0 ? "text-error" : "text-success", children: formatCurrency(lot.projectedProfit) }), _jsx(Td, { children: _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setPickerLot(lot), children: "Create pre-order link" }) })] }, lot.id))) })] })), meta && meta.totalPages > 1 && (_jsx(Pagination, { currentPage: page, totalPages: meta.totalPages, onPageChange: setPage, pageSize: pageSize, onPageSizeChange: (size) => {
|
|
29
30
|
setPageSize(size);
|
|
30
31
|
setPage(1);
|
|
31
32
|
} })), pickerLot && (_jsx(LotItemPickerModal, { lot: pickerLot, onClose: () => setPickerLot(null), onLinked: refetch }))] }));
|
|
@@ -37,6 +37,7 @@ import { Alert, Button, Card, Form, Heading, Stack, Text } from "../../../ui/ind
|
|
|
37
37
|
import { FieldInput } from "../../../ui/forms/FieldInput.js";
|
|
38
38
|
import { FieldSelect } from "../../../ui/forms/FieldSelect.js";
|
|
39
39
|
import { TIER_HELP } from "../constants/help-content.js";
|
|
40
|
+
import { toUserMessage } from "../../../errors/error-display-map.js";
|
|
40
41
|
/** Every tier, unlike the studio — see the header. */
|
|
41
42
|
const TIERS = ["draft", "standard", "precise", "max"];
|
|
42
43
|
export function StickerCommitPanel({ file, dimensions, onCommit, onCommitted, }) {
|
|
@@ -80,7 +81,7 @@ export function StickerCommitPanel({ file, dimensions, onCommit, onCommitted, })
|
|
|
80
81
|
* buyer, which this is not — and the routes already map their own
|
|
81
82
|
* failures to user-safe text.
|
|
82
83
|
*/
|
|
83
|
-
setError(n.
|
|
84
|
+
setError(toUserMessage(n.code, undefined, { fallback: "We couldn't commit that sheet. Please try again." }));
|
|
84
85
|
}
|
|
85
86
|
finally {
|
|
86
87
|
setPending(false);
|
|
@@ -32,6 +32,7 @@ import { useCallback, useEffect, useState } from "react";
|
|
|
32
32
|
import { normalizeError } from "../../../errors/normalize.js";
|
|
33
33
|
import { Alert, Badge, Button, Card, Heading, Row, Span, Stack, Text, } from "../../../ui/index.js";
|
|
34
34
|
import { FieldSelect } from "../../../ui/forms/FieldSelect.js";
|
|
35
|
+
import { toUserMessage } from "../../../errors/error-display-map.js";
|
|
35
36
|
const PROFILES = [
|
|
36
37
|
{ value: "print-shop", label: "Print shop (Roland / Graphtec)" },
|
|
37
38
|
{ value: "cricut-cut", label: "Cricut Joy 2 — cut only" },
|
|
@@ -83,7 +84,7 @@ export function StickerPrintPanel({ loadReport, onDownloadCutFile, onDownloadMos
|
|
|
83
84
|
* Rendering both the same way would have an operator confirming a DPI to
|
|
84
85
|
* fix a lapsed session.
|
|
85
86
|
*/
|
|
86
|
-
setError(n.
|
|
87
|
+
setError(toUserMessage(n.code, undefined, { fallback: "We couldn't prepare that print. Please try again." }));
|
|
87
88
|
setReport(null);
|
|
88
89
|
}
|
|
89
90
|
finally {
|
|
@@ -23,11 +23,18 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
23
23
|
* belongs to which stage.
|
|
24
24
|
*
|
|
25
25
|
* 🛑 Built on `<SectionForm>`, not a hand-rolled `<Form>`. Three controls is
|
|
26
|
-
* where a form stops being trivial, and sectionising is what buys the
|
|
27
|
-
*
|
|
26
|
+
* where a form stops being trivial, and sectionising is what buys the
|
|
27
|
+
* jump-to-field link and the pinned mobile action bar —
|
|
28
28
|
* audit-form-sectionised is strict-zero on exactly this, and its grandfather
|
|
29
29
|
* list is empty and closed.
|
|
30
30
|
*
|
|
31
|
+
* 🛑 The error summary is NOT free, and this comment used to claim it was.
|
|
32
|
+
* `SectionForm` deliberately does not own a `FormShellContext` — it consumes
|
|
33
|
+
* whichever one the owning view mounts, and tolerates none. So a view that
|
|
34
|
+
* only renders `<SectionForm schema={…}>` gets section badges and no readable
|
|
35
|
+
* message at all. The view must mount the context and render
|
|
36
|
+
* `<FormErrorSummary>` itself, which is what the bottom of this file now does.
|
|
37
|
+
*
|
|
31
38
|
* EXPORTS: StickerRetryPanel
|
|
32
39
|
*
|
|
33
40
|
* @tag domain:sticker-trace
|
|
@@ -43,11 +50,14 @@ import { normalizeError } from "../../../errors/normalize.js";
|
|
|
43
50
|
import { Alert, Card, Stack, Text } from "../../../ui/index.js";
|
|
44
51
|
import { FieldInput } from "../../../ui/forms/FieldInput.js";
|
|
45
52
|
import { FieldSelect } from "../../../ui/forms/FieldSelect.js";
|
|
53
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary.js";
|
|
54
|
+
import { FormShellContext, useFormShellState } from "../../../ui/forms/FormShell.js";
|
|
46
55
|
import { SectionForm } from "../../shell/SectionForm.js";
|
|
47
56
|
import { TRACE_STAGES } from "../../../_internal/shared/features/sticker-trace/types.js";
|
|
48
57
|
import { resolveResumeStage, explainResume, } from "../../../_internal/shared/features/sticker-trace/resume.js";
|
|
49
58
|
import { paramsForTier } from "../../../_internal/shared/features/sticker-trace/config.js";
|
|
50
59
|
import { TIER_HELP } from "../constants/help-content.js";
|
|
60
|
+
import { toUserMessage } from "../../../errors/error-display-map.js";
|
|
51
61
|
const TIERS = ["draft", "standard", "precise", "max"];
|
|
52
62
|
/**
|
|
53
63
|
* 🛑 `.min(1)` on the name, mirroring `requireName` on the server.
|
|
@@ -103,7 +113,7 @@ export function StickerRetryPanel({ source, artifactsPruned = false, onSubmit, o
|
|
|
103
113
|
* operator looking at a form that did nothing — and the likeliest causes,
|
|
104
114
|
* a lapsed session or a rejected name, are both named by the message.
|
|
105
115
|
*/
|
|
106
|
-
setError(n.
|
|
116
|
+
setError(toUserMessage(n.code, undefined, { fallback: "We couldn't retry that run. Please try again." }));
|
|
107
117
|
}
|
|
108
118
|
finally {
|
|
109
119
|
setPending(false);
|
|
@@ -124,5 +134,15 @@ export function StickerRetryPanel({ source, artifactsPruned = false, onSubmit, o
|
|
|
124
134
|
render: ({ values: v, onChange: set }) => (_jsxs(Stack, { gap: "comfortable", children: [_jsx(FieldSelect, { name: "requested", label: "Resume from", value: v.requested, options: TRACE_STAGES.map((s) => ({ value: s, label: s })), onChange: (next) => set({ requested: next }) }), notice ? _jsx(Alert, { variant: "warning", children: notice }) : null, artifactsPruned ? (_jsx(Alert, { variant: "warning", children: "This run's artifacts have been cleared, so the retry starts from the beginning whatever you pick here." })) : null, !source.params ? (_jsx(Text, { size: "xs", color: "muted", children: "This run predates parameter recording, so the resume point cannot be checked ahead of time \u2014 the server will decide and tell you." })) : null] })),
|
|
125
135
|
},
|
|
126
136
|
], [source.name, source.params, notice, artifactsPruned]);
|
|
127
|
-
|
|
137
|
+
/*
|
|
138
|
+
* 🛑 `SectionForm` does NOT provide a FormShellContext — it reads whichever
|
|
139
|
+
* one the owning view mounts, and may legitimately get none. The header of
|
|
140
|
+
* this file says sectionising buys "the error summary … for free", and that
|
|
141
|
+
* was wrong: nothing here mounted a context or rendered a summary, so a
|
|
142
|
+
* schema rejection set the section badge and produced no readable message
|
|
143
|
+
* anywhere. The file passed `audit-form-error-summary` only because a bare
|
|
144
|
+
* `<SectionForm schema=>` was not a recognised call site.
|
|
145
|
+
*/
|
|
146
|
+
const { shellCtx } = useFormShellState(retrySchema);
|
|
147
|
+
return (_jsx(Card, { variant: "default", children: _jsx(FormShellContext.Provider, { value: shellCtx, children: _jsxs(Stack, { gap: "comfortable", children: [error ? _jsx(Alert, { variant: "error", children: error }) : null, _jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: values, onChange: onChange, onSubmit: submit, schema: retrySchema, submitLabel: "Start retry", isLoading: pending, onCancel: onCancel, cancelLabel: "Cancel" })] }) }) }));
|
|
128
148
|
}
|
|
@@ -30,6 +30,7 @@ import { PARAM_HELP, TIER_HELP } from "../constants/help-content.js";
|
|
|
30
30
|
import { useLiveTrace } from "../hooks/useLiveTrace.js";
|
|
31
31
|
import { canvasCodec } from "../../../_internal/client/features/sticker-trace/canvas-codec.js";
|
|
32
32
|
import { normalizeError } from "../../../errors/normalize.js";
|
|
33
|
+
import { toUserMessage } from "../../../errors/error-display-map.js";
|
|
33
34
|
/*
|
|
34
35
|
* Only `draft` is offered. The hook runs on the main thread and draft's
|
|
35
36
|
* 1200px cap is what keeps one trace near a second; `standard`'s 3000 would
|
|
@@ -103,7 +104,7 @@ export function StickerTraceStudio({ renderCommit }) {
|
|
|
103
104
|
// the operator is the one who needs to know the commit is blocked.
|
|
104
105
|
if (live) {
|
|
105
106
|
setNatural(null);
|
|
106
|
-
setProbeError(n.
|
|
107
|
+
setProbeError(toUserMessage(n.code, undefined, { fallback: "That probe could not be run." }));
|
|
107
108
|
}
|
|
108
109
|
});
|
|
109
110
|
return () => {
|
|
@@ -6,3 +6,5 @@ export { rasterisePartToBox } from "../../_internal/shared/features/sticker-trac
|
|
|
6
6
|
export { buildCutFileBytes, buildCutFileForRun } from "../../_internal/server/features/sticker-trace/build-cut-file";
|
|
7
7
|
export { collectMosaicInputs, type MosaicInputs, type MosaicPartInput, } from "../../_internal/server/features/sticker-trace/mosaic-inputs";
|
|
8
8
|
export type { PrintPageSizeId } from "../../_internal/shared/features/sticker-trace/export/page-sizes";
|
|
9
|
+
export type { DecodeOptions, ImageCodec, PreviewOptions, ProbeResult, } from "../../_internal/shared/features/sticker-trace/codec";
|
|
10
|
+
export type { DecodedRaster, EncodedImage, Rect as StickerRect, } from "../../_internal/shared/features/sticker-trace/types";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
|
-
import { Button, Card, Details, Div, Row, Stack, Summary, Text } from "../../../ui/index.js";
|
|
4
|
+
import { Button, Card, Details, Div, Row, Stack, Summary, Text, TextLink } from "../../../ui/index.js";
|
|
5
|
+
import { ROUTES } from "../../../next/routing/route-map.js";
|
|
5
6
|
import { MediaImage } from "../../media/MediaImage.js";
|
|
6
7
|
import { apiClient } from "../../../http/index.js";
|
|
7
8
|
import { useApiMutation } from "../../../client/api/useApiMutation.js";
|
|
@@ -50,6 +51,6 @@ export function AdminTesterFeedbackIssuesView() {
|
|
|
50
51
|
const sortedPhases = Array.from(byPhase.keys()).sort((a, b) => a - b);
|
|
51
52
|
return (_jsx(Stack, { gap: "md", children: sortedPhases.map((phase) => {
|
|
52
53
|
const phaseIssues = byPhase.get(phase);
|
|
53
|
-
return (_jsxs(Details, { tone: "card", defaultOpen: sortedPhases.length === 1, children: [_jsxs(Summary, { size: "lg", weight: "bold", children: ["Phase ", phase, " (", phaseIssues.length, " issue", phaseIssues.length === 1 ? "" : "s", ")"] }), _jsx(Div, { padding: "t-sm", children: _jsx(Stack, { gap: "sm", children: phaseIssues.map((issue) => (_jsx(Card, { padding: "md", variant: "default", children: _jsxs(Stack, { gap: "xs", children: [_jsx(
|
|
54
|
+
return (_jsxs(Details, { tone: "card", defaultOpen: sortedPhases.length === 1, children: [_jsxs(Summary, { size: "lg", weight: "bold", children: ["Phase ", phase, " (", phaseIssues.length, " issue", phaseIssues.length === 1 ? "" : "s", ")"] }), _jsx(Div, { padding: "t-sm", children: _jsx(Stack, { gap: "sm", children: phaseIssues.map((issue) => (_jsx(Card, { padding: "md", variant: "default", children: _jsxs(Stack, { gap: "xs", children: [_jsx(TextLink, { href: String(ROUTES.ADMIN.TESTER_CHECKLIST_EDIT(issue.checklistItemId)), weight: "semibold", children: issue.label }), _jsxs(Text, { size: "sm", color: "muted", children: [issue.groupLabel, " \u203A ", issue.pageLabel] }), _jsxs(Text, { size: "sm", color: "muted", children: ["Reported by ", issue.testerDisplayName] }), issue.comment && _jsx(Text, { size: "sm", children: issue.comment }), issue.screenshotUrl && (_jsx(MediaImage, { src: issue.screenshotUrl, alt: "Tester screenshot", size: "card" })), issue.bugConfirmed ? (_jsxs(Row, { gap: "sm", align: "center", wrap: true, children: [_jsxs(Text, { size: "sm", weight: "semibold", color: "success", children: ["\uD83D\uDC1B Confirmed \u2014 credited to ", issue.bugHunterName ?? "unknown tester"] }), !issue.supersededByItemId && (_jsx(Button, { size: "sm", action: ACTIONS.ADMIN["reopen-checklist-item"], isLoading: reopenMutation.isPending, onClick: () => reopenMutation.mutate(issue.checklistItemId) }))] })) : (_jsx(Div, { children: _jsx(Button, { size: "sm", action: ACTIONS.ADMIN["confirm-bug"], isLoading: confirmBugMutation.isPending, onClick: () => confirmBugMutation.mutate(issue.id) }) }))] }) }, issue.id))) }) })] }, phase));
|
|
54
55
|
}) }));
|
|
55
56
|
}
|
package/dist/http/api-handler.js
CHANGED
|
@@ -2,7 +2,10 @@ import { normalizeError } from "../errors/normalize.js";
|
|
|
2
2
|
import { isAccountDisabled } from "../features/auth/role-predicates.js";
|
|
3
3
|
import { createApiHandlerFactory } from "../next/api/apiHandler.js";
|
|
4
4
|
import { applyRateLimit } from "../security/index.js";
|
|
5
|
-
import { AuthenticationError, AuthorizationError, ERROR_MESSAGES,
|
|
5
|
+
import { AuthenticationError, AuthorizationError, ERROR_MESSAGES, } from "../errors/index.js";
|
|
6
|
+
// Next-coupled, so it comes from the defining module — see errors/index.ts.
|
|
7
|
+
// This file is an API-route handler, so `next/server` here is correct.
|
|
8
|
+
import { handleApiError } from "../errors/error-handler.js";
|
|
6
9
|
import { errorResponse } from "../next/api/api-response.js";
|
|
7
10
|
import { serverLogger } from "../monitoring/index.js";
|
|
8
11
|
import { getProviders } from "../contracts/index.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -52,6 +52,10 @@ export type { FieldSelectProps } from "./ui/index";
|
|
|
52
52
|
export { FieldSelect } from "./ui/index";
|
|
53
53
|
export type { FieldCheckboxProps } from "./ui/index";
|
|
54
54
|
export { FieldCheckbox } from "./ui/index";
|
|
55
|
+
export type { FieldTextareaProps } from "./ui/index";
|
|
56
|
+
export { FieldTextarea } from "./ui/index";
|
|
57
|
+
export type { FormErrorSummaryProps } from "./ui/index";
|
|
58
|
+
export { FormErrorSummary } from "./ui/index";
|
|
55
59
|
export { GRID_MAP } from "./ui/index";
|
|
56
60
|
export { ImageGallery } from "./ui/index";
|
|
57
61
|
export { ImageLightbox } from "./ui/index";
|
|
@@ -321,9 +325,9 @@ export { ERROR_CODES, BID_ERROR_CODES, OFFER_ERROR_CODES } from "./errors/index"
|
|
|
321
325
|
export { ERROR_MESSAGES } from "./errors/index";
|
|
322
326
|
export { NotFoundError } from "./errors/index";
|
|
323
327
|
export { ValidationError } from "./errors/index";
|
|
324
|
-
export { handleApiError } from "./errors/
|
|
325
|
-
export { isAppError } from "./errors/
|
|
326
|
-
export { logError } from "./errors/
|
|
328
|
+
export { handleApiError } from "./errors/error-handler";
|
|
329
|
+
export { isAppError } from "./errors/error-handler";
|
|
330
|
+
export { logError } from "./errors/error-handler";
|
|
327
331
|
export type { ErrorCode } from "./errors/index";
|
|
328
332
|
export { PaymentProviderUnreachableError } from "./errors/index";
|
|
329
333
|
export { mapToHttpError, HTTP_ERROR_CODES } from "./errors/index";
|
|
@@ -2940,7 +2944,7 @@ export type { OwnerQuickActionsProps, OwnerQuickActionsResource } from "./featur
|
|
|
2940
2944
|
export type { ListingTypePlugin } from "./_internal/shared/listing-types/_registry";
|
|
2941
2945
|
export { getListingRule, getCategoryRule, pickOrderType, getSplitKey, runSyncPreflight, CHECKOUT_RULES, CATEGORY_CHECKOUT_RULES, CHECKOUT_MAX_ORDERS_PER_TX, PRIZE_DRAW_MAX_REVEALS_PER_ORDER, BUNDLE_MAX_QTY_PER_TX, STANDARD_MAX_QTY_PER_LINE, } from "./_internal/shared/checkout/rules";
|
|
2942
2946
|
export type { ListingCheckoutRule, CategoryCheckoutRule, CartItemProductPair, OrderItemInput, RefundPolicy, } from "./_internal/shared/checkout/rules";
|
|
2943
|
-
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, IMAGE_ACCEPT_ATTR, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits";
|
|
2947
|
+
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, IMAGE_ACCEPT_ATTR, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, REVIEW_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits";
|
|
2944
2948
|
export type { MediaKind, AllowedImageMime, AllowedVideoMime, AllowedDocMime, AllowedMime, } from "./_internal/shared/media/limits";
|
|
2945
2949
|
export { PRODUCT_FIELDS, PRODUCT_STATUS_TRANSITIONS, ORDER_FIELDS, REVIEW_FIELDS, BID_FIELDS, AD_FIELDS, AD_TRACKED_FIELDS, BLOG_FIELDS, BRAND_FIELDS, WISHLIST_FIELDS, HISTORY_FIELDS, COUPON_USAGE_FIELDS, CAROUSEL_FIELDS, FAQ_FIELDS, HOMEPAGE_SECTION_FIELDS, SITE_SETTINGS_FIELDS, COMMON_FIELDS, OAUTH_STATE_VALUES, SCHEMA_DEFAULTS, } from "./constants/field-names";
|
|
2946
2950
|
export { SORT_DIR, sortBy } from "./constants/sort";
|
package/dist/index.js
CHANGED
|
@@ -138,6 +138,8 @@ export { FormGroup } from "./ui/index.js";
|
|
|
138
138
|
export { FieldInput } from "./ui/index.js";
|
|
139
139
|
export { FieldSelect } from "./ui/index.js";
|
|
140
140
|
export { FieldCheckbox } from "./ui/index.js";
|
|
141
|
+
export { FieldTextarea } from "./ui/index.js";
|
|
142
|
+
export { FormErrorSummary } from "./ui/index.js";
|
|
141
143
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
142
144
|
// GRID_MAP - Constant used across modules.
|
|
143
145
|
export { GRID_MAP } from "./ui/index.js";
|
|
@@ -552,13 +554,13 @@ export { NotFoundError } from "./errors/index.js";
|
|
|
552
554
|
export { ValidationError } from "./errors/index.js";
|
|
553
555
|
// [UTIL]-Pure utility â€" no framework or runtime dependency; safe to import from any environment.
|
|
554
556
|
// handleApiError - Model for handle api error.
|
|
555
|
-
export { handleApiError } from "./errors/
|
|
557
|
+
export { handleApiError } from "./errors/error-handler.js";
|
|
556
558
|
// [UTIL]-Pure utility â€" no framework or runtime dependency; safe to import from any environment.
|
|
557
559
|
// isAppError - Model for is app error.
|
|
558
|
-
export { isAppError } from "./errors/
|
|
560
|
+
export { isAppError } from "./errors/error-handler.js";
|
|
559
561
|
// [UTIL]-Pure utility â€" no framework or runtime dependency; safe to import from any environment.
|
|
560
562
|
// logError - Helper for log error.
|
|
561
|
-
export { logError } from "./errors/
|
|
563
|
+
export { logError } from "./errors/error-handler.js";
|
|
562
564
|
// [UTIL]-Pure utility — no framework or runtime dependency; safe to import from any environment.
|
|
563
565
|
// PaymentProviderUnreachableError - Thrown when the payment gateway API exhausts retries.
|
|
564
566
|
export { PaymentProviderUnreachableError } from "./errors/index.js";
|
|
@@ -5249,7 +5251,7 @@ export { RecordOwnerQuickActions } from "./features/admin/components/RecordOwner
|
|
|
5249
5251
|
// Checkout rule registry â€" pure TS, safe in both client and server bundles.
|
|
5250
5252
|
export { getListingRule, getCategoryRule, pickOrderType, getSplitKey, runSyncPreflight, CHECKOUT_RULES, CATEGORY_CHECKOUT_RULES, CHECKOUT_MAX_ORDERS_PER_TX, PRIZE_DRAW_MAX_REVEALS_PER_ORDER, BUNDLE_MAX_QTY_PER_TX, STANDARD_MAX_QTY_PER_LINE, } from "./_internal/shared/checkout/rules/index.js";
|
|
5251
5253
|
// Media upload limits â€" pure constants, safe in both client and server bundles.
|
|
5252
|
-
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, IMAGE_ACCEPT_ATTR, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits.js";
|
|
5254
|
+
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, IMAGE_ACCEPT_ATTR, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, REVIEW_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits.js";
|
|
5253
5255
|
// ── Field name constants — canonical source of truth for all Firestore field names ──
|
|
5254
5256
|
// NOTE: STORE_FIELDS, CATEGORY_FIELDS, EVENT_FIELDS, EVENT_ENTRY_FIELDS, PAYOUT_FIELDS,
|
|
5255
5257
|
// COUPON_FIELDS, CART_FIELDS, USER_FIELDS, ADDRESS_FIELDS, NOTIFICATION_FIELDS,
|
|
@@ -182,6 +182,8 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
182
182
|
readonly CLAIMED_COUPONS: "/user/coupons";
|
|
183
183
|
/** Points balance + ledger. 1 point = ₹1, spendable at checkout. */
|
|
184
184
|
readonly POINTS: "/user/points";
|
|
185
|
+
readonly SESSIONS: "/user/sessions";
|
|
186
|
+
readonly PAYMENT_METHODS: "/user/payment-methods";
|
|
185
187
|
readonly BECOME_SELLER: "/user/become-seller";
|
|
186
188
|
readonly CART: "/cart";
|
|
187
189
|
readonly CHECKOUT: "/checkout";
|
|
@@ -299,7 +301,6 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
299
301
|
readonly ADMIN: {
|
|
300
302
|
readonly DASHBOARD: "/admin/dashboard";
|
|
301
303
|
readonly SHIPMENTS: "/admin/shipments";
|
|
302
|
-
readonly INVENTORY: "/admin/inventory";
|
|
303
304
|
readonly INVENTORY_BOXES: "/admin/inventory/boxes";
|
|
304
305
|
readonly INVENTORY_BOX_DETAIL: (id: string) => string;
|
|
305
306
|
readonly INVENTORY_BOX_LABELS: "/admin/inventory/boxes/labels";
|
|
@@ -635,6 +636,8 @@ export declare const ROUTES: {
|
|
|
635
636
|
readonly CLAIMED_COUPONS: "/user/coupons";
|
|
636
637
|
/** Points balance + ledger. 1 point = ₹1, spendable at checkout. */
|
|
637
638
|
readonly POINTS: "/user/points";
|
|
639
|
+
readonly SESSIONS: "/user/sessions";
|
|
640
|
+
readonly PAYMENT_METHODS: "/user/payment-methods";
|
|
638
641
|
readonly BECOME_SELLER: "/user/become-seller";
|
|
639
642
|
readonly CART: "/cart";
|
|
640
643
|
readonly CHECKOUT: "/checkout";
|
|
@@ -752,7 +755,6 @@ export declare const ROUTES: {
|
|
|
752
755
|
readonly ADMIN: {
|
|
753
756
|
readonly DASHBOARD: "/admin/dashboard";
|
|
754
757
|
readonly SHIPMENTS: "/admin/shipments";
|
|
755
|
-
readonly INVENTORY: "/admin/inventory";
|
|
756
758
|
readonly INVENTORY_BOXES: "/admin/inventory/boxes";
|
|
757
759
|
readonly INVENTORY_BOX_DETAIL: (id: string) => string;
|
|
758
760
|
readonly INVENTORY_BOX_LABELS: "/admin/inventory/boxes/labels";
|
|
@@ -302,6 +302,15 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
302
302
|
CLAIMED_COUPONS: "/user/coupons",
|
|
303
303
|
/** Points balance + ledger. 1 point = ₹1, spendable at checkout. */
|
|
304
304
|
POINTS: "/user/points",
|
|
305
|
+
/*
|
|
306
|
+
* 🛑 Both of these had a COMPLETE API and no page. `GET /api/user/sessions`
|
|
307
|
+
* + `DELETE [id]`, and `GET/POST /api/user/payment-methods` + `DELETE
|
|
308
|
+
* [id]`, were all live with zero consumers — and `BuyerAccountGuideView`
|
|
309
|
+
* already told buyers to "view all devices signed in at My Account →
|
|
310
|
+
* Sessions", pointing at a security control that did not exist.
|
|
311
|
+
*/
|
|
312
|
+
SESSIONS: "/user/sessions",
|
|
313
|
+
PAYMENT_METHODS: "/user/payment-methods",
|
|
305
314
|
BECOME_SELLER: "/user/become-seller",
|
|
306
315
|
CART: "/cart",
|
|
307
316
|
CHECKOUT: "/checkout",
|
|
@@ -433,7 +442,16 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
433
442
|
ADMIN: {
|
|
434
443
|
DASHBOARD: "/admin/dashboard",
|
|
435
444
|
SHIPMENTS: "/admin/shipments",
|
|
436
|
-
|
|
445
|
+
/*
|
|
446
|
+
* 🛑 There is deliberately NO `INVENTORY: "/admin/inventory"` key.
|
|
447
|
+
*
|
|
448
|
+
* I added one and never built the page behind it, and nothing ever
|
|
449
|
+
* referenced it — `audit-dead-route-key` caught it. A ROUTES key is a
|
|
450
|
+
* promise, and the two real surfaces (`INVENTORY_BOXES`,
|
|
451
|
+
* `INVENTORY_MOVEMENTS`) each carry their own nav entry, so an index page
|
|
452
|
+
* would be a third place to list the same two links. Build it only when
|
|
453
|
+
* there is a third child worth grouping.
|
|
454
|
+
*/
|
|
437
455
|
INVENTORY_BOXES: "/admin/inventory/boxes",
|
|
438
456
|
INVENTORY_BOX_DETAIL: (id) => `/admin/inventory/boxes/${id}`,
|
|
439
457
|
/*
|