@mohasinac/appkit 4.27.0 → 4.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +16 -16
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +29 -6
- package/dist/_internal/client/features/lottery/LotteryListView.js +13 -1
- package/dist/_internal/server/features/checkout/actions.js +6 -4
- package/dist/_internal/server/features/classified/metadata.js +3 -1
- package/dist/_internal/server/features/digital-code/metadata.js +3 -1
- package/dist/_internal/server/features/live/metadata.js +3 -1
- package/dist/_internal/server/features/refunds/actions.js +3 -2
- package/dist/_internal/server/features/reviews/actions.js +3 -2
- package/dist/_internal/server/features/seo/sitemap.js +189 -40
- package/dist/_internal/server/jobs/core/listingProcessor.d.ts +0 -1
- package/dist/_internal/server/jobs/core/listingProcessor.js +19 -1
- package/dist/_internal/shared/actions/action-registry.js +14 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +25 -3
- package/dist/_internal/shared/features/checkout/config.d.ts +3 -3
- package/dist/_internal/shared/features/checkout/config.js +6 -3
- package/dist/client.d.ts +32 -2
- package/dist/client.js +37 -2
- package/dist/constants/api-endpoints.d.ts +21 -3
- package/dist/constants/api-endpoints.js +14 -1
- package/dist/constants/field-names.d.ts +2 -2
- package/dist/constants/field-names.js +2 -2
- package/dist/core/newsletter.repository.js +14 -1
- package/dist/features/about/components/FAQPageView.js +1 -1
- package/dist/features/about/components/FeesView.js +1 -1
- package/dist/features/account/components/AddressForm.d.ts +2 -0
- package/dist/features/account/components/AddressForm.js +5 -1
- package/dist/features/account/components/UserBidsView.js +1 -1
- package/dist/features/account/components/UserSidebar.js +1 -1
- package/dist/features/account/components/UserSupportView.js +24 -2
- package/dist/features/account/hooks/useAddresses.d.ts +29 -1
- package/dist/features/account/hooks/useAddresses.js +4 -1
- package/dist/features/account/hooks/useProfile.d.ts +1 -1
- package/dist/features/account/hooks/useProfile.js +1 -1
- package/dist/features/addresses/repository/addresses.repository.d.ts +13 -0
- package/dist/features/addresses/repository/addresses.repository.js +19 -0
- package/dist/features/admin/actions/chat-actions.js +5 -1
- package/dist/features/admin/components/AdminAdsView.js +1 -1
- package/dist/features/admin/components/AdminBidsView.js +6 -1
- package/dist/features/admin/components/AdminCarouselGroupEditorView.d.ts +9 -1
- package/dist/features/admin/components/AdminCarouselGroupEditorView.js +42 -10
- package/dist/features/admin/components/AdminCarouselView.js +1 -1
- package/dist/features/admin/components/AdminFeatureEditorView.js +1 -1
- package/dist/features/admin/components/AdminGroupedListingsView.js +14 -2
- package/dist/features/admin/components/AdminSidebar.js +1 -1
- package/dist/features/admin/components/AdminViewCards.js +2 -2
- package/dist/features/admin/components/DataTable.js +2 -2
- package/dist/features/admin/schemas/firestore.js +9 -3
- package/dist/features/auctions/actions/bid-actions.js +45 -4
- package/dist/features/auctions/components/AuctionBottomActions.d.ts +21 -6
- package/dist/features/auctions/components/AuctionBottomActions.js +42 -11
- package/dist/features/auctions/components/AuctionDetailPageView.d.ts +8 -3
- package/dist/features/auctions/components/AuctionDetailPageView.js +2 -2
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +4 -5
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +27 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +42 -20
- package/dist/features/auth/components/SocialAuthButtons.js +1 -1
- package/dist/features/auth/hooks/useAuth.d.ts +23 -1
- package/dist/features/auth/hooks/useAuth.js +27 -1
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/permissions/constants.js +3 -0
- package/dist/features/auth/repository/session.repository.d.ts +16 -1
- package/dist/features/auth/repository/session.repository.js +23 -0
- package/dist/features/auth/repository/user.repository.js +13 -2
- package/dist/features/blog/components/BlogListView.js +2 -2
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +11 -1
- package/dist/features/cart/components/CartPriceBreakdown.js +3 -3
- package/dist/features/cart/components/StoreAddonsPicker.d.ts +9 -0
- package/dist/features/cart/components/StoreAddonsPicker.js +15 -5
- package/dist/features/cart/components/index.d.ts +1 -1
- package/dist/features/cart/components/index.js +1 -1
- package/dist/features/cart/hooks/useGuestCartMerge.d.ts +8 -1
- package/dist/features/cart/hooks/useGuestCartMerge.js +2 -1
- package/dist/features/cart/utils/line-total.d.ts +25 -0
- package/dist/features/cart/utils/line-total.js +26 -0
- package/dist/features/catalogue/repository/catalogue.repository.d.ts +12 -0
- package/dist/features/catalogue/repository/catalogue.repository.js +15 -0
- package/dist/features/categories/components/CategoryTree.js +2 -2
- package/dist/features/contact/components/ContactForm.js +1 -1
- package/dist/features/copilot/hooks/useCopilotFeedback.d.ts +1 -1
- package/dist/features/copilot/hooks/useCopilotFeedback.js +1 -1
- package/dist/features/events/components/AdminEventEditorView.js +16 -9
- package/dist/features/events/components/AdminEventEntriesView.js +7 -13
- package/dist/features/events/components/EventRafflesSection.js +14 -2
- package/dist/features/events/components/EventStatusBadge.js +1 -1
- package/dist/features/events/components/event-entry-detail-fields.d.ts +24 -0
- package/dist/features/events/components/event-entry-detail-fields.js +13 -0
- package/dist/features/faq/components/ContactCTA.js +1 -1
- package/dist/features/faq/components/FAQHelpfulButtons.js +2 -2
- package/dist/features/faq/repository/faqs.repository.d.ts +16 -1
- package/dist/features/faq/repository/faqs.repository.js +64 -54
- package/dist/features/faq/schemas/firestore.d.ts +3 -2
- package/dist/features/faq/schemas/firestore.js +2 -2
- package/dist/features/faq/schemas/index.d.ts +3 -3
- package/dist/features/faq/schemas/index.js +1 -1
- package/dist/features/filters/FilterFacetSection.js +2 -2
- package/dist/features/forms/Toggle.js +1 -1
- package/dist/features/grouped/components/GroupedListingEditorView.d.ts +18 -0
- package/dist/features/grouped/components/GroupedListingEditorView.js +192 -0
- package/dist/features/homepage/components/AdvertisementBanner.js +2 -2
- package/dist/features/homepage/components/BrandsSection.js +1 -1
- package/dist/features/homepage/components/CTABannerSection.js +1 -1
- package/dist/features/homepage/components/HomepageCustomerReviewsSection.js +1 -1
- package/dist/features/homepage/components/HomepageSectionBoundary.d.ts +39 -0
- package/dist/features/homepage/components/HomepageSectionBoundary.js +49 -0
- package/dist/features/homepage/components/ShopByCategorySection.js +1 -1
- package/dist/features/homepage/components/WelcomeSection.js +1 -1
- package/dist/features/homepage/lib/section-renderer.js +2 -1
- package/dist/features/layout/AppLayoutShell.js +5 -5
- package/dist/features/layout/BottomActions.js +5 -5
- package/dist/features/layout/BottomSheet.js +1 -1
- package/dist/features/layout/MainNavbar.js +1 -1
- package/dist/features/layout/NavbarLayout.js +1 -1
- package/dist/features/layout/TitleBarLayout.js +3 -3
- package/dist/features/layout/hooks/useFormBottomActions.d.ts +11 -1
- package/dist/features/layout/hooks/useFormBottomActions.js +16 -2
- package/dist/features/lottery/schemas/config-write.d.ts +141 -0
- package/dist/features/lottery/schemas/config-write.js +139 -0
- package/dist/features/media/MediaPickerModal.js +3 -1
- package/dist/features/media/MediaVideo.js +4 -2
- package/dist/features/media/components/MediaSlider.js +1 -1
- package/dist/features/orders/components/OrdersList.js +2 -2
- package/dist/features/payments/repository/payout.repository.d.ts +11 -1
- package/dist/features/payments/repository/payout.repository.js +14 -6
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -2
- package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
- package/dist/features/products/columns/productTableColumns.js +3 -3
- package/dist/features/products/components/CompareOverlay.js +1 -1
- package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
- package/dist/features/products/components/PrizeDrawEntryActions.js +2 -2
- package/dist/features/products/components/PrizeDrawsSection.js +5 -1
- package/dist/features/products/components/ProductDetailActions.js +11 -7
- package/dist/features/products/components/ProductDetailPageView.js +3 -3
- package/dist/features/products/components/ProductFeatureBadges.js +2 -2
- package/dist/features/products/components/ProductFeaturesSelector.js +1 -1
- package/dist/features/products/components/ProductFilters.js +15 -8
- package/dist/features/products/components/ProductGrid.js +8 -17
- package/dist/features/products/components/ProductTabs.js +7 -4
- package/dist/features/products/components/ProductTabsShell.js +2 -7
- package/dist/features/products/constants/action-defs.d.ts +21 -4
- package/dist/features/products/constants/action-defs.js +7 -3
- package/dist/features/products/hooks/useRelatedProducts.js +2 -1
- package/dist/features/products/repository/products.repository.d.ts +1 -1
- package/dist/features/products/repository/products.repository.js +27 -13
- package/dist/features/products/schemas/firestore.d.ts +3 -2
- package/dist/features/products/schemas/firestore.js +1 -1
- package/dist/features/reviews/actions/review-actions.d.ts +8 -1
- package/dist/features/reviews/actions/review-actions.js +14 -3
- package/dist/features/reviews/components/ReviewDetailShell.js +10 -4
- package/dist/features/reviews/components/ReviewModal.js +1 -1
- package/dist/features/reviews/repository/reviews.repository.d.ts +25 -1
- package/dist/features/reviews/repository/reviews.repository.js +53 -13
- package/dist/features/reviews/schemas/firestore.d.ts +6 -0
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/search/components/Search.js +3 -3
- package/dist/features/search/components/SearchResultsSection.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +3 -1
- package/dist/features/seller/components/SellerBidsView.js +6 -1
- package/dist/features/seller/components/SellerSidebar.js +1 -1
- package/dist/features/shell/SectionForm.d.ts +21 -1
- package/dist/features/shell/SectionForm.js +15 -3
- package/dist/features/shell/build-sections.js +10 -1
- package/dist/features/shell/field-ui-meta.d.ts +21 -0
- package/dist/features/shipments/components/AdminShipmentLotItemsView.js +23 -6
- package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
- package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +21 -4
- package/dist/features/store-extensions/repository/store-extensions.repositories.js +24 -4
- package/dist/features/store-extensions/schemas/shipping-config-form.js +13 -0
- package/dist/features/store-extensions/schemas/store-category-form.js +1 -1
- package/dist/features/stores/actions/store-query-actions.js +25 -1
- package/dist/features/stores/components/InteractiveStoreCard.js +1 -1
- package/dist/features/stores/components/StoreHeader.js +1 -1
- package/dist/features/stores/components/StoreNavTabs.js +20 -8
- package/dist/features/support/repository/support.repository.d.ts +12 -0
- package/dist/features/support/repository/support.repository.js +15 -0
- package/dist/features/support/schemas/ticket-create-form.d.ts +33 -0
- package/dist/features/support/schemas/ticket-create-form.js +82 -0
- package/dist/features/tester/seed-data/products-tester-seed-data.js +2 -2
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +138 -1
- package/dist/features/wishlist/components/WishlistHeartButton.d.ts +55 -0
- package/dist/features/wishlist/components/WishlistHeartButton.js +52 -0
- package/dist/features/wishlist/components/index.d.ts +2 -1
- package/dist/features/wishlist/components/index.js +1 -1
- package/dist/index.d.ts +10 -3
- package/dist/index.js +19 -4
- package/dist/monitoring/error-tracking.js +39 -5
- package/dist/monitoring/server-logger.js +70 -32
- package/dist/next/components/ErrorView.js +1 -1
- package/dist/next/routing/route-map.d.ts +39 -7
- package/dist/next/routing/route-map.js +34 -4
- package/dist/providers/db-firebase/base.repository.d.ts +30 -0
- package/dist/providers/db-firebase/base.repository.js +56 -9
- package/dist/react/ErrorBoundary.js +9 -1
- package/dist/react/contexts/SessionContext.js +6 -2
- package/dist/react/hooks/useAuthGate.d.ts +22 -0
- package/dist/react/hooks/useAuthGate.js +86 -7
- package/dist/schemas/registry.d.ts +3 -3
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/pii-encrypt.d.ts +13 -5
- package/dist/security/pii-encrypt.js +132 -6
- package/dist/security/pii-schemas.d.ts +20 -4
- package/dist/security/pii-schemas.js +23 -5
- package/dist/seed/_helpers/product-search-txt.d.ts +18 -0
- package/dist/seed/_helpers/product-search-txt.js +33 -0
- package/dist/seed/faq-seed-data.d.ts +16 -0
- package/dist/seed/faq-seed-data.js +22 -1
- package/dist/seed/products-art-seed-data.js +49 -7
- package/dist/seed/products-auctions-seed-data.js +12 -4
- package/dist/seed/products-classifieds-seed-data.d.ts +5 -0
- package/dist/seed/products-classifieds-seed-data.js +65 -9
- package/dist/seed/products-digital-codes-seed-data.d.ts +5 -0
- package/dist/seed/products-digital-codes-seed-data.js +65 -9
- package/dist/seed/products-live-items-seed-data.d.ts +5 -0
- package/dist/seed/products-live-items-seed-data.js +33 -5
- package/dist/seed/products-preorders-seed-data.d.ts +1 -1
- package/dist/seed/products-preorders-seed-data.js +6 -2
- package/dist/seed/products-prize-draws-seed-data.d.ts +5 -0
- package/dist/seed/products-prize-draws-seed-data.js +57 -8
- package/dist/seed/products-standard-seed-data.js +6 -2
- package/dist/seed/products-stickers-seed-data.js +49 -7
- package/dist/seed/site-settings-seed-data.js +5 -3
- package/dist/seo/json-ld.d.ts +44 -2
- package/dist/seo/json-ld.js +57 -11
- package/dist/styles.css +368 -90
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/index.d.ts +1 -1
- package/dist/tokens/index.js +1 -1
- package/dist/tokens/themes/default-dark.js +6 -0
- package/dist/tokens/themes/default-light.js +5 -0
- package/dist/tokens/tokens.css +84 -0
- package/dist/ui/DataTable.js +1 -1
- package/dist/ui/DataTable.style.css +3 -3
- package/dist/ui/components/BulkActionBar.style.css +1 -3
- package/dist/ui/components/Button.js +15 -6
- package/dist/ui/components/Dropdown.style.css +1 -4
- package/dist/ui/components/FilterChipGroup.js +1 -1
- package/dist/ui/components/FilterDrawer.style.css +1 -1
- package/dist/ui/components/Icon.d.ts +53 -0
- package/dist/ui/components/Icon.js +41 -0
- package/dist/ui/components/IconButton.d.ts +13 -0
- package/dist/ui/components/IconButton.js +13 -0
- package/dist/ui/components/IconButton.style.css +18 -10
- package/dist/ui/components/ListingLayout.style.css +2 -10
- package/dist/ui/components/ListingToolbar.js +6 -6
- package/dist/ui/components/Modal.js +1 -1
- package/dist/ui/components/PaginatedSelect.style.css +3 -3
- package/dist/ui/components/Pagination.style.css +1 -6
- package/dist/ui/components/RichTextEditor.js +1 -1
- package/dist/ui/components/RowActionMenu.style.css +1 -1
- package/dist/ui/components/SideDrawer.style.css +1 -1
- package/dist/ui/components/Slider.js +1 -1
- package/dist/ui/components/TabBarShell.d.ts +78 -0
- package/dist/ui/components/TabBarShell.js +95 -0
- package/dist/ui/components/Tabs.js +12 -8
- package/dist/ui/components/Tabs.style.css +249 -41
- package/dist/ui/components/ViewToggle.style.css +1 -5
- package/dist/ui/components/tabs-overflow.d.ts +17 -0
- package/dist/ui/components/tabs-overflow.js +142 -0
- package/dist/ui/icons/icon-registry.d.ts +38 -0
- package/dist/ui/icons/icon-registry.js +37 -0
- package/dist/ui/index.d.ts +6 -0
- package/dist/ui/index.js +3 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/media-url.d.ts +27 -4
- package/dist/utils/media-url.js +37 -6
- package/dist/utils/search-txt.d.ts +71 -0
- package/dist/utils/search-txt.js +118 -0
- package/package.json +1 -1
|
@@ -18,7 +18,26 @@ function getLogsDir() {
|
|
|
18
18
|
}
|
|
19
19
|
const MAX_LOG_FILE_SIZE = 10 * 1024 * 1024;
|
|
20
20
|
const MAX_LOG_FILES = 10;
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* File logging is OPT-IN, not "anywhere that isn't Vercel".
|
|
23
|
+
*
|
|
24
|
+
* This was `!process.env.VERCEL`, which is `true` inside Cloud Functions — where
|
|
25
|
+
* `VERCEL` is simply unset and the filesystem is read-only. Every log line in
|
|
26
|
+
* every Function therefore ran mkdir/stat/appendFile, failed, and emitted a
|
|
27
|
+
* SECOND line carrying the EROFS stack: every Function log doubled, plus four
|
|
28
|
+
* wasted syscalls per line, against a log buffer that drops at ~4 KB/s
|
|
29
|
+
* (CLAUDE.md Rule #6).
|
|
30
|
+
*
|
|
31
|
+
* Serverless runtimes have no durable disk and their stdout is already
|
|
32
|
+
* collected, so the only place local files help is a developer machine.
|
|
33
|
+
* `LOG_TO_FILE=1` turns it on there; nothing else does.
|
|
34
|
+
*/
|
|
35
|
+
const isServerlessRuntime = Boolean(process.env.VERCEL ||
|
|
36
|
+
process.env.FUNCTION_TARGET || // Cloud Functions (2nd gen)
|
|
37
|
+
process.env.K_SERVICE || // Cloud Run / Functions
|
|
38
|
+
process.env.FUNCTIONS_EMULATOR ||
|
|
39
|
+
process.env.AWS_LAMBDA_FUNCTION_NAME);
|
|
40
|
+
const isFileLoggingEnabled = process.env.LOG_TO_FILE === "1" && !isServerlessRuntime;
|
|
22
41
|
function serializeError(error) {
|
|
23
42
|
const serialized = {
|
|
24
43
|
name: error.name,
|
|
@@ -136,46 +155,65 @@ async function writeLog(entry) {
|
|
|
136
155
|
console.error("Failed to write log:", error);
|
|
137
156
|
}
|
|
138
157
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
158
|
+
/**
|
|
159
|
+
* Cloud Logging severity names. GCP parses a JSON stdout line and promotes
|
|
160
|
+
* `severity` into the real log level; a plain `[INFO] msg` string arrives as
|
|
161
|
+
* unparsed `DEFAULT`-severity text, which is what every appkit server log has
|
|
162
|
+
* been doing — invisible to any severity filter or alert.
|
|
163
|
+
*/
|
|
164
|
+
const GCP_SEVERITY = {
|
|
165
|
+
debug: "DEBUG",
|
|
166
|
+
info: "INFO",
|
|
167
|
+
warn: "WARNING",
|
|
168
|
+
error: "ERROR",
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* Emit one line. Structured JSON where a collector is reading stdout
|
|
172
|
+
* (Vercel/Cloud Functions/Cloud Run), human-readable locally.
|
|
173
|
+
*/
|
|
174
|
+
function emit(level, message, sanitized) {
|
|
175
|
+
const consoleFn = level === "error" ? console.error : level === "warn" ? console.warn : level === "debug" ? console.debug : console.info;
|
|
176
|
+
if (isServerlessRuntime) {
|
|
177
|
+
consoleFn(JSON.stringify({
|
|
178
|
+
severity: GCP_SEVERITY[level],
|
|
148
179
|
message,
|
|
149
180
|
timestamp: new Date().toISOString(),
|
|
150
|
-
data: sanitized,
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
181
|
+
...(sanitized !== undefined ? { data: sanitized } : {}),
|
|
182
|
+
}));
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
consoleFn(`[${level.toUpperCase()}] ${message}`, sanitized);
|
|
186
|
+
}
|
|
187
|
+
function record(level, message, data) {
|
|
188
|
+
const sanitized = data ? redactPii(normalizeLogData(data)) : undefined;
|
|
189
|
+
emit(level, message, sanitized);
|
|
190
|
+
if (isFileLoggingEnabled) {
|
|
158
191
|
const entry = {
|
|
159
|
-
level
|
|
192
|
+
level,
|
|
160
193
|
message,
|
|
161
194
|
timestamp: new Date().toISOString(),
|
|
162
195
|
data: sanitized,
|
|
163
196
|
};
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
197
|
+
writeLog(entry).catch((err) => {
|
|
198
|
+
// stderr, not the logger — a logger that logs its own failure through
|
|
199
|
+
// itself recurses. normalizeError is pure classification, so it is safe
|
|
200
|
+
// to call here.
|
|
201
|
+
process.stderr.write(`log write failed: ${normalizeError(err).message}\n`);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export const serverLogger = {
|
|
206
|
+
debug(message, data) {
|
|
207
|
+
record("debug", message, data);
|
|
208
|
+
},
|
|
209
|
+
info(message, data) {
|
|
210
|
+
record("info", message, data);
|
|
211
|
+
},
|
|
212
|
+
warn(message, data) {
|
|
213
|
+
record("warn", message, data);
|
|
167
214
|
},
|
|
168
215
|
error(message, data) {
|
|
169
|
-
|
|
170
|
-
const entry = {
|
|
171
|
-
level: "error",
|
|
172
|
-
message,
|
|
173
|
-
timestamp: new Date().toISOString(),
|
|
174
|
-
data: sanitized,
|
|
175
|
-
};
|
|
176
|
-
console.error(`[ERROR] ${message}`, sanitized);
|
|
177
|
-
if (isFileLoggingEnabled)
|
|
178
|
-
writeLog(entry).catch((err) => { process.stderr.write(String(err) + "\n"); });
|
|
216
|
+
record("error", message, data);
|
|
179
217
|
},
|
|
180
218
|
};
|
|
181
219
|
/** Pull the commonly needed headers from a NextRequest in one call. */
|
|
@@ -28,5 +28,5 @@ export function ErrorView({ error, reset, heading = "Something went wrong", desc
|
|
|
28
28
|
metadata: { digest: error.digest ?? null },
|
|
29
29
|
});
|
|
30
30
|
}, [error]);
|
|
31
|
-
return (_jsxs(Stack, { justify: "center", role: "alert", className: "min-h-[50vh] text-center", padding: "x-xl", align: "center", children: [_jsx(Heading, { level: 2, className: "mb-2", children: heading }), _jsx(Text, { variant: "secondary", className: "mb-6", children: description }),
|
|
31
|
+
return (_jsxs(Stack, { justify: "center", role: "alert", className: "min-h-[50vh] text-center", padding: "x-xl", align: "center", children: [_jsx(Heading, { level: 2, className: "mb-2", children: heading }), _jsx(Text, { variant: "secondary", className: "mb-6", children: description }), error.digest && (_jsxs(Text, { variant: "muted", size: "xs", className: "mb-4", children: ["Reference: ", error.digest] })), _jsx(Button, { type: "button", variant: "primary", onClick: reset, children: retryLabel })] }));
|
|
32
32
|
}
|
|
@@ -143,8 +143,6 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
143
143
|
*/
|
|
144
144
|
readonly ADDRESSES_NEW: "/user/addresses/new";
|
|
145
145
|
readonly ADDRESSES_EDIT: (id: string) => string;
|
|
146
|
-
/** @deprecated Use ADDRESSES_NEW. Kept so existing links keep resolving. */
|
|
147
|
-
readonly ADDRESSES_ADD: "/user/addresses/new";
|
|
148
146
|
readonly ORDER_DETAIL: (id: string) => string;
|
|
149
147
|
readonly ORDER_CANCEL: (id: string) => string;
|
|
150
148
|
readonly ORDER_TRACK: (id: string) => string;
|
|
@@ -161,6 +159,7 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
161
159
|
readonly OFFERS: "/user/offers";
|
|
162
160
|
readonly REVIEWS: "/user/reviews";
|
|
163
161
|
readonly BIDS: "/user/bids";
|
|
162
|
+
readonly BID_DETAIL: (id: string) => string;
|
|
164
163
|
readonly RETURNS: "/user/returns";
|
|
165
164
|
readonly SUPPORT: "/user/support";
|
|
166
165
|
readonly SUPPORT_NEW: "/user/support/new";
|
|
@@ -198,10 +197,13 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
198
197
|
readonly SHIPPING: "/store/shipping";
|
|
199
198
|
readonly PAYOUT_SETTINGS: "/store/payout-settings";
|
|
200
199
|
readonly ADDRESSES: "/store/addresses";
|
|
200
|
+
readonly ADDRESSES_NEW: "/store/addresses/new";
|
|
201
|
+
readonly ADDRESSES_EDIT: (id: string) => string;
|
|
201
202
|
readonly OFFERS: "/store/offers";
|
|
202
203
|
readonly WHATSAPP: "/store/whatsapp";
|
|
203
204
|
readonly REVIEWS: "/store/reviews";
|
|
204
205
|
readonly BIDS: "/store/bids";
|
|
206
|
+
readonly BID_DETAIL: (id: string) => string;
|
|
205
207
|
readonly SUBLISTING_CATEGORIES: "/store/sublisting-categories";
|
|
206
208
|
readonly SUBLISTING_CATEGORIES_NEW: "/store/sublisting-categories/new";
|
|
207
209
|
readonly SUBLISTING_CATEGORIES_EDIT: (id: string) => string;
|
|
@@ -216,7 +218,6 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
216
218
|
readonly PRIZE_DRAWS_NEW: "/store/prize-draws/new";
|
|
217
219
|
readonly PRIZE_DRAWS_EDIT: (id: string) => string;
|
|
218
220
|
readonly PRIZE_DRAW_ENTRIES: (id: string) => string;
|
|
219
|
-
readonly PRODUCT_CODES: (id: string) => string;
|
|
220
221
|
readonly CLASSIFIED: "/store/classified";
|
|
221
222
|
readonly CLASSIFIED_NEW: "/store/classified/new";
|
|
222
223
|
readonly CLASSIFIED_EDIT: (id: string) => string;
|
|
@@ -270,14 +271,19 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
270
271
|
readonly CATALOGUE_APPROVALS: "/admin/catalogue-approvals";
|
|
271
272
|
readonly USERS: "/admin/users";
|
|
272
273
|
readonly USER_DETAIL: (id: string) => string;
|
|
274
|
+
readonly USER_EDIT: (id: string) => string;
|
|
273
275
|
readonly SITE: "/admin/site";
|
|
274
276
|
readonly CAROUSEL: "/admin/carousel";
|
|
275
277
|
readonly CAROUSEL_NEW: "/admin/carousel/new";
|
|
276
278
|
readonly CAROUSEL_EDIT: (id: string) => string;
|
|
277
279
|
readonly CAROUSELS: "/admin/carousels";
|
|
278
280
|
readonly CAROUSEL_DETAIL: (id: string) => string;
|
|
281
|
+
readonly CAROUSELS_NEW: "/admin/carousels/new";
|
|
282
|
+
readonly CAROUSELS_EDIT: (id: string) => string;
|
|
279
283
|
readonly SECTIONS: "/admin/sections";
|
|
280
284
|
readonly NAVIGATION: "/admin/navigation";
|
|
285
|
+
readonly NAVIGATION_NEW: "/admin/navigation/new";
|
|
286
|
+
readonly NAVIGATION_EDIT: (id: string) => string;
|
|
281
287
|
readonly CATEGORIES: "/admin/categories";
|
|
282
288
|
readonly CATEGORIES_NEW: "/admin/categories/new";
|
|
283
289
|
readonly CATEGORIES_EDIT: (id: string) => string;
|
|
@@ -288,6 +294,8 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
288
294
|
readonly FAQS_NEW: "/admin/faqs/new";
|
|
289
295
|
readonly FAQS_EDIT: (id: string) => string;
|
|
290
296
|
readonly TESTER_CHECKLIST: "/admin/tester-checklist";
|
|
297
|
+
readonly TESTER_CHECKLIST_NEW: "/admin/tester-checklist/new";
|
|
298
|
+
readonly TESTER_CHECKLIST_EDIT: (id: string) => string;
|
|
291
299
|
readonly TESTER_FEEDBACK: "/admin/tester-feedback";
|
|
292
300
|
readonly REVIEWS: "/admin/reviews";
|
|
293
301
|
readonly COUPONS: "/admin/coupons";
|
|
@@ -303,11 +311,14 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
303
311
|
readonly ART: "/admin/art";
|
|
304
312
|
readonly STICKERS: "/admin/stickers";
|
|
305
313
|
readonly GROUPED_LISTINGS: "/admin/grouped-listings";
|
|
314
|
+
readonly GROUPED_LISTINGS_NEW: "/admin/grouped-listings/new";
|
|
315
|
+
readonly GROUPED_LISTINGS_EDIT: (id: string) => string;
|
|
306
316
|
readonly DEALS: "/admin/deals";
|
|
307
317
|
readonly FEATURED: "/admin/featured";
|
|
308
318
|
readonly ORDERS: "/admin/orders";
|
|
309
319
|
readonly ORDER_DETAIL: (id: string) => string;
|
|
310
320
|
readonly BIDS: "/admin/bids";
|
|
321
|
+
readonly BID_DETAIL: (id: string) => string;
|
|
311
322
|
readonly OFFERS: "/admin/offers";
|
|
312
323
|
readonly BLOG: "/admin/blog";
|
|
313
324
|
readonly BLOG_NEW: "/admin/blog/new";
|
|
@@ -331,6 +342,7 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
331
342
|
readonly CONTACT: "/admin/contact";
|
|
332
343
|
readonly SESSIONS: "/admin/sessions";
|
|
333
344
|
readonly ALL_EVENT_ENTRIES: "/admin/event-entries";
|
|
345
|
+
readonly EVENT_ENTRY_DETAIL: (id: string) => string;
|
|
334
346
|
readonly NOTIFICATIONS: "/admin/notifications";
|
|
335
347
|
readonly CARTS: "/admin/carts";
|
|
336
348
|
readonly WISHLISTS: "/admin/wishlists";
|
|
@@ -353,8 +365,11 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
353
365
|
readonly PRIZE_DRAWS_EDIT: (id: string) => string;
|
|
354
366
|
readonly PRIZE_DRAWS_ENTRIES: (id: string) => string;
|
|
355
367
|
readonly LOTTERIES: "/admin/lotteries";
|
|
368
|
+
readonly LOTTERY_CONFIG: (id: string) => string;
|
|
356
369
|
readonly LOTTERY_ENTRIES: (id: string) => string;
|
|
357
370
|
readonly TEAM: "/admin/team";
|
|
371
|
+
readonly TEAM_NEW: "/admin/team/new";
|
|
372
|
+
readonly TEAM_EDIT: (id: string) => string;
|
|
358
373
|
readonly SUPPORT_TICKETS: "/admin/support-tickets";
|
|
359
374
|
readonly SUPPORT_TICKET_BY_ID: (id: string) => string;
|
|
360
375
|
readonly SCAMMERS: "/admin/scammers";
|
|
@@ -531,8 +546,6 @@ export declare const ROUTES: {
|
|
|
531
546
|
*/
|
|
532
547
|
readonly ADDRESSES_NEW: "/user/addresses/new";
|
|
533
548
|
readonly ADDRESSES_EDIT: (id: string) => string;
|
|
534
|
-
/** @deprecated Use ADDRESSES_NEW. Kept so existing links keep resolving. */
|
|
535
|
-
readonly ADDRESSES_ADD: "/user/addresses/new";
|
|
536
549
|
readonly ORDER_DETAIL: (id: string) => string;
|
|
537
550
|
readonly ORDER_CANCEL: (id: string) => string;
|
|
538
551
|
readonly ORDER_TRACK: (id: string) => string;
|
|
@@ -549,6 +562,7 @@ export declare const ROUTES: {
|
|
|
549
562
|
readonly OFFERS: "/user/offers";
|
|
550
563
|
readonly REVIEWS: "/user/reviews";
|
|
551
564
|
readonly BIDS: "/user/bids";
|
|
565
|
+
readonly BID_DETAIL: (id: string) => string;
|
|
552
566
|
readonly RETURNS: "/user/returns";
|
|
553
567
|
readonly SUPPORT: "/user/support";
|
|
554
568
|
readonly SUPPORT_NEW: "/user/support/new";
|
|
@@ -586,10 +600,13 @@ export declare const ROUTES: {
|
|
|
586
600
|
readonly SHIPPING: "/store/shipping";
|
|
587
601
|
readonly PAYOUT_SETTINGS: "/store/payout-settings";
|
|
588
602
|
readonly ADDRESSES: "/store/addresses";
|
|
603
|
+
readonly ADDRESSES_NEW: "/store/addresses/new";
|
|
604
|
+
readonly ADDRESSES_EDIT: (id: string) => string;
|
|
589
605
|
readonly OFFERS: "/store/offers";
|
|
590
606
|
readonly WHATSAPP: "/store/whatsapp";
|
|
591
607
|
readonly REVIEWS: "/store/reviews";
|
|
592
608
|
readonly BIDS: "/store/bids";
|
|
609
|
+
readonly BID_DETAIL: (id: string) => string;
|
|
593
610
|
readonly SUBLISTING_CATEGORIES: "/store/sublisting-categories";
|
|
594
611
|
readonly SUBLISTING_CATEGORIES_NEW: "/store/sublisting-categories/new";
|
|
595
612
|
readonly SUBLISTING_CATEGORIES_EDIT: (id: string) => string;
|
|
@@ -604,7 +621,6 @@ export declare const ROUTES: {
|
|
|
604
621
|
readonly PRIZE_DRAWS_NEW: "/store/prize-draws/new";
|
|
605
622
|
readonly PRIZE_DRAWS_EDIT: (id: string) => string;
|
|
606
623
|
readonly PRIZE_DRAW_ENTRIES: (id: string) => string;
|
|
607
|
-
readonly PRODUCT_CODES: (id: string) => string;
|
|
608
624
|
readonly CLASSIFIED: "/store/classified";
|
|
609
625
|
readonly CLASSIFIED_NEW: "/store/classified/new";
|
|
610
626
|
readonly CLASSIFIED_EDIT: (id: string) => string;
|
|
@@ -658,14 +674,19 @@ export declare const ROUTES: {
|
|
|
658
674
|
readonly CATALOGUE_APPROVALS: "/admin/catalogue-approvals";
|
|
659
675
|
readonly USERS: "/admin/users";
|
|
660
676
|
readonly USER_DETAIL: (id: string) => string;
|
|
677
|
+
readonly USER_EDIT: (id: string) => string;
|
|
661
678
|
readonly SITE: "/admin/site";
|
|
662
679
|
readonly CAROUSEL: "/admin/carousel";
|
|
663
680
|
readonly CAROUSEL_NEW: "/admin/carousel/new";
|
|
664
681
|
readonly CAROUSEL_EDIT: (id: string) => string;
|
|
665
682
|
readonly CAROUSELS: "/admin/carousels";
|
|
666
683
|
readonly CAROUSEL_DETAIL: (id: string) => string;
|
|
684
|
+
readonly CAROUSELS_NEW: "/admin/carousels/new";
|
|
685
|
+
readonly CAROUSELS_EDIT: (id: string) => string;
|
|
667
686
|
readonly SECTIONS: "/admin/sections";
|
|
668
687
|
readonly NAVIGATION: "/admin/navigation";
|
|
688
|
+
readonly NAVIGATION_NEW: "/admin/navigation/new";
|
|
689
|
+
readonly NAVIGATION_EDIT: (id: string) => string;
|
|
669
690
|
readonly CATEGORIES: "/admin/categories";
|
|
670
691
|
readonly CATEGORIES_NEW: "/admin/categories/new";
|
|
671
692
|
readonly CATEGORIES_EDIT: (id: string) => string;
|
|
@@ -676,6 +697,8 @@ export declare const ROUTES: {
|
|
|
676
697
|
readonly FAQS_NEW: "/admin/faqs/new";
|
|
677
698
|
readonly FAQS_EDIT: (id: string) => string;
|
|
678
699
|
readonly TESTER_CHECKLIST: "/admin/tester-checklist";
|
|
700
|
+
readonly TESTER_CHECKLIST_NEW: "/admin/tester-checklist/new";
|
|
701
|
+
readonly TESTER_CHECKLIST_EDIT: (id: string) => string;
|
|
679
702
|
readonly TESTER_FEEDBACK: "/admin/tester-feedback";
|
|
680
703
|
readonly REVIEWS: "/admin/reviews";
|
|
681
704
|
readonly COUPONS: "/admin/coupons";
|
|
@@ -691,11 +714,14 @@ export declare const ROUTES: {
|
|
|
691
714
|
readonly ART: "/admin/art";
|
|
692
715
|
readonly STICKERS: "/admin/stickers";
|
|
693
716
|
readonly GROUPED_LISTINGS: "/admin/grouped-listings";
|
|
717
|
+
readonly GROUPED_LISTINGS_NEW: "/admin/grouped-listings/new";
|
|
718
|
+
readonly GROUPED_LISTINGS_EDIT: (id: string) => string;
|
|
694
719
|
readonly DEALS: "/admin/deals";
|
|
695
720
|
readonly FEATURED: "/admin/featured";
|
|
696
721
|
readonly ORDERS: "/admin/orders";
|
|
697
722
|
readonly ORDER_DETAIL: (id: string) => string;
|
|
698
723
|
readonly BIDS: "/admin/bids";
|
|
724
|
+
readonly BID_DETAIL: (id: string) => string;
|
|
699
725
|
readonly OFFERS: "/admin/offers";
|
|
700
726
|
readonly BLOG: "/admin/blog";
|
|
701
727
|
readonly BLOG_NEW: "/admin/blog/new";
|
|
@@ -719,6 +745,7 @@ export declare const ROUTES: {
|
|
|
719
745
|
readonly CONTACT: "/admin/contact";
|
|
720
746
|
readonly SESSIONS: "/admin/sessions";
|
|
721
747
|
readonly ALL_EVENT_ENTRIES: "/admin/event-entries";
|
|
748
|
+
readonly EVENT_ENTRY_DETAIL: (id: string) => string;
|
|
722
749
|
readonly NOTIFICATIONS: "/admin/notifications";
|
|
723
750
|
readonly CARTS: "/admin/carts";
|
|
724
751
|
readonly WISHLISTS: "/admin/wishlists";
|
|
@@ -741,8 +768,11 @@ export declare const ROUTES: {
|
|
|
741
768
|
readonly PRIZE_DRAWS_EDIT: (id: string) => string;
|
|
742
769
|
readonly PRIZE_DRAWS_ENTRIES: (id: string) => string;
|
|
743
770
|
readonly LOTTERIES: "/admin/lotteries";
|
|
771
|
+
readonly LOTTERY_CONFIG: (id: string) => string;
|
|
744
772
|
readonly LOTTERY_ENTRIES: (id: string) => string;
|
|
745
773
|
readonly TEAM: "/admin/team";
|
|
774
|
+
readonly TEAM_NEW: "/admin/team/new";
|
|
775
|
+
readonly TEAM_EDIT: (id: string) => string;
|
|
746
776
|
readonly SUPPORT_TICKETS: "/admin/support-tickets";
|
|
747
777
|
readonly SUPPORT_TICKET_BY_ID: (id: string) => string;
|
|
748
778
|
readonly SCAMMERS: "/admin/scammers";
|
|
@@ -810,10 +840,13 @@ export declare const SELLER_ROUTES: {
|
|
|
810
840
|
readonly SHIPPING: "/store/shipping";
|
|
811
841
|
readonly PAYOUT_SETTINGS: "/store/payout-settings";
|
|
812
842
|
readonly ADDRESSES: "/store/addresses";
|
|
843
|
+
readonly ADDRESSES_NEW: "/store/addresses/new";
|
|
844
|
+
readonly ADDRESSES_EDIT: (id: string) => string;
|
|
813
845
|
readonly OFFERS: "/store/offers";
|
|
814
846
|
readonly WHATSAPP: "/store/whatsapp";
|
|
815
847
|
readonly REVIEWS: "/store/reviews";
|
|
816
848
|
readonly BIDS: "/store/bids";
|
|
849
|
+
readonly BID_DETAIL: (id: string) => string;
|
|
817
850
|
readonly SUBLISTING_CATEGORIES: "/store/sublisting-categories";
|
|
818
851
|
readonly SUBLISTING_CATEGORIES_NEW: "/store/sublisting-categories/new";
|
|
819
852
|
readonly SUBLISTING_CATEGORIES_EDIT: (id: string) => string;
|
|
@@ -828,7 +861,6 @@ export declare const SELLER_ROUTES: {
|
|
|
828
861
|
readonly PRIZE_DRAWS_NEW: "/store/prize-draws/new";
|
|
829
862
|
readonly PRIZE_DRAWS_EDIT: (id: string) => string;
|
|
830
863
|
readonly PRIZE_DRAW_ENTRIES: (id: string) => string;
|
|
831
|
-
readonly PRODUCT_CODES: (id: string) => string;
|
|
832
864
|
readonly CLASSIFIED: "/store/classified";
|
|
833
865
|
readonly CLASSIFIED_NEW: "/store/classified/new";
|
|
834
866
|
readonly CLASSIFIED_EDIT: (id: string) => string;
|
|
@@ -147,8 +147,6 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
147
147
|
*/
|
|
148
148
|
ADDRESSES_NEW: "/user/addresses/new",
|
|
149
149
|
ADDRESSES_EDIT: (id) => `/user/addresses/${id}/edit`,
|
|
150
|
-
/** @deprecated Use ADDRESSES_NEW. Kept so existing links keep resolving. */
|
|
151
|
-
ADDRESSES_ADD: "/user/addresses/new",
|
|
152
150
|
ORDER_DETAIL: (id) => `/user/orders/view/${id}`,
|
|
153
151
|
ORDER_CANCEL: (id) => `/user/orders/${id}/cancel`,
|
|
154
152
|
ORDER_TRACK: (id) => `/user/orders/${id}/track`,
|
|
@@ -165,6 +163,7 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
165
163
|
OFFERS: "/user/offers",
|
|
166
164
|
REVIEWS: "/user/reviews",
|
|
167
165
|
BIDS: "/user/bids",
|
|
166
|
+
BID_DETAIL: (id) => `/user/bids/${id}/view`,
|
|
168
167
|
RETURNS: "/user/returns",
|
|
169
168
|
SUPPORT: "/user/support",
|
|
170
169
|
SUPPORT_NEW: "/user/support/new",
|
|
@@ -202,10 +201,13 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
202
201
|
SHIPPING: "/store/shipping",
|
|
203
202
|
PAYOUT_SETTINGS: "/store/payout-settings",
|
|
204
203
|
ADDRESSES: "/store/addresses",
|
|
204
|
+
ADDRESSES_NEW: "/store/addresses/new",
|
|
205
|
+
ADDRESSES_EDIT: (id) => `/store/addresses/${id}/edit`,
|
|
205
206
|
OFFERS: "/store/offers",
|
|
206
207
|
WHATSAPP: "/store/whatsapp",
|
|
207
208
|
REVIEWS: "/store/reviews",
|
|
208
209
|
BIDS: "/store/bids",
|
|
210
|
+
BID_DETAIL: (id) => `/store/bids/${id}/view`,
|
|
209
211
|
SUBLISTING_CATEGORIES: "/store/sublisting-categories",
|
|
210
212
|
SUBLISTING_CATEGORIES_NEW: "/store/sublisting-categories/new",
|
|
211
213
|
SUBLISTING_CATEGORIES_EDIT: (id) => `/store/sublisting-categories/${id}/edit`,
|
|
@@ -221,7 +223,6 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
221
223
|
PRIZE_DRAWS_NEW: "/store/prize-draws/new",
|
|
222
224
|
PRIZE_DRAWS_EDIT: (id) => `/store/prize-draws/${id}/edit`,
|
|
223
225
|
PRIZE_DRAW_ENTRIES: (id) => `/store/prize-draws/${id}/entries`,
|
|
224
|
-
PRODUCT_CODES: (id) => `/store/products/${id}/codes`,
|
|
225
226
|
// SB-UNI-R — classified / digital-code / live
|
|
226
227
|
CLASSIFIED: "/store/classified",
|
|
227
228
|
CLASSIFIED_NEW: "/store/classified/new",
|
|
@@ -279,14 +280,24 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
279
280
|
CATALOGUE_APPROVALS: "/admin/catalogue-approvals",
|
|
280
281
|
USERS: "/admin/users",
|
|
281
282
|
USER_DETAIL: (id) => `/admin/users/${id}`,
|
|
283
|
+
USER_EDIT: (id) => `/admin/users/${id}/edit`,
|
|
282
284
|
SITE: "/admin/site",
|
|
283
285
|
CAROUSEL: "/admin/carousel",
|
|
284
286
|
CAROUSEL_NEW: "/admin/carousel/new",
|
|
285
287
|
CAROUSEL_EDIT: (id) => `/admin/carousel/${id}/edit`,
|
|
286
288
|
CAROUSELS: "/admin/carousels",
|
|
287
289
|
CAROUSEL_DETAIL: (id) => `/admin/carousels/${id}`,
|
|
290
|
+
/*
|
|
291
|
+
* 🛑 PLURAL = the named carousel GROUP. Singular `CAROUSEL_NEW` /
|
|
292
|
+
* `CAROUSEL_EDIT` above are `/admin/carousel/*` and mean a SLIDE.
|
|
293
|
+
* Two different entities, one letter apart — do not merge them.
|
|
294
|
+
*/
|
|
295
|
+
CAROUSELS_NEW: "/admin/carousels/new",
|
|
296
|
+
CAROUSELS_EDIT: (id) => `/admin/carousels/${id}/edit`,
|
|
288
297
|
SECTIONS: "/admin/sections",
|
|
289
298
|
NAVIGATION: "/admin/navigation",
|
|
299
|
+
NAVIGATION_NEW: "/admin/navigation/new",
|
|
300
|
+
NAVIGATION_EDIT: (id) => `/admin/navigation/${id}/edit`,
|
|
290
301
|
CATEGORIES: "/admin/categories",
|
|
291
302
|
CATEGORIES_NEW: "/admin/categories/new",
|
|
292
303
|
CATEGORIES_EDIT: (id) => `/admin/categories/${id}/edit`,
|
|
@@ -297,6 +308,8 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
297
308
|
FAQS_NEW: "/admin/faqs/new",
|
|
298
309
|
FAQS_EDIT: (id) => `/admin/faqs/${id}/edit`,
|
|
299
310
|
TESTER_CHECKLIST: "/admin/tester-checklist",
|
|
311
|
+
TESTER_CHECKLIST_NEW: "/admin/tester-checklist/new",
|
|
312
|
+
TESTER_CHECKLIST_EDIT: (id) => `/admin/tester-checklist/${id}/edit`,
|
|
300
313
|
TESTER_FEEDBACK: "/admin/tester-feedback",
|
|
301
314
|
REVIEWS: "/admin/reviews",
|
|
302
315
|
COUPONS: "/admin/coupons",
|
|
@@ -313,11 +326,14 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
313
326
|
ART: "/admin/art",
|
|
314
327
|
STICKERS: "/admin/stickers",
|
|
315
328
|
GROUPED_LISTINGS: "/admin/grouped-listings",
|
|
329
|
+
GROUPED_LISTINGS_NEW: "/admin/grouped-listings/new",
|
|
330
|
+
GROUPED_LISTINGS_EDIT: (id) => `/admin/grouped-listings/${id}/edit`,
|
|
316
331
|
DEALS: "/admin/deals",
|
|
317
332
|
FEATURED: "/admin/featured",
|
|
318
333
|
ORDERS: "/admin/orders",
|
|
319
334
|
ORDER_DETAIL: (id) => `/admin/orders/${id}/view`,
|
|
320
335
|
BIDS: "/admin/bids",
|
|
336
|
+
BID_DETAIL: (id) => `/admin/bids/${id}/view`,
|
|
321
337
|
OFFERS: "/admin/offers",
|
|
322
338
|
BLOG: "/admin/blog",
|
|
323
339
|
BLOG_NEW: "/admin/blog/new",
|
|
@@ -341,6 +357,7 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
341
357
|
CONTACT: "/admin/contact",
|
|
342
358
|
SESSIONS: "/admin/sessions",
|
|
343
359
|
ALL_EVENT_ENTRIES: "/admin/event-entries",
|
|
360
|
+
EVENT_ENTRY_DETAIL: (id) => `/admin/event-entries/${id}/view`,
|
|
344
361
|
NOTIFICATIONS: "/admin/notifications",
|
|
345
362
|
CARTS: "/admin/carts",
|
|
346
363
|
WISHLISTS: "/admin/wishlists",
|
|
@@ -365,8 +382,18 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
365
382
|
PRIZE_DRAWS_EDIT: (id) => `/admin/prize-draws/${id}/edit`,
|
|
366
383
|
PRIZE_DRAWS_ENTRIES: (id) => `/admin/prize-draws/${id}/entries`,
|
|
367
384
|
LOTTERIES: "/admin/lotteries",
|
|
385
|
+
/*
|
|
386
|
+
* Slot configuration only. A lottery EVENT (title, dates, status, media)
|
|
387
|
+
* is created through the ordinary event editor — `LotteryAdminEditView`
|
|
388
|
+
* has no fields for any of those, so treating it as the whole create
|
|
389
|
+
* flow would produce a lottery with no dates, and the draw window is
|
|
390
|
+
* measured from them.
|
|
391
|
+
*/
|
|
392
|
+
LOTTERY_CONFIG: (id) => `/admin/lotteries/${id}/edit`,
|
|
368
393
|
LOTTERY_ENTRIES: (id) => `/admin/lotteries/${id}/entries`,
|
|
369
394
|
TEAM: "/admin/team",
|
|
395
|
+
TEAM_NEW: "/admin/team/new",
|
|
396
|
+
TEAM_EDIT: (id) => `/admin/team/${id}/edit`,
|
|
370
397
|
SUPPORT_TICKETS: "/admin/support-tickets",
|
|
371
398
|
SUPPORT_TICKET_BY_ID: (id) => `/admin/support-tickets/${id}`,
|
|
372
399
|
SCAMMERS: "/admin/scammers",
|
|
@@ -479,7 +506,10 @@ export const PROTECTED_ROUTES = [
|
|
|
479
506
|
ROUTES.USER.SETTINGS,
|
|
480
507
|
ROUTES.USER.ORDERS,
|
|
481
508
|
ROUTES.USER.ADDRESSES,
|
|
482
|
-
|
|
509
|
+
// Was ADDRESSES_ADD, a deprecated alias resolving to the same path. Swapped
|
|
510
|
+
// rather than dropped: deleting the alias without this would have removed
|
|
511
|
+
// /user/addresses/new from the protected set entirely.
|
|
512
|
+
ROUTES.USER.ADDRESSES_NEW,
|
|
483
513
|
ROUTES.USER.NOTIFICATIONS,
|
|
484
514
|
ROUTES.USER.CART,
|
|
485
515
|
];
|
|
@@ -12,6 +12,36 @@ export declare abstract class BaseRepository<T extends DocumentData> {
|
|
|
12
12
|
findBy(field: string, value: unknown): Promise<T[]>;
|
|
13
13
|
findOneBy(field: string, value: unknown): Promise<T | null>;
|
|
14
14
|
findAll(limit?: number): Promise<T[]>;
|
|
15
|
+
/**
|
|
16
|
+
* PII fields for this collection, encrypted on every write path below.
|
|
17
|
+
*
|
|
18
|
+
* Declare it in the subclass instead of overriding `create`/`update` by hand.
|
|
19
|
+
* There were no encryption hooks here at all, so encryption was opt-in per
|
|
20
|
+
* method per repository — which meant `createWithId`, `update`, and the whole
|
|
21
|
+
* `*InTx` / `*InBatch` family were silent bypasses in any repo that had only
|
|
22
|
+
* overridden `create`. `sessions.deviceInfo.ip` sat in plaintext for exactly
|
|
23
|
+
* that reason.
|
|
24
|
+
*/
|
|
25
|
+
protected piiFields: readonly string[];
|
|
26
|
+
/** Blind-index map (plaintext field → index field) for equality lookups. */
|
|
27
|
+
protected piiIndexMap: Record<string, string>;
|
|
28
|
+
/**
|
|
29
|
+
* Build this document's `searchTxt` tokens. Return `null` to leave the field
|
|
30
|
+
* alone (the default), so a collection opts in by overriding this.
|
|
31
|
+
*
|
|
32
|
+
* MUST NOT include PII: `searchTxt` stores readable fragments of the source
|
|
33
|
+
* text, so indexing an encrypted field would undo the encryption.
|
|
34
|
+
*/
|
|
35
|
+
protected buildSearchTxtFor(_data: Record<string, unknown>): string[] | null;
|
|
36
|
+
/**
|
|
37
|
+
* The one place writes are transformed. Every write path routes through here
|
|
38
|
+
* so a new method cannot silently skip encryption.
|
|
39
|
+
*
|
|
40
|
+
* `encryptPiiFields` is idempotent (it skips anything already carrying the
|
|
41
|
+
* `enc:v1:` prefix), so a subclass that still encrypts by hand before calling
|
|
42
|
+
* super is harmless rather than double-encrypted.
|
|
43
|
+
*/
|
|
44
|
+
protected applyWriteHooks<D extends object>(data: D): D;
|
|
15
45
|
create(data: Partial<T> | FirestoreDocument): Promise<T>;
|
|
16
46
|
createWithId(id: string, data: Partial<T>): Promise<T>;
|
|
17
47
|
update(id: string, data: Partial<T>): Promise<T>;
|
|
@@ -4,8 +4,22 @@ import { serverLogger } from "../../monitoring";
|
|
|
4
4
|
import { applySieveToFirestore, countSieveMatches, } from "./sieve";
|
|
5
5
|
import { deserializeTimestamps, prepareForFirestore } from "./helpers";
|
|
6
6
|
import { getAdminDb } from "./admin";
|
|
7
|
+
import { encryptPiiFields, piiIndicesFor } from "../../security/pii-encrypt";
|
|
7
8
|
export class BaseRepository {
|
|
8
9
|
constructor(collectionName) {
|
|
10
|
+
/**
|
|
11
|
+
* PII fields for this collection, encrypted on every write path below.
|
|
12
|
+
*
|
|
13
|
+
* Declare it in the subclass instead of overriding `create`/`update` by hand.
|
|
14
|
+
* There were no encryption hooks here at all, so encryption was opt-in per
|
|
15
|
+
* method per repository — which meant `createWithId`, `update`, and the whole
|
|
16
|
+
* `*InTx` / `*InBatch` family were silent bypasses in any repo that had only
|
|
17
|
+
* overridden `create`. `sessions.deviceInfo.ip` sat in plaintext for exactly
|
|
18
|
+
* that reason.
|
|
19
|
+
*/
|
|
20
|
+
this.piiFields = [];
|
|
21
|
+
/** Blind-index map (plaintext field → index field) for equality lookups. */
|
|
22
|
+
this.piiIndexMap = {};
|
|
9
23
|
this.collection = collectionName;
|
|
10
24
|
}
|
|
11
25
|
get db() {
|
|
@@ -82,10 +96,43 @@ export class BaseRepository {
|
|
|
82
96
|
throw new DatabaseError("Failed to fetch all documents", error);
|
|
83
97
|
}
|
|
84
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Build this document's `searchTxt` tokens. Return `null` to leave the field
|
|
101
|
+
* alone (the default), so a collection opts in by overriding this.
|
|
102
|
+
*
|
|
103
|
+
* MUST NOT include PII: `searchTxt` stores readable fragments of the source
|
|
104
|
+
* text, so indexing an encrypted field would undo the encryption.
|
|
105
|
+
*/
|
|
106
|
+
buildSearchTxtFor(_data) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The one place writes are transformed. Every write path routes through here
|
|
111
|
+
* so a new method cannot silently skip encryption.
|
|
112
|
+
*
|
|
113
|
+
* `encryptPiiFields` is idempotent (it skips anything already carrying the
|
|
114
|
+
* `enc:v1:` prefix), so a subclass that still encrypts by hand before calling
|
|
115
|
+
* super is harmless rather than double-encrypted.
|
|
116
|
+
*/
|
|
117
|
+
applyWriteHooks(data) {
|
|
118
|
+
let out = data;
|
|
119
|
+
if (this.piiFields.length > 0) {
|
|
120
|
+
out = {
|
|
121
|
+
...encryptPiiFields(out, [...this.piiFields]),
|
|
122
|
+
// Indices are derived from the PLAINTEXT source and must not carry it
|
|
123
|
+
// along — see piiIndicesFor's docstring for the bug that caused.
|
|
124
|
+
...piiIndicesFor(data, this.piiIndexMap),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const searchTxt = this.buildSearchTxtFor(out);
|
|
128
|
+
if (searchTxt)
|
|
129
|
+
out.searchTxt = searchTxt;
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
85
132
|
async create(data) {
|
|
86
133
|
try {
|
|
87
134
|
const cleanData = prepareForFirestore({
|
|
88
|
-
...data,
|
|
135
|
+
...this.applyWriteHooks(data),
|
|
89
136
|
createdAt: new Date(),
|
|
90
137
|
updatedAt: new Date(),
|
|
91
138
|
});
|
|
@@ -102,7 +149,7 @@ export class BaseRepository {
|
|
|
102
149
|
try {
|
|
103
150
|
const now = new Date();
|
|
104
151
|
const cleanData = prepareForFirestore({
|
|
105
|
-
...data,
|
|
152
|
+
...this.applyWriteHooks(data),
|
|
106
153
|
createdAt: now,
|
|
107
154
|
updatedAt: now,
|
|
108
155
|
});
|
|
@@ -125,7 +172,7 @@ export class BaseRepository {
|
|
|
125
172
|
async update(id, data) {
|
|
126
173
|
try {
|
|
127
174
|
const cleanData = prepareForFirestore({
|
|
128
|
-
...data,
|
|
175
|
+
...this.applyWriteHooks(data),
|
|
129
176
|
updatedAt: new Date(),
|
|
130
177
|
});
|
|
131
178
|
await this.getCollection().doc(id).update(cleanData);
|
|
@@ -240,7 +287,7 @@ export class BaseRepository {
|
|
|
240
287
|
const docRef = this.getCollection().doc();
|
|
241
288
|
const now = new Date();
|
|
242
289
|
tx.set(docRef, prepareForFirestore({
|
|
243
|
-
...data,
|
|
290
|
+
...this.applyWriteHooks(data),
|
|
244
291
|
createdAt: now,
|
|
245
292
|
updatedAt: now,
|
|
246
293
|
}));
|
|
@@ -250,7 +297,7 @@ export class BaseRepository {
|
|
|
250
297
|
const docRef = this.getCollection().doc(id);
|
|
251
298
|
const now = new Date();
|
|
252
299
|
tx.set(docRef, prepareForFirestore({
|
|
253
|
-
...data,
|
|
300
|
+
...this.applyWriteHooks(data),
|
|
254
301
|
createdAt: now,
|
|
255
302
|
updatedAt: now,
|
|
256
303
|
}));
|
|
@@ -258,7 +305,7 @@ export class BaseRepository {
|
|
|
258
305
|
}
|
|
259
306
|
updateInTx(tx, id, data) {
|
|
260
307
|
const docRef = this.getCollection().doc(id);
|
|
261
|
-
tx.update(docRef, prepareForFirestore({ ...data, updatedAt: new Date() }));
|
|
308
|
+
tx.update(docRef, prepareForFirestore({ ...this.applyWriteHooks(data), updatedAt: new Date() }));
|
|
262
309
|
}
|
|
263
310
|
deleteInTx(tx, id) {
|
|
264
311
|
const docRef = this.getCollection().doc(id);
|
|
@@ -268,7 +315,7 @@ export class BaseRepository {
|
|
|
268
315
|
const docRef = this.getCollection().doc();
|
|
269
316
|
const now = new Date();
|
|
270
317
|
batch.set(docRef, prepareForFirestore({
|
|
271
|
-
...data,
|
|
318
|
+
...this.applyWriteHooks(data),
|
|
272
319
|
createdAt: now,
|
|
273
320
|
updatedAt: now,
|
|
274
321
|
}));
|
|
@@ -278,14 +325,14 @@ export class BaseRepository {
|
|
|
278
325
|
const docRef = this.getCollection().doc(id);
|
|
279
326
|
const now = new Date();
|
|
280
327
|
batch.set(docRef, prepareForFirestore({
|
|
281
|
-
...data,
|
|
328
|
+
...this.applyWriteHooks(data),
|
|
282
329
|
createdAt: now,
|
|
283
330
|
updatedAt: now,
|
|
284
331
|
}));
|
|
285
332
|
}
|
|
286
333
|
updateInBatch(batch, id, data) {
|
|
287
334
|
const docRef = this.getCollection().doc(id);
|
|
288
|
-
batch.update(docRef, prepareForFirestore({ ...data, updatedAt: new Date() }));
|
|
335
|
+
batch.update(docRef, prepareForFirestore({ ...this.applyWriteHooks(data), updatedAt: new Date() }));
|
|
289
336
|
}
|
|
290
337
|
deleteInBatch(batch, id) {
|
|
291
338
|
const docRef = this.getCollection().doc(id);
|