@mohasinac/appkit 3.6.5 → 3.7.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/layout/DashboardLayoutClient.d.ts +17 -3
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +7 -2
- package/dist/_internal/client/features/tour/TourProvider.d.ts +5 -2
- package/dist/_internal/client/features/tour/TourProvider.js +125 -12
- package/dist/_internal/client/theme/ThemeProvider.js +1 -1
- package/dist/_internal/server/features/faqs/og.js +5 -1
- package/dist/_internal/server/features/reviews/og.js +5 -1
- package/dist/_internal/server/features/seo/manifest.js +2 -1
- package/dist/_internal/server/features/seo/og.js +7 -1
- package/dist/_internal/server/features/tester/visibility.d.ts +13 -0
- package/dist/_internal/server/features/tester/visibility.js +28 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +9 -1
- package/dist/_internal/server/jobs/core/jobRunners.js +5 -0
- package/dist/_internal/server/jobs/core/resetOtpVerification.d.ts +17 -0
- package/dist/_internal/server/jobs/core/resetOtpVerification.js +88 -0
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +6 -0
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +35 -0
- package/dist/_internal/server/jobs/handlers/_helpers.d.ts +6 -0
- package/dist/_internal/server/jobs/handlers/_helpers.js +12 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +2 -0
- package/dist/_internal/server/jobs/handlers/messages.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/messages.js +2 -0
- package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.js +2 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +48 -0
- package/dist/_internal/shared/tokens/index.d.ts +12 -12
- package/dist/client.d.ts +15 -3
- package/dist/client.js +10 -2
- package/dist/constants/api-endpoints.d.ts +36 -12
- package/dist/constants/api-endpoints.js +16 -8
- package/dist/constants/field-names.d.ts +9 -0
- package/dist/constants/field-names.js +12 -0
- package/dist/constants/index.d.ts +1 -1
- package/dist/constants/index.js +1 -1
- package/dist/features/account/components/LinkedAccountsSection.d.ts +11 -0
- package/dist/features/account/components/LinkedAccountsSection.js +11 -0
- package/dist/features/account/components/UserSidebar.d.ts +3 -1
- package/dist/features/account/components/UserSidebar.js +9 -13
- package/dist/features/account/hooks/useCollapsedSections.d.ts +20 -0
- package/dist/features/account/hooks/useCollapsedSections.js +63 -0
- package/dist/features/account/hooks/useProfile.d.ts +8 -0
- package/dist/features/account/index.d.ts +1 -0
- package/dist/features/account/index.js +1 -0
- package/dist/features/admin/components/AdminAddressEditorView.js +2 -2
- package/dist/features/admin/components/AdminCarouselView.js +2 -2
- package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
- package/dist/features/admin/components/AdminProductEditorView.js +5 -2
- package/dist/features/admin/components/AdminSectionsView.js +3 -18
- package/dist/features/admin/components/AdminSidebar.js +8 -6
- package/dist/features/admin/components/AdminSiteSettingsView.js +133 -127
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.d.ts +9 -0
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +129 -0
- package/dist/features/admin/components/AdminTesterChecklistView.d.ts +6 -0
- package/dist/features/admin/components/AdminTesterChecklistView.js +93 -0
- package/dist/features/admin/components/AdminUserEditorView.d.ts +3 -1
- package/dist/features/admin/components/AdminUserEditorView.js +7 -1
- package/dist/features/admin/components/AdminUsersView.js +1 -1
- package/dist/features/admin/components/DataListingView.js +2 -2
- package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +8 -0
- package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +41 -0
- package/dist/features/admin/components/index.d.ts +6 -0
- package/dist/features/admin/components/index.js +3 -0
- package/dist/features/admin/schemas/firestore.d.ts +0 -7
- package/dist/features/admin/schemas/firestore.js +6 -6
- package/dist/features/analytics/components/PageViewTracker.d.ts +12 -0
- package/dist/features/analytics/components/PageViewTracker.js +22 -0
- package/dist/features/analytics/repository/page-views.repository.d.ts +32 -0
- package/dist/features/analytics/repository/page-views.repository.js +43 -0
- package/dist/features/analytics/types.d.ts +6 -0
- package/dist/features/analytics/types.js +13 -0
- package/dist/features/auth/components/SocialAuthButtons.js +1 -1
- package/dist/features/auth/hooks/useAuth.d.ts +12 -0
- package/dist/features/auth/hooks/useAuth.js +109 -0
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/schemas/firestore.d.ts +17 -1
- package/dist/features/auth/schemas/firestore.js +2 -0
- package/dist/features/blog/components/BlogIndexListing.js +2 -2
- package/dist/features/blog/schemas/firestore.d.ts +4 -0
- package/dist/features/cart/actions/cart-actions.js +20 -2
- package/dist/features/categories/components/CategoriesIndexListing.js +2 -2
- package/dist/features/categories/components/CategoryBundlesListing.js +2 -2
- package/dist/features/categories/components/CategoryDetailPageView.js +3 -1
- package/dist/features/categories/components/CategoryProductsListing.js +2 -2
- package/dist/features/categories/schemas/firestore.d.ts +4 -0
- package/dist/features/classified/components/ClassifiedIndexListing.js +2 -2
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +2 -2
- package/dist/features/email/primitives.d.ts +2 -2
- package/dist/features/email/primitives.js +2 -2
- package/dist/features/events/components/EventsIndexListing.js +2 -2
- package/dist/features/events/schemas/firestore.d.ts +4 -0
- package/dist/features/layout/BackToTop.d.ts +6 -1
- package/dist/features/layout/BackToTop.js +31 -6
- package/dist/features/live/components/LiveItemsIndexListing.js +2 -2
- package/dist/features/media/MediaVideo.js +5 -1
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -2
- package/dist/features/products/components/AuctionsIndexListing.js +2 -2
- package/dist/features/products/components/PrizeDrawsIndexListing.js +2 -2
- package/dist/features/products/components/ProductsIndexListing.js +2 -2
- package/dist/features/products/constants/action-defs.d.ts +2 -0
- package/dist/features/products/constants/action-defs.js +2 -0
- package/dist/features/products/schemas/firestore.d.ts +4 -0
- package/dist/features/promotions/components/CouponsIndexListing.js +2 -2
- package/dist/features/reviews/components/ReviewsIndexListing.js +2 -2
- package/dist/features/seller/actions/seller-actions.js +11 -6
- package/dist/features/seller/components/SellerAddressesView.js +2 -2
- package/dist/features/seller/components/SellerBidsView.js +2 -2
- package/dist/features/seller/components/SellerOrdersView.js +2 -2
- package/dist/features/seller/components/SellerProductsView.js +2 -2
- package/dist/features/seller/components/SellerSidebar.d.ts +3 -1
- package/dist/features/seller/components/SellerSidebar.js +9 -8
- package/dist/features/seller/components/SellerStoreCategoriesView.js +2 -2
- package/dist/features/seller/components/SellerTemplatesView.js +2 -2
- package/dist/features/shell/StepForm.js +4 -4
- package/dist/features/stores/actions/store-query-actions.d.ts +5 -4
- package/dist/features/stores/actions/store-query-actions.js +11 -8
- package/dist/features/stores/components/StoreAuctionsListing.js +2 -2
- package/dist/features/stores/components/StoreClassifiedsListing.js +2 -2
- package/dist/features/stores/components/StoreDigitalCodesListing.js +2 -2
- package/dist/features/stores/components/StoreLiveItemsListing.js +2 -2
- package/dist/features/stores/components/StorePreOrdersListing.js +2 -2
- package/dist/features/stores/components/StoreProductsListing.js +2 -2
- package/dist/features/stores/components/StoreReviewsListing.js +2 -2
- package/dist/features/stores/components/StoresIndexListing.js +2 -2
- package/dist/features/stores/schemas/firestore.d.ts +4 -0
- package/dist/features/tester/actions/checklist-item-actions.d.ts +69 -0
- package/dist/features/tester/actions/checklist-item-actions.js +42 -0
- package/dist/features/tester/actions/index.d.ts +1 -0
- package/dist/features/tester/actions/index.js +1 -0
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.d.ts +2 -0
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +22 -0
- package/dist/features/tester/components/AdminTesterFeedbackListView.d.ts +3 -0
- package/dist/features/tester/components/AdminTesterFeedbackListView.js +100 -0
- package/dist/features/tester/components/AdminTesterFeedbackReportView.d.ts +2 -0
- package/dist/features/tester/components/AdminTesterFeedbackReportView.js +30 -0
- package/dist/features/tester/components/AdminTesterFeedbackView.d.ts +2 -0
- package/dist/features/tester/components/AdminTesterFeedbackView.js +9 -0
- package/dist/features/tester/components/TesterChecklistStepRow.d.ts +18 -0
- package/dist/features/tester/components/TesterChecklistStepRow.js +29 -0
- package/dist/features/tester/components/TesterFeedbackChart.d.ts +14 -0
- package/dist/features/tester/components/TesterFeedbackChart.js +18 -0
- package/dist/features/tester/components/TesterHubView.d.ts +5 -0
- package/dist/features/tester/components/TesterHubView.js +71 -0
- package/dist/features/tester/components/index.d.ts +11 -0
- package/dist/features/tester/components/index.js +7 -0
- package/dist/features/tester/index.d.ts +2 -0
- package/dist/features/tester/index.js +2 -0
- package/dist/features/tester/repository/index.d.ts +2 -0
- package/dist/features/tester/repository/index.js +2 -0
- package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +13 -0
- package/dist/features/tester/repository/tester-checklist-item.repository.js +55 -0
- package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +42 -0
- package/dist/features/tester/repository/tester-checklist-response.repository.js +98 -0
- package/dist/features/tester/schemas/firestore.d.ts +85 -0
- package/dist/features/tester/schemas/firestore.js +93 -0
- package/dist/features/tester/schemas/index.d.ts +1 -0
- package/dist/features/tester/schemas/index.js +1 -0
- package/dist/features/tester/seed-data/blog-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/blog-tester-seed-data.js +40 -0
- package/dist/features/tester/seed-data/categories-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/categories-tester-seed-data.js +177 -0
- package/dist/features/tester/seed-data/events-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/events-tester-seed-data.js +50 -0
- package/dist/features/tester/seed-data/index.d.ts +6 -0
- package/dist/features/tester/seed-data/index.js +6 -0
- package/dist/features/tester/seed-data/products-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/products-tester-seed-data.js +151 -0
- package/dist/features/tester/seed-data/stores-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/stores-tester-seed-data.js +40 -0
- package/dist/features/tester/seed-data/tester-checklist-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +248 -0
- package/dist/features/tester/seed-data/tester-ttl.d.ts +4 -0
- package/dist/features/tester/seed-data/tester-ttl.js +6 -0
- package/dist/features/tester/server.d.ts +9 -0
- package/dist/features/tester/server.js +8 -0
- package/dist/index.d.ts +28 -2
- package/dist/index.js +51 -1
- package/dist/next/api/api-response.js +5 -1
- package/dist/next/api/routeHandler.js +10 -1
- package/dist/next/middleware/chain.js +1 -1
- package/dist/next/routing/route-map.d.ts +6 -7
- package/dist/next/routing/route-map.js +3 -7
- package/dist/providers/payment-manual/index.d.ts +2 -2
- package/dist/providers/payment-manual/index.js +2 -2
- package/dist/react/contexts/SessionContext.d.ts +4 -0
- package/dist/react/contexts/SessionContext.js +4 -0
- package/dist/repositories/index.d.ts +4 -0
- package/dist/repositories/index.js +3 -0
- package/dist/security/pii-encrypt.js +12 -2
- package/dist/seed/addresses-seed-data.js +1 -1
- package/dist/seed/bids-seed-data.js +1 -1
- package/dist/seed/blog-posts-seed-data.js +1 -1
- package/dist/seed/carousel-slides-seed-data.js +22 -16
- package/dist/seed/carousels-seed-data.js +1 -1
- package/dist/seed/cart-seed-data.js +1 -1
- package/dist/seed/categories-seed-data.js +1 -1
- package/dist/seed/claimed-coupons-seed-data.js +1 -1
- package/dist/seed/conversations-seed-data.js +1 -1
- package/dist/seed/coupon-usage-seed-data.js +1 -1
- package/dist/seed/coupons-seed-data.js +1 -1
- package/dist/seed/events-seed-data.js +1 -1
- package/dist/seed/faq-seed-data.js +119 -3
- package/dist/seed/grouped-listings-seed-data.d.ts +2 -0
- package/dist/seed/grouped-listings-seed-data.js +1 -1
- package/dist/seed/history-seed-data.js +1 -1
- package/dist/seed/homepage-sections-seed-data.js +1 -1
- package/dist/seed/index.d.ts +2 -2
- package/dist/seed/index.js +7 -3
- package/dist/seed/lottery-entries-seed-data.js +1 -1
- package/dist/seed/manifest.d.ts +4 -3
- package/dist/seed/manifest.js +13 -6
- package/dist/seed/notifications-seed-data.js +1 -1
- package/dist/seed/offers-seed-data.js +1 -1
- package/dist/seed/orders-seed-data.js +1 -1
- package/dist/seed/payouts-seed-data.js +1 -1
- package/dist/seed/products-art-seed-data.js +1 -1
- package/dist/seed/products-auctions-seed-data.js +1 -1
- package/dist/seed/products-classifieds-seed-data.js +1 -1
- package/dist/seed/products-digital-codes-seed-data.js +1 -1
- package/dist/seed/products-live-items-seed-data.js +1 -1
- package/dist/seed/products-preorders-seed-data.d.ts +2 -0
- package/dist/seed/products-preorders-seed-data.js +1 -1
- package/dist/seed/products-prize-draws-seed-data.js +1 -1
- package/dist/seed/products-standard-seed-data.js +1 -1
- package/dist/seed/products-stickers-seed-data.js +1 -1
- package/dist/seed/reviews-seed-data.js +1 -1
- package/dist/seed/scammers-seed-data.js +1 -1
- package/dist/seed/sessions-seed-data.js +1 -1
- package/dist/seed/site-settings-seed-data.js +22 -16
- package/dist/seed/store-addresses-seed-data.js +1 -1
- package/dist/seed/store-extensions-seed-data.js +1 -1
- package/dist/seed/stores-seed-data.js +1 -1
- package/dist/seed/support-tickets-seed-data.js +1 -1
- package/dist/seed/tester-checklist-seed-data.d.ts +2 -0
- package/dist/seed/tester-checklist-seed-data.js +248 -0
- package/dist/seed/users-seed-data.js +1 -1
- package/dist/seed/wishlists-seed-data.js +1 -1
- package/dist/server.d.ts +7 -2
- package/dist/server.js +19 -12
- package/dist/styles.css +163 -147
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/color-pairs.d.ts +46 -0
- package/dist/tokens/color-pairs.js +93 -0
- package/dist/tokens/index.d.ts +60 -60
- package/dist/tokens/index.js +30 -30
- package/dist/tokens/themes/cobalt-night.d.ts +3 -2
- package/dist/tokens/themes/cobalt-night.js +3 -2
- package/dist/tokens/themes/default-dark.d.ts +4 -1
- package/dist/tokens/themes/default-dark.js +44 -41
- package/dist/tokens/themes/default-light.d.ts +5 -1
- package/dist/tokens/themes/default-light.js +39 -35
- package/dist/tokens/themes/sunset.js +4 -4
- package/dist/tokens/tokens.css +110 -104
- package/dist/ui/components/CollapsibleSection.d.ts +15 -0
- package/dist/ui/components/CollapsibleSection.js +13 -0
- package/dist/ui/components/ListingToolbar.js +2 -1
- package/dist/ui/components/Semantic.style.css +3 -3
- package/dist/ui/components/SideDrawer.style.css +19 -19
- package/dist/ui/components/Slider.js +1 -1
- package/dist/ui/components/StickyToolbar.d.ts +36 -7
- package/dist/ui/components/StickyToolbar.js +63 -5
- package/dist/ui/components/Tabs.js +17 -1
- package/dist/ui/components/Tabs.style.css +10 -0
- package/dist/ui/components/Typography.js +7 -0
- package/dist/ui/components/surface-tokens.d.ts +0 -7
- package/dist/ui/components/surface-tokens.js +7 -0
- package/dist/ui/index.d.ts +6 -0
- package/dist/ui/index.js +3 -0
- package/dist/ui/rich-text/RichText.style.css +20 -20
- package/dist/utils/id-generators.d.ts +13 -0
- package/dist/utils/id-generators.js +17 -0
- package/package.json +1 -1
- package/scripts/seed-cli-loader.mjs +15 -0
- package/scripts/seed-cli.mjs +71 -17
|
@@ -19,11 +19,11 @@ export function StepIndicator({ steps, currentStep, onStepClick, stepErrors, })
|
|
|
19
19
|
const isDone = i < currentStep;
|
|
20
20
|
const isActive = i === currentStep;
|
|
21
21
|
const hasError = stepErrors?.[i] === true;
|
|
22
|
-
return (_jsxs(Row, { className: "flex-shrink-0", align: "center", gap: "none", children: [_jsxs(Button, { variant: "ghost", type: "button",
|
|
22
|
+
return (_jsxs(Row, { className: "flex-shrink-0", align: "center", gap: "none", children: [_jsxs(Button, { variant: "ghost", type: "button", onClick: () => onStepClick?.(i), gap: "sm", rounded: "lg", paddingX: "sm", paddingY: "xs", textSize: "sm", className: classNames("cursor-pointer", isActive
|
|
23
23
|
? "font-semibold text-[var(--appkit-color-primary)]"
|
|
24
24
|
: isDone
|
|
25
|
-
? "text-[var(--appkit-color-text-muted)]
|
|
26
|
-
: "text-[var(--appkit-color-text-faint)]
|
|
25
|
+
? "text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]"
|
|
26
|
+
: "text-[var(--appkit-color-text-faint)] hover:text-[var(--appkit-color-text-muted)]"), children: [_jsxs(Span, { className: "relative flex-shrink-0", children: [hasError && (_jsx(Span, { className: "absolute -top-0.5 -right-0.5 w-2 h-2 bg-[var(--appkit-color-error)] z-10", rounded: "full", "aria-label": "This step has errors" })), _jsx(Span, { className: classNames("flex-shrink-0 w-6 h-6 rounded-full flex items-center justify-center text-[length:var(--appkit-text-xs)] font-bold border-2", hasError
|
|
27
27
|
? "border-[var(--appkit-color-error)] bg-[var(--appkit-color-error)] text-white"
|
|
28
28
|
: isActive
|
|
29
29
|
? "border-[var(--appkit-color-primary)] bg-[var(--appkit-color-primary)] text-white"
|
|
@@ -86,7 +86,7 @@ export function StepForm({ steps, values, onChange, onComplete, completeLabel =
|
|
|
86
86
|
}, [currentStep, onStepChange]);
|
|
87
87
|
const currentStepDef = steps[currentStep];
|
|
88
88
|
const isLast = currentStep === steps.length - 1;
|
|
89
|
-
return (_jsxs(Stack, { children: [_jsx(StepIndicator, { steps: steps, currentStep: currentStep, stepErrors: stepErrors, onStepClick: (i) => { if (i
|
|
89
|
+
return (_jsxs(Stack, { children: [_jsx(StepIndicator, { steps: steps, currentStep: currentStep, stepErrors: stepErrors, onStepClick: (i) => { if (i !== currentStep) {
|
|
90
90
|
setStepError(null);
|
|
91
91
|
onStepChange(i);
|
|
92
92
|
} } }), _jsx(Div, { className: "flex-1", children: currentStepDef?.render({ values, onChange, errors: fieldErrors }) }), !hideActions && stepError && (_jsx(Text, { className: "mt-3 text-[var(--appkit-color-error)]", size: "sm", children: stepError })), !hideActions && (_jsx(StepFormActions, { currentStep: currentStep, totalSteps: steps.length, onNext: () => void goNext(), onPrev: currentStep > 0 ? goPrev : undefined, completeLabel: completeLabel, isLoading: isLoading && isLast, disabled: isLoading }))] }));
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Public and seller-facing read functions for store listings, products,
|
|
5
5
|
* auctions and reviews. No auth — callers decide whether auth is needed.
|
|
6
6
|
*/
|
|
7
|
+
import { type ViewerLike } from "../../../_internal/server/features/tester/visibility";
|
|
7
8
|
import type { StoreDocument } from "../schemas";
|
|
8
9
|
import type { ProductDocument } from "../../products/schemas";
|
|
9
10
|
import type { ReviewDocument } from "../../reviews/schemas";
|
|
@@ -27,8 +28,8 @@ export interface StoreReviewsResult {
|
|
|
27
28
|
totalReviews: number;
|
|
28
29
|
ratingDistribution: Record<number, number>;
|
|
29
30
|
}
|
|
30
|
-
export declare function listStores(params?: StoreQueryListParams): Promise<FirebaseSieveResult<StoreDocument>>;
|
|
31
|
-
export declare function getStoreBySlug(storeSlug: string): Promise<StoreDocument | null>;
|
|
32
|
-
export declare function getStoreProducts(storeSlug: string, params?: StoreContentParams): Promise<FirebaseSieveResult<ProductDocument>>;
|
|
33
|
-
export declare function getStoreAuctions(storeSlug: string, params?: StoreContentParams): Promise<FirebaseSieveResult<ProductDocument>>;
|
|
31
|
+
export declare function listStores(params?: StoreQueryListParams, viewer?: ViewerLike | null): Promise<FirebaseSieveResult<StoreDocument>>;
|
|
32
|
+
export declare function getStoreBySlug(storeSlug: string, viewer?: ViewerLike | null): Promise<StoreDocument | null>;
|
|
33
|
+
export declare function getStoreProducts(storeSlug: string, params?: StoreContentParams, viewer?: ViewerLike | null): Promise<FirebaseSieveResult<ProductDocument>>;
|
|
34
|
+
export declare function getStoreAuctions(storeSlug: string, params?: StoreContentParams, viewer?: ViewerLike | null): Promise<FirebaseSieveResult<ProductDocument>>;
|
|
34
35
|
export declare function getStoreReviews(storeSlug: string): Promise<StoreReviewsResult>;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
8
8
|
import { NotFoundError } from "../../../errors";
|
|
9
|
+
import { filterTestDataForViewer, filterSingleTestData } from "../../../_internal/server/features/tester/visibility";
|
|
9
10
|
const ERR_STORE_NOT_FOUND = "Store not found";
|
|
10
11
|
import { maskPublicReview } from "../../../security";
|
|
11
12
|
import { storeRepository } from "../repository/store.repository";
|
|
@@ -13,7 +14,7 @@ import { STORE_FIELDS } from "../schemas";
|
|
|
13
14
|
import { productRepository } from "../../products/repository/products.repository";
|
|
14
15
|
import { ProductStatusValues } from "../../products/schemas";
|
|
15
16
|
import { reviewRepository } from "../../reviews/repository/reviews.repository";
|
|
16
|
-
export async function listStores(params = {}) {
|
|
17
|
+
export async function listStores(params = {}, viewer) {
|
|
17
18
|
const { filters, sorts = "-createdAt", page = 1, pageSize = 24, q } = params;
|
|
18
19
|
const model = { filters, sorts, page, pageSize };
|
|
19
20
|
const filtersArr = [];
|
|
@@ -22,14 +23,16 @@ export async function listStores(params = {}) {
|
|
|
22
23
|
if (filters)
|
|
23
24
|
filtersArr.push(filters);
|
|
24
25
|
model.filters = filtersArr.join(",") || undefined;
|
|
25
|
-
|
|
26
|
+
const result = await storeRepository.listStores(model);
|
|
27
|
+
return { ...result, items: filterTestDataForViewer(result.items, viewer) };
|
|
26
28
|
}
|
|
27
|
-
export async function getStoreBySlug(storeSlug) {
|
|
28
|
-
|
|
29
|
+
export async function getStoreBySlug(storeSlug, viewer) {
|
|
30
|
+
const store = await storeRepository.findBySlug(storeSlug);
|
|
31
|
+
return filterSingleTestData(store, viewer);
|
|
29
32
|
}
|
|
30
|
-
export async function getStoreProducts(storeSlug, params = {}) {
|
|
33
|
+
export async function getStoreProducts(storeSlug, params = {}, viewer) {
|
|
31
34
|
const { sorts = "-createdAt", page = 1, pageSize = 24, filters } = params;
|
|
32
|
-
const storeDoc = await storeRepository.findBySlug(storeSlug);
|
|
35
|
+
const storeDoc = await filterSingleTestData(await storeRepository.findBySlug(storeSlug), viewer);
|
|
33
36
|
if (!storeDoc ||
|
|
34
37
|
storeDoc.status !== STORE_FIELDS.STATUS_VALUES.ACTIVE ||
|
|
35
38
|
!storeDoc.isPublic) {
|
|
@@ -49,9 +52,9 @@ export async function getStoreProducts(storeSlug, params = {}) {
|
|
|
49
52
|
pageSize,
|
|
50
53
|
});
|
|
51
54
|
}
|
|
52
|
-
export async function getStoreAuctions(storeSlug, params = {}) {
|
|
55
|
+
export async function getStoreAuctions(storeSlug, params = {}, viewer) {
|
|
53
56
|
const { sorts = "auctionEndDate", page = 1, pageSize = 24, filters } = params;
|
|
54
|
-
const storeDoc = await storeRepository.findBySlug(storeSlug);
|
|
57
|
+
const storeDoc = await filterSingleTestData(await storeRepository.findBySlug(storeSlug), viewer);
|
|
55
58
|
if (!storeDoc ||
|
|
56
59
|
storeDoc.status !== STORE_FIELDS.STATUS_VALUES.ACTIVE ||
|
|
57
60
|
!storeDoc.isPublic) {
|
|
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
|
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
5
|
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
6
6
|
import { useProducts } from "../../products/hooks/useProducts";
|
|
7
|
-
import { Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast } from "../../../ui";
|
|
7
|
+
import { Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast, StickyToolbar } from "../../../ui";
|
|
8
8
|
import { useAuthGate } from "../../../react/hooks/useAuthGate";
|
|
9
9
|
import { ACTION_ID } from "../../products/constants/action-defs";
|
|
10
10
|
import { MarketplaceAuctionGrid } from "../../auctions/components/MarketplaceAuctionGrid";
|
|
@@ -113,7 +113,7 @@ export function StoreAuctionsListing({ storeId, initialData }) {
|
|
|
113
113
|
return;
|
|
114
114
|
setView(v);
|
|
115
115
|
table.set("view", v);
|
|
116
|
-
}, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(
|
|
116
|
+
}, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-[var(--appkit-space-4)]" : gridClass, wishlistActions: wishlistActions, labels: {
|
|
117
117
|
emptyTitle: "No auctions yet",
|
|
118
118
|
emptyDescription: "This store has no active auctions.",
|
|
119
119
|
} })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(AuctionFilters, { table: pendingTable, currencyPrefix: "\u20B9" }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
|
|
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
|
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
5
|
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
6
6
|
import { useProducts } from "../../products/hooks/useProducts";
|
|
7
|
-
import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Text } from "../../../ui";
|
|
7
|
+
import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Text, StickyToolbar } from "../../../ui";
|
|
8
8
|
import { ClassifiedFilters } from "../../classified/components/ClassifiedFilters";
|
|
9
9
|
import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
|
|
10
10
|
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
@@ -40,7 +40,7 @@ export function StoreClassifiedsListing({ storeId, initialData }) {
|
|
|
40
40
|
};
|
|
41
41
|
const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
42
42
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
|
|
43
|
-
return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store classifieds\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(
|
|
43
|
+
return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store classifieds\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Div, { className: gridClass, children: isLoading
|
|
44
44
|
? Array.from({ length: 8 }).map((_, i) => _jsx(Div, { surface: "muted", rounded: "xl", className: "aspect-square animate-pulse" }, i))
|
|
45
45
|
: products.length === 0
|
|
46
46
|
? _jsx(Div, { paddingY: "y-4xl", className: "col-span-full text-left", children: _jsx(Text, { color: "muted", children: "No classified listings in this store yet." }) })
|
|
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
|
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
5
|
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
6
6
|
import { useProducts } from "../../products/hooks/useProducts";
|
|
7
|
-
import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Text } from "../../../ui";
|
|
7
|
+
import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Text, StickyToolbar } from "../../../ui";
|
|
8
8
|
import { DigitalCodeFilters } from "../../digital-codes/components/DigitalCodeFilters";
|
|
9
9
|
import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
|
|
10
10
|
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
@@ -40,7 +40,7 @@ export function StoreDigitalCodesListing({ storeId, initialData }) {
|
|
|
40
40
|
};
|
|
41
41
|
const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
42
42
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
|
|
43
|
-
return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search digital codes\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(
|
|
43
|
+
return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search digital codes\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Div, { className: gridClass, children: isLoading
|
|
44
44
|
? Array.from({ length: 8 }).map((_, i) => _jsx(Div, { surface: "muted", rounded: "xl", className: "aspect-square animate-pulse" }, i))
|
|
45
45
|
: products.length === 0
|
|
46
46
|
? _jsx(Div, { paddingY: "y-4xl", className: "col-span-full text-left", children: _jsx(Text, { color: "muted", children: "No digital codes in this store yet." }) })
|
|
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
|
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
5
|
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
6
6
|
import { useProducts } from "../../products/hooks/useProducts";
|
|
7
|
-
import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Text } from "../../../ui";
|
|
7
|
+
import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Text, StickyToolbar } from "../../../ui";
|
|
8
8
|
import { LiveItemFilters } from "../../live/components/LiveItemFilters";
|
|
9
9
|
import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
|
|
10
10
|
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
@@ -40,7 +40,7 @@ export function StoreLiveItemsListing({ storeId, initialData }) {
|
|
|
40
40
|
};
|
|
41
41
|
const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
42
42
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
|
|
43
|
-
return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search live animals & plants\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(
|
|
43
|
+
return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search live animals & plants\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Div, { className: gridClass, children: isLoading
|
|
44
44
|
? Array.from({ length: 8 }).map((_, i) => _jsx(Div, { surface: "muted", rounded: "xl", className: "aspect-square animate-pulse" }, i))
|
|
45
45
|
: products.length === 0
|
|
46
46
|
? _jsx(Div, { paddingY: "y-4xl", className: "col-span-full text-left", children: _jsx(Text, { color: "muted", children: "No live items in this store yet." }) })
|
|
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
|
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
5
|
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
6
6
|
import { useProducts } from "../../products/hooks/useProducts";
|
|
7
|
-
import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Stack, Text } from "../../../ui";
|
|
7
|
+
import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Stack, Text, StickyToolbar } from "../../../ui";
|
|
8
8
|
import { MarketplacePreorderCard } from "../../pre-orders/components/MarketplacePreorderCard";
|
|
9
9
|
import { ProductFilters } from "../../products/components/ProductFilters";
|
|
10
10
|
import { ROUTES } from "../../../next";
|
|
@@ -66,5 +66,5 @@ export function StorePreOrdersListing({ storeId, initialData }) {
|
|
|
66
66
|
return;
|
|
67
67
|
setView(v);
|
|
68
68
|
table.set("view", v);
|
|
69
|
-
}, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(
|
|
69
|
+
}, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "This store has no pre-orders yet." })) : view === "list" ? (_jsx(Stack, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", border: "subtle", rounded: "xl", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)) }, product.id))) })) : (_jsx(Div, { className: gridClass, children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "grid", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)) }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9" }) })] }));
|
|
70
70
|
}
|
|
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
|
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
5
|
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
6
6
|
import { useProducts } from "../../products/hooks/useProducts";
|
|
7
|
-
import { Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast } from "../../../ui";
|
|
7
|
+
import { Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast, StickyToolbar } from "../../../ui";
|
|
8
8
|
import { useAuthGate } from "../../../react/hooks/useAuthGate";
|
|
9
9
|
import { ACTION_ID } from "../../products/constants/action-defs";
|
|
10
10
|
import { ROUTES } from "../../../next";
|
|
@@ -93,5 +93,5 @@ export function StoreProductsListing({ storeId, initialData }) {
|
|
|
93
93
|
}, [localCart, showToast]);
|
|
94
94
|
return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store products...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "-createdAt", sortOptions: PRODUCT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => {
|
|
95
95
|
table.set("sort", v);
|
|
96
|
-
}, view: view === "card" ? "grid" : "list", onViewChange: (v) => handleViewToggle(v === "grid" ? "card" : "list"), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(
|
|
96
|
+
}, view: view === "card" ? "grid" : "list", onViewChange: (v) => handleViewToggle(v === "grid" ? "card" : "list"), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { layout: "grid", gap: "6", className: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-4", children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug || p.id)), view: view, emptyLabel: "This store has no products yet.", onWishlistToggle: handleWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: handleAddToCart })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9" }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
|
|
97
97
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useCallback, useMemo } from "react";
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
|
-
import { Div, ListingFilterDrawer, ListingToolbar, Pagination, Row, Span, Stack, Text } from "../../../ui";
|
|
5
|
+
import { Div, ListingFilterDrawer, ListingToolbar, Pagination, Row, Span, Stack, Text, StickyToolbar } from "../../../ui";
|
|
6
6
|
import { ReviewCard } from "../../reviews/components/ReviewsList";
|
|
7
7
|
import { ReviewFilters, REVIEW_PUBLIC_SORT_OPTIONS } from "../../reviews/components/ReviewFilters";
|
|
8
8
|
import { useStoreReviews } from "../hooks/useStores";
|
|
@@ -107,5 +107,5 @@ export function StoreReviewsListing({ storeSlug }) {
|
|
|
107
107
|
value: opt.value,
|
|
108
108
|
label: SORT_OPTION_LABELS[opt.key] ?? opt.key,
|
|
109
109
|
}));
|
|
110
|
-
return (_jsxs(Div, { className: "min-h-screen", children: [totalReviews > 0 && (_jsxs(Row, { border: "default", align: "center", gap: "sm", className: "border-b", padding: "inline", children: [_jsx(Span, { weight: "bold", size: "2xl", color: "primary", children: averageRating.toFixed(1) }), _jsxs(Span, { size: "sm", color: "muted", children: ["/ 5 \u00B7 ", totalReviews, " reviews"] })] })), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(
|
|
110
|
+
return (_jsxs(Div, { className: "min-h-screen", children: [totalReviews > 0 && (_jsxs(Row, { border: "default", align: "center", gap: "sm", className: "border-b", padding: "inline", children: [_jsx(Span, { weight: "bold", size: "2xl", color: "primary", children: averageRating.toFixed(1) }), _jsxs(Span, { size: "sm", color: "muted", children: ["/ 5 \u00B7 ", totalReviews, " reviews"] })] })), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { paddingY: "y-lg", paddingX: "x-md", children: isLoading ? (_jsx(Div, { layout: "grid", gap: "6", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => (_jsx(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: _jsxs(Stack, { className: `${__P.p4}`, gap: "3", children: [_jsx(Div, { className: "h-4 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-2/3", surface: "subtle", rounded: "default" })] }) }, i))) })) : reviews.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No reviews found." })) : view === "list" ? (_jsx(Stack, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", border: "subtle", rounded: "xl", children: reviews.map((review) => (_jsx(ReviewCard, { review: review, context: "store" }, review.id))) })) : (_jsx(Div, { layout: "grid", gap: "6", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: reviews.map((review) => (_jsx(ReviewCard, { review: review, context: "store" }, review.id))) })) }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(ReviewFilters, { table: pendingTable, variant: "public" }) })] }));
|
|
111
111
|
}
|
|
@@ -4,7 +4,7 @@ import { Row, SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
|
|
|
4
4
|
import { useState, useCallback, useMemo } from "react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useStores } from "../hooks/useStores";
|
|
7
|
-
import { BulkActionBar, Div, ListingFilterDrawer, ListingToolbar, Pagination, Text } from "../../../ui";
|
|
7
|
+
import { BulkActionBar, Div, ListingFilterDrawer, ListingToolbar, Pagination, Text, StickyToolbar } from "../../../ui";
|
|
8
8
|
import { ROUTES } from "../../../next";
|
|
9
9
|
import { InteractiveStoreCard } from "./InteractiveStoreCard";
|
|
10
10
|
import { StoreFilters } from "./StoreFilters";
|
|
@@ -161,7 +161,7 @@ export function StoresIndexListing({ initialData }) {
|
|
|
161
161
|
disabled: selection.selectedCount !== 1,
|
|
162
162
|
onClick: () => { selection.clearSelection(); },
|
|
163
163
|
},
|
|
164
|
-
] }), totalPages > 1 && (_jsx(
|
|
164
|
+
] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: table.getNumber("page", 1), totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { layout: "grid", gap: "6", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-video", surface: "subtle" }), _jsxs(Div, { className: `${__P.p4} space-y-[0.625rem]`, children: [_jsx(Row, { align: "center", gap: "sm", children: _jsx(Div, { className: "h-10 w-10", surface: "subtle", rounded: "lg" }) }), _jsx(Div, { className: "h-4 w-2/3", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" })] })] }, i))) })) : stores.length === 0 ? (_jsx(Text, { paddingY: "4xl", color: "muted", size: "sm", align: "start", children: "No stores found." })) : view === "list" ? (_jsx(Stack, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", border: "subtle", rounded: "xl", children: stores.map((store) => {
|
|
165
165
|
const storeKey = store.storeSlug ?? store.id;
|
|
166
166
|
return (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(storeKey)), selectable: selection.isSelecting, isSelected: selection.isSelected(store.id ?? store.storeSlug), onSelect: (id, sel) => { void sel; selection.toggle(id); } }, storeKey));
|
|
167
167
|
}) })) : (_jsx(Div, { layout: "grid", gap: "6", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: stores.map((store) => {
|
|
@@ -50,6 +50,10 @@ export type StoreStatus = (typeof StoreStatusValues)[keyof typeof StoreStatusVal
|
|
|
50
50
|
export interface StoreDocument extends BaseDocument {
|
|
51
51
|
storeSlug: string;
|
|
52
52
|
ownerId: string;
|
|
53
|
+
/** Tester sandbox flag — set on the shared admin-seeded test store; swept by testerSandboxCleanup. */
|
|
54
|
+
isTestData?: boolean;
|
|
55
|
+
/** When isTestData, the cutoff after which testerSandboxCleanup deletes this doc. */
|
|
56
|
+
testDataExpiresAt?: Date;
|
|
53
57
|
storeName: string;
|
|
54
58
|
storeDescription?: string;
|
|
55
59
|
storeCategory?: string;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { TesterChecklistItemDocument } from "../schemas/firestore";
|
|
3
|
+
export declare const checklistItemCreateSchema: z.ZodObject<{
|
|
4
|
+
groupKey: z.ZodString;
|
|
5
|
+
groupLabel: z.ZodString;
|
|
6
|
+
pageKey: z.ZodString;
|
|
7
|
+
pageLabel: z.ZodString;
|
|
8
|
+
label: z.ZodString;
|
|
9
|
+
description: z.ZodOptional<z.ZodString>;
|
|
10
|
+
href: z.ZodOptional<z.ZodString>;
|
|
11
|
+
order: z.ZodDefault<z.ZodNumber>;
|
|
12
|
+
isActive: z.ZodDefault<z.ZodBoolean>;
|
|
13
|
+
}, "strip", z.ZodTypeAny, {
|
|
14
|
+
label: string;
|
|
15
|
+
order: number;
|
|
16
|
+
isActive: boolean;
|
|
17
|
+
groupKey: string;
|
|
18
|
+
pageKey: string;
|
|
19
|
+
groupLabel: string;
|
|
20
|
+
pageLabel: string;
|
|
21
|
+
href?: string | undefined;
|
|
22
|
+
description?: string | undefined;
|
|
23
|
+
}, {
|
|
24
|
+
label: string;
|
|
25
|
+
groupKey: string;
|
|
26
|
+
pageKey: string;
|
|
27
|
+
groupLabel: string;
|
|
28
|
+
pageLabel: string;
|
|
29
|
+
href?: string | undefined;
|
|
30
|
+
description?: string | undefined;
|
|
31
|
+
order?: number | undefined;
|
|
32
|
+
isActive?: boolean | undefined;
|
|
33
|
+
}>;
|
|
34
|
+
export declare const checklistItemUpdateSchema: z.ZodObject<{
|
|
35
|
+
groupKey: z.ZodOptional<z.ZodString>;
|
|
36
|
+
groupLabel: z.ZodOptional<z.ZodString>;
|
|
37
|
+
pageKey: z.ZodOptional<z.ZodString>;
|
|
38
|
+
pageLabel: z.ZodOptional<z.ZodString>;
|
|
39
|
+
label: z.ZodOptional<z.ZodString>;
|
|
40
|
+
description: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
41
|
+
href: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
42
|
+
order: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
43
|
+
isActive: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
44
|
+
}, "strip", z.ZodTypeAny, {
|
|
45
|
+
label?: string | undefined;
|
|
46
|
+
href?: string | undefined;
|
|
47
|
+
description?: string | undefined;
|
|
48
|
+
order?: number | undefined;
|
|
49
|
+
isActive?: boolean | undefined;
|
|
50
|
+
groupKey?: string | undefined;
|
|
51
|
+
pageKey?: string | undefined;
|
|
52
|
+
groupLabel?: string | undefined;
|
|
53
|
+
pageLabel?: string | undefined;
|
|
54
|
+
}, {
|
|
55
|
+
label?: string | undefined;
|
|
56
|
+
href?: string | undefined;
|
|
57
|
+
description?: string | undefined;
|
|
58
|
+
order?: number | undefined;
|
|
59
|
+
isActive?: boolean | undefined;
|
|
60
|
+
groupKey?: string | undefined;
|
|
61
|
+
pageKey?: string | undefined;
|
|
62
|
+
groupLabel?: string | undefined;
|
|
63
|
+
pageLabel?: string | undefined;
|
|
64
|
+
}>;
|
|
65
|
+
export type ChecklistItemCreateInput = z.infer<typeof checklistItemCreateSchema>;
|
|
66
|
+
export type ChecklistItemUpdateInput = z.infer<typeof checklistItemUpdateSchema>;
|
|
67
|
+
export declare function createChecklistItem(input: ChecklistItemCreateInput): Promise<TesterChecklistItemDocument>;
|
|
68
|
+
export declare function updateChecklistItem(id: string, input: ChecklistItemUpdateInput): Promise<TesterChecklistItemDocument>;
|
|
69
|
+
export declare function deleteChecklistItem(id: string): Promise<void>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { serverLogger } from "../../../monitoring";
|
|
3
|
+
import { testerChecklistItemRepository } from "../repository/tester-checklist-item.repository";
|
|
4
|
+
// --- Schemas --------------------------------------------------------------
|
|
5
|
+
const checklistItemBaseSchema = z.object({
|
|
6
|
+
groupKey: z.string().min(1).max(100),
|
|
7
|
+
groupLabel: z.string().min(1).max(200),
|
|
8
|
+
pageKey: z.string().min(1).max(100),
|
|
9
|
+
pageLabel: z.string().min(1).max(200),
|
|
10
|
+
label: z.string().min(3).max(500),
|
|
11
|
+
description: z.string().max(2000).optional(),
|
|
12
|
+
href: z.string().max(300).optional(),
|
|
13
|
+
order: z.number().int().min(0).default(0),
|
|
14
|
+
isActive: z.boolean().default(true),
|
|
15
|
+
});
|
|
16
|
+
export const checklistItemCreateSchema = checklistItemBaseSchema;
|
|
17
|
+
export const checklistItemUpdateSchema = checklistItemBaseSchema.partial();
|
|
18
|
+
// --- Actions --------------------------------------------------------------
|
|
19
|
+
export async function createChecklistItem(input) {
|
|
20
|
+
const item = await testerChecklistItemRepository.createItem({
|
|
21
|
+
groupKey: input.groupKey,
|
|
22
|
+
groupLabel: input.groupLabel,
|
|
23
|
+
pageKey: input.pageKey,
|
|
24
|
+
pageLabel: input.pageLabel,
|
|
25
|
+
label: input.label,
|
|
26
|
+
description: input.description,
|
|
27
|
+
href: input.href,
|
|
28
|
+
order: input.order ?? 0,
|
|
29
|
+
isActive: input.isActive ?? true,
|
|
30
|
+
});
|
|
31
|
+
serverLogger.info("createChecklistItem", { itemId: item.id });
|
|
32
|
+
return item;
|
|
33
|
+
}
|
|
34
|
+
export async function updateChecklistItem(id, input) {
|
|
35
|
+
const item = await testerChecklistItemRepository.update(id, input);
|
|
36
|
+
serverLogger.info("updateChecklistItem", { itemId: id });
|
|
37
|
+
return item;
|
|
38
|
+
}
|
|
39
|
+
export async function deleteChecklistItem(id) {
|
|
40
|
+
await testerChecklistItemRepository.delete(id);
|
|
41
|
+
serverLogger.info("deleteChecklistItem", { itemId: id });
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./checklist-item-actions";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./checklist-item-actions";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
|
+
import { Card, Stack, Text } from "../../../ui";
|
|
5
|
+
import { MediaImage } from "../../media/MediaImage";
|
|
6
|
+
import { apiClient } from "../../../http";
|
|
7
|
+
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
|
+
export function AdminTesterFeedbackIssuesView() {
|
|
9
|
+
const query = useQuery({
|
|
10
|
+
queryKey: ["admin", "tester-feedback", "report"],
|
|
11
|
+
queryFn: async () => {
|
|
12
|
+
const res = await apiClient.get(ADMIN_ENDPOINTS.TESTER_FEEDBACK_REPORT);
|
|
13
|
+
return res?.data ?? res;
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
const issues = query.data?.issues ?? [];
|
|
17
|
+
if (query.isLoading)
|
|
18
|
+
return _jsx(Text, { color: "muted", children: "Loading issues\u2026" });
|
|
19
|
+
if (issues.length === 0)
|
|
20
|
+
return _jsx(Text, { color: "muted", children: "No \"No\" answers recorded yet." });
|
|
21
|
+
return (_jsx(Stack, { gap: "sm", children: issues.map((issue) => (_jsx(Card, { padding: "md", variant: "default", children: _jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { weight: "semibold", children: [issue.groupKey, " / ", issue.pageKey] }), _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.id))) }));
|
|
22
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useApiMutation } from "../../../client";
|
|
4
|
+
import { SIEVE_OP, sieveFilter } from "../../../utils/sieve-builder";
|
|
5
|
+
import { sortBy } from "../../../constants/sort";
|
|
6
|
+
import { TESTER_CHECKLIST_RESPONSE_FIELDS } from "../schemas/firestore";
|
|
7
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
8
|
+
import { FilterChipGroup, RowActionMenu, Stack, Text } from "../../../ui";
|
|
9
|
+
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
|
+
import { toRecordArray, toRelativeDate, toStringValue, } from "../../admin/hooks/useAdminListingData";
|
|
12
|
+
import { DataListingView } from "../../admin/components/DataListingView";
|
|
13
|
+
import { apiClient } from "../../../http";
|
|
14
|
+
const COLUMNS = [
|
|
15
|
+
{
|
|
16
|
+
key: "primary",
|
|
17
|
+
header: "Tester / Case",
|
|
18
|
+
sortable: true,
|
|
19
|
+
render: (row) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { weight: "semibold", color: "primary", children: row.primary }), _jsx(Text, { size: "xs", color: "muted", children: row.secondary })] })),
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
key: "answer",
|
|
23
|
+
header: "Answer",
|
|
24
|
+
className: "w-24",
|
|
25
|
+
render: (row) => (_jsx(Text, { weight: "medium", color: row.answer === "no" ? "error" : "success", children: row.answer === "no" ? "No" : row.answer === "yes" ? "Yes" : "—" })),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
key: "status",
|
|
29
|
+
header: "Status",
|
|
30
|
+
className: "w-28",
|
|
31
|
+
render: (row) => _jsx(Text, { size: "sm", color: "muted", children: row.status }),
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: "updatedAt",
|
|
35
|
+
header: "Updated",
|
|
36
|
+
className: "w-32",
|
|
37
|
+
render: (row) => _jsx(Text, { size: "sm", color: "muted", children: row.updatedAt }),
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
export function AdminTesterFeedbackListView(_props) {
|
|
41
|
+
const queryClient = useQueryClient();
|
|
42
|
+
const patchMutation = useApiMutation({
|
|
43
|
+
mutationFn: async (id) => {
|
|
44
|
+
await apiClient.patch(ADMIN_ENDPOINTS.TESTER_FEEDBACK_BY_ID(id), {});
|
|
45
|
+
},
|
|
46
|
+
onSuccess: () => {
|
|
47
|
+
queryClient.invalidateQueries({ queryKey: ["admin", "tester-feedback"] });
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
const config = {
|
|
51
|
+
portal: "admin",
|
|
52
|
+
title: "All Submissions",
|
|
53
|
+
searchPlaceholder: "Search tester, comment, or case",
|
|
54
|
+
emptyLabel: "No feedback submitted yet",
|
|
55
|
+
filterKeys: ["answer", "status"],
|
|
56
|
+
defaultSort: sortBy(TESTER_CHECKLIST_RESPONSE_FIELDS.CREATED_AT, "DESC"),
|
|
57
|
+
queryKey: ["admin", "tester-feedback", "listing"],
|
|
58
|
+
endpoint: ADMIN_ENDPOINTS.TESTER_FEEDBACK,
|
|
59
|
+
sortOptions: [
|
|
60
|
+
{ value: sortBy(TESTER_CHECKLIST_RESPONSE_FIELDS.CREATED_AT, "DESC"), label: "Newest" },
|
|
61
|
+
{ value: sortBy(TESTER_CHECKLIST_RESPONSE_FIELDS.CREATED_AT, "ASC"), label: "Oldest" },
|
|
62
|
+
],
|
|
63
|
+
columns: COLUMNS,
|
|
64
|
+
mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
|
|
65
|
+
id: toStringValue(item.id, `feedback-${index}`),
|
|
66
|
+
primary: toStringValue(item.testerDisplayName, "Unknown tester"),
|
|
67
|
+
secondary: `${toStringValue(item.groupKey, "")} / ${toStringValue(item.pageKey, "")}${item.comment ? " — " + toStringValue(item.comment, "") : ""}`,
|
|
68
|
+
answer: toStringValue(item.answer, ""),
|
|
69
|
+
status: toStringValue(item.status, "new"),
|
|
70
|
+
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
71
|
+
screenshotUrl: item.screenshotUrl ? toStringValue(item.screenshotUrl, "") : undefined,
|
|
72
|
+
})),
|
|
73
|
+
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
74
|
+
buildFilters: (state) => {
|
|
75
|
+
const parts = [];
|
|
76
|
+
if (state.answer && state.answer !== "All")
|
|
77
|
+
parts.push(sieveFilter("answer", SIEVE_OP.EQ, state.answer));
|
|
78
|
+
if (state.status && state.status !== "All")
|
|
79
|
+
parts.push(sieveFilter("status", SIEVE_OP.EQ, state.status));
|
|
80
|
+
return parts.join(",") || undefined;
|
|
81
|
+
},
|
|
82
|
+
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Answer", tabs: [
|
|
83
|
+
{ id: "All", label: "All" },
|
|
84
|
+
{ id: "yes", label: "Yes" },
|
|
85
|
+
{ id: "no", label: "No" },
|
|
86
|
+
], value: pendingFilters.answer || "", onChange: (v) => setPendingFilters((p) => ({ ...p, answer: v })) }), _jsx(FilterChipGroup, { label: "Status", tabs: [
|
|
87
|
+
{ id: "All", label: "All" },
|
|
88
|
+
{ id: "new", label: "New" },
|
|
89
|
+
{ id: "reviewed", label: "Reviewed" },
|
|
90
|
+
], value: pendingFilters.status || "", onChange: (v) => setPendingFilters((p) => ({ ...p, status: v })) })] })),
|
|
91
|
+
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
92
|
+
{
|
|
93
|
+
label: ACTIONS.ADMIN["mark-feedback-reviewed"].label,
|
|
94
|
+
disabled: row.status === "reviewed",
|
|
95
|
+
onClick: () => patchMutation.mutate(row.id),
|
|
96
|
+
},
|
|
97
|
+
] })),
|
|
98
|
+
};
|
|
99
|
+
return _jsx(DataListingView, { config: config });
|
|
100
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
|
+
import { Stack, Text } from "../../../ui";
|
|
5
|
+
import { apiClient } from "../../../http";
|
|
6
|
+
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
7
|
+
import { TesterFeedbackChart } from "./TesterFeedbackChart";
|
|
8
|
+
export function AdminTesterFeedbackReportView() {
|
|
9
|
+
const query = useQuery({
|
|
10
|
+
queryKey: ["admin", "tester-feedback", "report"],
|
|
11
|
+
queryFn: async () => {
|
|
12
|
+
const res = await apiClient.get(ADMIN_ENDPOINTS.TESTER_FEEDBACK_REPORT);
|
|
13
|
+
return res?.data ?? res;
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
if (query.isLoading)
|
|
17
|
+
return _jsx(Text, { color: "muted", children: "Loading report\u2026" });
|
|
18
|
+
const itemCoverage = query.data?.itemCoverage ?? [];
|
|
19
|
+
const totals = query.data?.totals ?? { totalAnswered: 0, totalYes: 0, totalNo: 0 };
|
|
20
|
+
const byGroup = new Map();
|
|
21
|
+
for (const item of itemCoverage) {
|
|
22
|
+
if (!byGroup.has(item.groupKey)) {
|
|
23
|
+
byGroup.set(item.groupKey, { groupLabel: item.groupKey, yes: 0, no: 0 });
|
|
24
|
+
}
|
|
25
|
+
const entry = byGroup.get(item.groupKey);
|
|
26
|
+
entry.yes += item.yesCount;
|
|
27
|
+
entry.no += item.noCount;
|
|
28
|
+
}
|
|
29
|
+
return (_jsx(Stack, { gap: "lg", children: _jsx(TesterFeedbackChart, { data: Array.from(byGroup.values()), totalCases: itemCoverage.length, totalAnswered: totals.totalAnswered, totalYes: totals.totalYes, totalNo: totals.totalNo }) }));
|
|
30
|
+
}
|