@mohasinac/appkit 4.40.1 → 4.41.1
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/features/categories/repository/categories.repository.js +2 -2
- package/dist/features/shell/SectionForm.js +44 -5
- package/dist/features/tester/seed-data/_money-flows.js +65 -49
- package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.js +6 -4
- package/dist/features/tester/seed-data/authored/admin__coupons.js +14 -13
- package/dist/features/tester/seed-data/authored/buying__bidding.js +154 -140
- package/dist/features/tester/seed-data/authored/buying__browsing-search.js +4 -4
- package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +59 -56
- package/dist/features/tester/seed-data/authored/buying__buying-coupons.js +18 -18
- package/dist/features/tester/seed-data/authored/buying__cart.js +105 -79
- package/dist/features/tester/seed-data/authored/buying__image-tile-layout.js +6 -8
- package/dist/features/tester/seed-data/authored/buying__my-orders.js +13 -13
- package/dist/features/tester/seed-data/authored/buying__offers.js +6 -6
- package/dist/features/tester/seed-data/authored/buying__order-detail-actions.js +8 -7
- package/dist/features/tester/seed-data/authored/buying__product-detail.js +62 -56
- package/dist/features/tester/seed-data/authored/buying__return-request.js +6 -6
- package/dist/features/tester/seed-data/authored/buying__reviews-pagination.js +4 -4
- package/dist/features/tester/seed-data/authored/buying__reviews.js +1 -1
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-extras.js +8 -8
- package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.js +1 -1
- package/dist/features/tester/seed-data/authored/content-discovery__coupons.js +29 -16
- package/dist/features/tester/seed-data/authored/content-discovery__event-detail-subroutes.js +9 -9
- package/dist/features/tester/seed-data/authored/content-discovery__events.js +69 -36
- package/dist/features/tester/seed-data/authored/content-discovery__notifications.js +1 -1
- package/dist/features/tester/seed-data/authored/cta-layout__checkout-bottom-bar.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__back-to-top-button.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__general-design.js +1 -1
- package/dist/features/tester/seed-data/authored/design-ux__status-badge-legibility.js +1 -1
- package/dist/features/tester/seed-data/authored/design-ux__sticky-cta-bar.js +3 -3
- package/dist/features/tester/seed-data/authored/index.js +0 -2
- package/dist/features/tester/seed-data/authored/page-wiring__data-loss.js +2 -2
- package/dist/features/tester/seed-data/authored/page-wiring__reachability.js +6 -4
- package/dist/features/tester/seed-data/authored/public-pages__auth-error-pages.js +2 -2
- package/dist/features/tester/seed-data/authored/public-pages__core-listing-pages.js +1 -1
- package/dist/features/tester/seed-data/authored/public-pages__stores-sellers-directories.js +8 -10
- package/dist/features/tester/seed-data/authored/selling__become-seller.js +2 -2
- package/dist/features/tester/seed-data/authored/selling__digital-content-delivery.js +2 -2
- package/dist/features/tester/seed-data/authored/selling__listing-a-product.js +2 -2
- package/dist/features/tester/seed-data/authored/selling__listing-edit-roundtrip.js +2 -2
- package/dist/features/tester/seed-data/authored/selling__listing-lifecycle.js +2 -2
- package/dist/features/tester/seed-data/authored/selling__seller-listing-types.js +2 -2
- package/dist/features/tester/seed-data/authored/seo__og-images.js +3 -4
- package/dist/features/tester/seed-data/authored/seo__page-metadata.js +6 -8
- package/dist/features/tester/seed-data/authored/seo__sitemap-and-robots.js +5 -5
- package/dist/features/tester/seed-data/index.d.ts +0 -10
- package/dist/features/tester/seed-data/index.js +0 -10
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +75 -89
- package/dist/features/tester/seed-data/tester-responses-seed-data.js +5 -5
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -21
- package/dist/seed/addresses-seed-data.js +0 -34
- package/dist/seed/index.d.ts +1 -1
- package/dist/seed/index.js +4 -3
- package/dist/seed/manifest.js +11 -12
- package/dist/seed/notifications-seed-data.js +4 -4
- package/dist/seed/stores-seed-data.js +7 -52
- package/dist/seed/users-seed-data.js +3 -83
- package/package.json +2 -2
- package/scripts/seed-cli.mjs +15 -20
- package/dist/_internal/server/features/classified/actions.d.ts +0 -12
- package/dist/_internal/server/features/classified/actions.js +0 -33
- package/dist/_internal/server/features/digital-code/actions.d.ts +0 -12
- package/dist/_internal/server/features/digital-code/actions.js +0 -33
- package/dist/_internal/server/features/homepage/data.d.ts +0 -18
- package/dist/_internal/server/features/homepage/data.js +0 -70
- package/dist/_internal/server/features/live/actions.d.ts +0 -6
- package/dist/_internal/server/features/live/actions.js +0 -27
- package/dist/_internal/server/features/messages/actions.d.ts +0 -6
- package/dist/_internal/server/features/messages/actions.js +0 -6
- package/dist/_internal/server/features/messages/data.d.ts +0 -10
- package/dist/_internal/server/features/messages/data.js +0 -24
- package/dist/_internal/server/features/messages/index.d.ts +0 -2
- package/dist/_internal/server/features/messages/index.js +0 -2
- package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +0 -12
- package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +0 -112
- package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +0 -2
- package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +0 -2
- package/dist/_internal/shared/features/homepage/section-gate.d.ts +0 -14
- package/dist/_internal/shared/features/homepage/section-gate.js +0 -51
- package/dist/_internal/shared/features/lottery/config-form.d.ts +0 -58
- package/dist/_internal/shared/features/lottery/config-form.js +0 -71
- package/dist/client/api/useFeatureFlags.d.ts +0 -22
- package/dist/client/api/useFeatureFlags.js +0 -27
- package/dist/features/account/address-validation.d.ts +0 -20
- package/dist/features/account/address-validation.js +0 -35
- package/dist/features/account/components/ChatList.d.ts +0 -14
- package/dist/features/account/components/ChatList.js +0 -9
- package/dist/features/account/components/ChatWindow.d.ts +0 -17
- package/dist/features/account/components/ChatWindow.js +0 -6
- package/dist/features/account/components/MessagesView.d.ts +0 -16
- package/dist/features/account/components/MessagesView.js +0 -5
- package/dist/features/account/components/UserOffersView.d.ts +0 -10
- package/dist/features/account/components/UserOffersView.js +0 -5
- package/dist/features/account/constants/addresses.d.ts +0 -13
- package/dist/features/account/constants/addresses.js +0 -43
- package/dist/features/account/hooks/useAddressForm.d.ts +0 -24
- package/dist/features/account/hooks/useAddressForm.js +0 -58
- package/dist/features/admin/actions/chat-actions.d.ts +0 -29
- package/dist/features/admin/actions/chat-actions.js +0 -101
- package/dist/features/admin/components/AdminFeatureFlagsView.d.ts +0 -9
- package/dist/features/admin/components/AdminFeatureFlagsView.js +0 -109
- package/dist/features/admin/components/AdminSiteView.d.ts +0 -10
- package/dist/features/admin/components/AdminSiteView.js +0 -52
- package/dist/features/admin/hooks/useChat.d.ts +0 -24
- package/dist/features/admin/hooks/useChat.js +0 -100
- package/dist/features/admin/permission-map.d.ts +0 -3
- package/dist/features/admin/permission-map.js +0 -26
- package/dist/features/admin/repository/chat.repository.d.ts +0 -50
- package/dist/features/admin/repository/chat.repository.js +0 -223
- package/dist/features/auth/actions/password-change-otp-actions.d.ts +0 -18
- package/dist/features/auth/actions/password-change-otp-actions.js +0 -88
- package/dist/features/auth/actions/realtime-token-actions.d.ts +0 -11
- package/dist/features/auth/actions/realtime-token-actions.js +0 -38
- package/dist/features/auth/password-change-otp.d.ts +0 -34
- package/dist/features/auth/password-change-otp.js +0 -56
- package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +0 -12
- package/dist/features/classified/components/ClassifiedContactSellerPanel.js +0 -49
- package/dist/features/events/api/route.d.ts +0 -12
- package/dist/features/events/api/route.js +0 -90
- package/dist/features/faq/api/route.d.ts +0 -11
- package/dist/features/faq/api/route.js +0 -52
- package/dist/features/faq/hooks/useFAQs.d.ts +0 -22
- package/dist/features/faq/hooks/useFAQs.js +0 -41
- package/dist/features/media/upload/video-poster.test.d.ts +0 -1
- package/dist/features/media/upload/video-poster.test.js +0 -42
- package/dist/features/messages/actions/index.d.ts +0 -2
- package/dist/features/messages/actions/index.js +0 -2
- package/dist/features/messages/actions/messages-actions.d.ts +0 -15
- package/dist/features/messages/actions/messages-actions.js +0 -33
- package/dist/features/messages/actions/ping-rtdb.d.ts +0 -2
- package/dist/features/messages/actions/ping-rtdb.js +0 -26
- package/dist/features/messages/hooks/useConversation.d.ts +0 -22
- package/dist/features/messages/hooks/useConversation.js +0 -155
- package/dist/features/messages/hooks/useConversations.d.ts +0 -15
- package/dist/features/messages/hooks/useConversations.js +0 -104
- package/dist/features/messages/index.d.ts +0 -6
- package/dist/features/messages/index.js +0 -4
- package/dist/features/messages/realtime.d.ts +0 -32
- package/dist/features/messages/realtime.js +0 -39
- package/dist/features/messages/repository/conversations.repository.d.ts +0 -71
- package/dist/features/messages/repository/conversations.repository.js +0 -284
- package/dist/features/messages/schemas/firestore.d.ts +0 -34
- package/dist/features/messages/schemas/firestore.js +0 -16
- package/dist/features/messages/schemas/index.d.ts +0 -177
- package/dist/features/messages/schemas/index.js +0 -35
- package/dist/features/messages/server.d.ts +0 -6
- package/dist/features/messages/server.js +0 -6
- package/dist/features/products/components/MakeOfferForm.d.ts +0 -20
- package/dist/features/products/components/MakeOfferForm.js +0 -9
- package/dist/features/reviews/api/route.d.ts +0 -21
- package/dist/features/reviews/api/route.js +0 -174
- package/dist/features/seller/api/offers/route.d.ts +0 -17
- package/dist/features/seller/api/offers/route.js +0 -63
- package/dist/features/seller/components/SellerAuctionsView.d.ts +0 -9
- package/dist/features/seller/components/SellerAuctionsView.js +0 -113
- package/dist/features/seller/components/SellerOffersPanel.d.ts +0 -21
- package/dist/features/seller/components/SellerOffersPanel.js +0 -160
- package/dist/features/seller/components/SellerPreOrdersView.d.ts +0 -6
- package/dist/features/seller/components/SellerPreOrdersView.js +0 -148
- package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +0 -6
- package/dist/features/seller/components/SellerPrizeDrawsView.js +0 -152
- package/dist/features/seller/permission-map.d.ts +0 -5
- package/dist/features/seller/permission-map.js +0 -17
- package/dist/features/shell/StepForm.d.ts +0 -80
- package/dist/features/shell/StepForm.js +0 -136
- package/dist/features/stores/api/route.d.ts +0 -12
- package/dist/features/stores/api/route.js +0 -110
- package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.js +0 -33
- package/dist/features/tester/seed-data/authored/account-auth__testing-program.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/account-auth__testing-program.js +0 -138
- package/dist/features/tester/seed-data/bids-tester-seed-data.d.ts +0 -2
- package/dist/features/tester/seed-data/bids-tester-seed-data.js +0 -37
- package/dist/features/tester/seed-data/blog-tester-seed-data.d.ts +0 -2
- package/dist/features/tester/seed-data/blog-tester-seed-data.js +0 -40
- package/dist/features/tester/seed-data/carts-tester-seed-data.d.ts +0 -3
- package/dist/features/tester/seed-data/carts-tester-seed-data.js +0 -70
- package/dist/features/tester/seed-data/categories-tester-seed-data.d.ts +0 -2
- package/dist/features/tester/seed-data/categories-tester-seed-data.js +0 -218
- package/dist/features/tester/seed-data/coupons-tester-seed-data.d.ts +0 -2
- package/dist/features/tester/seed-data/coupons-tester-seed-data.js +0 -76
- package/dist/features/tester/seed-data/events-tester-seed-data.d.ts +0 -2
- package/dist/features/tester/seed-data/events-tester-seed-data.js +0 -193
- package/dist/features/tester/seed-data/offers-tester-seed-data.d.ts +0 -2
- package/dist/features/tester/seed-data/offers-tester-seed-data.js +0 -191
- package/dist/features/tester/seed-data/orders-tester-seed-data.d.ts +0 -2
- package/dist/features/tester/seed-data/orders-tester-seed-data.js +0 -329
- package/dist/features/tester/seed-data/products-tester-seed-data.d.ts +0 -2
- package/dist/features/tester/seed-data/products-tester-seed-data.js +0 -815
- package/dist/features/tester/seed-data/stores-tester-seed-data.d.ts +0 -2
- package/dist/features/tester/seed-data/stores-tester-seed-data.js +0 -79
- package/dist/features/wishlist/components/WishlistToggleButton.d.ts +0 -12
- package/dist/features/wishlist/components/WishlistToggleButton.js +0 -36
- package/dist/monitoring/analytics.d.ts +0 -80
- package/dist/monitoring/analytics.js +0 -176
- package/dist/next/middleware/pii-redactor.d.ts +0 -32
- package/dist/next/middleware/pii-redactor.js +0 -84
- package/dist/providers/db-firebase/realtime.d.ts +0 -42
- package/dist/providers/db-firebase/realtime.js +0 -138
- package/dist/security/rbac/Can.d.ts +0 -31
- package/dist/security/rbac/Can.js +0 -22
- package/dist/security/rbac/default-roles.d.ts +0 -2
- package/dist/security/rbac/default-roles.js +0 -83
- package/dist/security/rbac/hook.d.ts +0 -20
- package/dist/security/rbac/hook.js +0 -30
- package/dist/security/rbac/index.d.ts +0 -8
- package/dist/security/rbac/index.js +0 -6
- package/dist/security/rbac/middleware.d.ts +0 -51
- package/dist/security/rbac/middleware.js +0 -51
- package/dist/security/rbac/resolver.d.ts +0 -5
- package/dist/security/rbac/resolver.js +0 -38
- package/dist/security/rbac/server.d.ts +0 -24
- package/dist/security/rbac/server.js +0 -42
- package/dist/security/rbac/types.d.ts +0 -22
- package/dist/security/rbac/types.js +0 -2
- package/dist/seed/conversations-seed-data.d.ts +0 -2
- package/dist/seed/conversations-seed-data.js +0 -292
- package/dist/utils/filter.helper.d.ts +0 -18
- package/dist/utils/filter.helper.js +0 -21
- package/dist/utils/pagination.helper.d.ts +0 -23
- package/dist/utils/pagination.helper.js +0 -24
- package/dist/utils/search-tokens.d.ts +0 -2
- package/dist/utils/search-tokens.js +0 -21
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// [SCHEMA] messages / conversations feature — Firestore document schema.
|
|
2
|
-
//
|
|
3
|
-
// Mirrors ConversationDocument + ConversationMessage in ./firestore.ts.
|
|
4
|
-
// Registered into SCHEMAS.firestore.conversations.
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
import { auditTimestampsShape, firestoreDateSchema } from "../../../schemas/firestore-helpers";
|
|
7
|
-
export * from "./firestore";
|
|
8
|
-
export const conversationMessageSenderRoleSchema = z.enum(["buyer", "seller"]);
|
|
9
|
-
export const conversationStatusSchema = z.enum(["active", "archived", "blocked"]);
|
|
10
|
-
export const conversationMessageSchema = z.object({
|
|
11
|
-
id: z.string(),
|
|
12
|
-
senderId: z.string(),
|
|
13
|
-
senderRole: conversationMessageSenderRoleSchema,
|
|
14
|
-
body: z.string(),
|
|
15
|
-
isRead: z.boolean(),
|
|
16
|
-
sentAt: firestoreDateSchema,
|
|
17
|
-
attachments: z.array(z.string()).optional(),
|
|
18
|
-
});
|
|
19
|
-
export const conversationFirestoreSchema = z.object({
|
|
20
|
-
id: z.string(),
|
|
21
|
-
buyerId: z.string(),
|
|
22
|
-
buyerDisplayName: z.string(),
|
|
23
|
-
storeId: z.string(),
|
|
24
|
-
storeName: z.string(),
|
|
25
|
-
sellerDisplayName: z.string(),
|
|
26
|
-
productId: z.string().optional(),
|
|
27
|
-
productTitle: z.string().optional(),
|
|
28
|
-
messages: z.array(conversationMessageSchema),
|
|
29
|
-
lastMessage: z.string(),
|
|
30
|
-
lastMessageAt: firestoreDateSchema,
|
|
31
|
-
unreadBuyer: z.number().int().nonnegative(),
|
|
32
|
-
unreadSeller: z.number().int().nonnegative(),
|
|
33
|
-
status: conversationStatusSchema,
|
|
34
|
-
...auditTimestampsShape,
|
|
35
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
export interface MakeOfferFormProps {
|
|
3
|
-
listedPrice?: number;
|
|
4
|
-
isLoading?: boolean;
|
|
5
|
-
isSubmitted?: boolean;
|
|
6
|
-
/** Render the offer amount input */
|
|
7
|
-
renderInput?: () => React.ReactNode;
|
|
8
|
-
/** Render the create / submit action button */
|
|
9
|
-
renderAction?: () => React.ReactNode;
|
|
10
|
-
/** Render terms acceptance checkbox or note */
|
|
11
|
-
renderTerms?: () => React.ReactNode;
|
|
12
|
-
/** Render success confirmation after offer is placed */
|
|
13
|
-
renderSuccess?: () => React.ReactNode;
|
|
14
|
-
/** Render auth gate when user is not authenticated */
|
|
15
|
-
renderAuthGate?: () => React.ReactNode;
|
|
16
|
-
/** Render validation error message */
|
|
17
|
-
renderError?: () => React.ReactNode;
|
|
18
|
-
className?: string;
|
|
19
|
-
}
|
|
20
|
-
export declare function MakeOfferForm({ isSubmitted, renderInput, renderAction, renderTerms, renderSuccess, renderAuthGate, renderError, className, }: MakeOfferFormProps): React.JSX.Element;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Stack } from "@mohasinac/appkit/client";
|
|
3
|
-
export function MakeOfferForm({ isSubmitted = false, renderInput, renderAction, renderTerms, renderSuccess, renderAuthGate, renderError, className = "", }) {
|
|
4
|
-
if (renderAuthGate)
|
|
5
|
-
return _jsx(_Fragment, { children: renderAuthGate() });
|
|
6
|
-
if (isSubmitted && renderSuccess)
|
|
7
|
-
return _jsx(_Fragment, { children: renderSuccess() });
|
|
8
|
-
return (_jsxs(Stack, { className: `${className}`, gap: "md", children: [renderError?.(), renderInput?.(), renderTerms?.(), renderAction?.()] }));
|
|
9
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* feat-reviews — Next.js App Router API handler (GET/POST /api/reviews)
|
|
3
|
-
*
|
|
4
|
-
* 2-line stub for GET:
|
|
5
|
-
* ```ts
|
|
6
|
-
* // app/api/reviews/route.ts
|
|
7
|
-
* export { GET, POST } from "@mohasinac/feat-reviews";
|
|
8
|
-
* ```
|
|
9
|
-
*
|
|
10
|
-
* Query modes for GET:
|
|
11
|
-
* ?featured=true → returns flat Review[] (for testimonial sections)
|
|
12
|
-
* ?latest=true → paginated approved reviews, no productId required
|
|
13
|
-
* ?productId=<id> → paginated + aggregate stats (averageRating, ratingDistribution)
|
|
14
|
-
*
|
|
15
|
-
* POST creates a new review; requires authentication.
|
|
16
|
-
*/
|
|
17
|
-
import { NextResponse } from "next/server.js";
|
|
18
|
-
export declare function GET(request: Request): Promise<NextResponse>;
|
|
19
|
-
export declare const POST: (request: Request, context?: {
|
|
20
|
-
params: Promise<Record<string, string>>;
|
|
21
|
-
} | undefined) => Promise<Response>;
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* feat-reviews — Next.js App Router API handler (GET/POST /api/reviews)
|
|
3
|
-
*
|
|
4
|
-
* 2-line stub for GET:
|
|
5
|
-
* ```ts
|
|
6
|
-
* // app/api/reviews/route.ts
|
|
7
|
-
* export { GET, POST } from "@mohasinac/feat-reviews";
|
|
8
|
-
* ```
|
|
9
|
-
*
|
|
10
|
-
* Query modes for GET:
|
|
11
|
-
* ?featured=true → returns flat Review[] (for testimonial sections)
|
|
12
|
-
* ?latest=true → paginated approved reviews, no productId required
|
|
13
|
-
* ?productId=<id> → paginated + aggregate stats (averageRating, ratingDistribution)
|
|
14
|
-
*
|
|
15
|
-
* POST creates a new review; requires authentication.
|
|
16
|
-
*/
|
|
17
|
-
import { NextResponse } from "next/server.js";
|
|
18
|
-
import { z } from "zod";
|
|
19
|
-
import { mediaUrlSchema } from "../../../validation/schemas";
|
|
20
|
-
import { getProviders } from "../../../contracts";
|
|
21
|
-
import { createRouteHandler } from "../../../next";
|
|
22
|
-
import { ReviewStatusValues } from "../schemas";
|
|
23
|
-
import { normalizeError } from "../../../errors/normalize";
|
|
24
|
-
function param(url, key) {
|
|
25
|
-
return url.searchParams.get(key);
|
|
26
|
-
}
|
|
27
|
-
function numParam(url, key, fallback) {
|
|
28
|
-
const v = url.searchParams.get(key);
|
|
29
|
-
const n = v !== null ? Number(v) : NaN;
|
|
30
|
-
return Number.isFinite(n) ? n : fallback;
|
|
31
|
-
}
|
|
32
|
-
function computeAggregates(reviews) {
|
|
33
|
-
const dist = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 };
|
|
34
|
-
let total = 0;
|
|
35
|
-
let count = 0;
|
|
36
|
-
for (const r of reviews) {
|
|
37
|
-
if (r.status === ReviewStatusValues.APPROVED) {
|
|
38
|
-
dist[r.rating] = (dist[r.rating] ?? 0) + 1;
|
|
39
|
-
total += r.rating;
|
|
40
|
-
count++;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
averageRating: count > 0 ? total / count : 0,
|
|
45
|
-
ratingDistribution: dist,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
export async function GET(request) {
|
|
49
|
-
try {
|
|
50
|
-
const url = new URL(request.url);
|
|
51
|
-
const featured = param(url, "featured") === "true";
|
|
52
|
-
const latest = param(url, "latest") === "true";
|
|
53
|
-
const page = numParam(url, "page", 1);
|
|
54
|
-
const pageSize = numParam(url, "pageSize", 10);
|
|
55
|
-
const sort = param(url, "sorts") ?? param(url, "sort") ?? "-createdAt";
|
|
56
|
-
const { db } = getProviders();
|
|
57
|
-
if (!db) {
|
|
58
|
-
return NextResponse.json({ success: false, error: "Database provider not registered" }, { status: 503 });
|
|
59
|
-
}
|
|
60
|
-
const repo = db.getRepository("reviews");
|
|
61
|
-
// -- ?featured=true --------------------------------------------------------
|
|
62
|
-
// Returns flat Review[] for homepage testimonial sections.
|
|
63
|
-
if (featured) {
|
|
64
|
-
const result = await repo.findAll({
|
|
65
|
-
filters: "featured==true,status==approved",
|
|
66
|
-
sort,
|
|
67
|
-
perPage: Math.min(pageSize, 50),
|
|
68
|
-
});
|
|
69
|
-
const response = NextResponse.json({ success: true, data: result.data });
|
|
70
|
-
response.headers.set("Cache-Control", "public, max-age=300, s-maxage=600, stale-while-revalidate=120");
|
|
71
|
-
return response;
|
|
72
|
-
}
|
|
73
|
-
// -- ?latest=true ----------------------------------------------------------
|
|
74
|
-
// Paginated latest approved reviews, no productId required.
|
|
75
|
-
if (latest) {
|
|
76
|
-
const latestPageSize = numParam(url, "pageSize", 24);
|
|
77
|
-
const result = await repo.findAll({
|
|
78
|
-
filters: "status==approved",
|
|
79
|
-
sort,
|
|
80
|
-
page,
|
|
81
|
-
perPage: latestPageSize,
|
|
82
|
-
});
|
|
83
|
-
const totalPages = Math.max(1, Math.ceil(result.total / latestPageSize));
|
|
84
|
-
const body = {
|
|
85
|
-
items: result.data,
|
|
86
|
-
total: result.total,
|
|
87
|
-
page: result.page,
|
|
88
|
-
pageSize: latestPageSize,
|
|
89
|
-
totalPages,
|
|
90
|
-
hasMore: result.page < totalPages,
|
|
91
|
-
};
|
|
92
|
-
const response = NextResponse.json({ success: true, data: body });
|
|
93
|
-
response.headers.set("Cache-Control", "public, max-age=120, s-maxage=300, stale-while-revalidate=60");
|
|
94
|
-
return response;
|
|
95
|
-
}
|
|
96
|
-
// -- ?productId=<id> -------------------------------------------------------
|
|
97
|
-
// Paginated reviews for a product with aggregate rating stats.
|
|
98
|
-
const productId = param(url, "productId");
|
|
99
|
-
if (!productId) {
|
|
100
|
-
return NextResponse.json({ success: false, error: "productId query parameter is required" }, { status: 400 });
|
|
101
|
-
}
|
|
102
|
-
// Fetch all approved reviews for aggregate computation (no pagination)
|
|
103
|
-
const allResult = await repo.findAll({
|
|
104
|
-
filters: `productId==${productId},status==approved`,
|
|
105
|
-
perPage: 1000,
|
|
106
|
-
});
|
|
107
|
-
const { averageRating, ratingDistribution } = computeAggregates(allResult.data);
|
|
108
|
-
// Fetch the current page for display
|
|
109
|
-
const pagedResult = await repo.findAll({
|
|
110
|
-
filters: `productId==${productId},status==approved`,
|
|
111
|
-
sort,
|
|
112
|
-
page,
|
|
113
|
-
perPage: pageSize,
|
|
114
|
-
});
|
|
115
|
-
const totalPages = Math.max(1, Math.ceil(pagedResult.total / pageSize));
|
|
116
|
-
const body = {
|
|
117
|
-
items: pagedResult.data,
|
|
118
|
-
total: pagedResult.total,
|
|
119
|
-
page: pagedResult.page,
|
|
120
|
-
pageSize,
|
|
121
|
-
totalPages,
|
|
122
|
-
hasMore: pagedResult.page < totalPages,
|
|
123
|
-
averageRating,
|
|
124
|
-
ratingDistribution,
|
|
125
|
-
};
|
|
126
|
-
const response = NextResponse.json({ success: true, data: body });
|
|
127
|
-
response.headers.set("Cache-Control", "public, max-age=120, s-maxage=300, stale-while-revalidate=60");
|
|
128
|
-
return response;
|
|
129
|
-
}
|
|
130
|
-
catch (error) {
|
|
131
|
-
void normalizeError(error);
|
|
132
|
-
console.error("[feat-reviews] GET /api/reviews failed", error);
|
|
133
|
-
return NextResponse.json({ success: false, error: "Failed to fetch reviews" }, { status: 500 });
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
|
-
// POST /api/reviews — create a new review (authenticated)
|
|
138
|
-
// ---------------------------------------------------------------------------
|
|
139
|
-
const reviewCreateSchema = z
|
|
140
|
-
.object({
|
|
141
|
-
productId: z.string().min(1),
|
|
142
|
-
rating: z.union([
|
|
143
|
-
z.literal(1),
|
|
144
|
-
z.literal(2),
|
|
145
|
-
z.literal(3),
|
|
146
|
-
z.literal(4),
|
|
147
|
-
z.literal(5),
|
|
148
|
-
]),
|
|
149
|
-
title: z.string().min(1).max(100).optional(),
|
|
150
|
-
comment: z.string().min(1).max(2000).optional(),
|
|
151
|
-
images: z.array(mediaUrlSchema).max(10).optional(),
|
|
152
|
-
video: mediaUrlSchema.optional(),
|
|
153
|
-
})
|
|
154
|
-
.passthrough();
|
|
155
|
-
export const POST = createRouteHandler({
|
|
156
|
-
auth: true,
|
|
157
|
-
schema: reviewCreateSchema,
|
|
158
|
-
handler: async ({ user, body }) => {
|
|
159
|
-
const { db } = getProviders();
|
|
160
|
-
if (!db) {
|
|
161
|
-
return NextResponse.json({ success: false, error: "DB not configured" }, { status: 503 });
|
|
162
|
-
}
|
|
163
|
-
const repo = db.getRepository("reviews");
|
|
164
|
-
const now = new Date().toISOString();
|
|
165
|
-
const created = await repo.create({
|
|
166
|
-
...body,
|
|
167
|
-
userId: user.uid,
|
|
168
|
-
status: "pending",
|
|
169
|
-
createdAt: now,
|
|
170
|
-
updatedAt: now,
|
|
171
|
-
});
|
|
172
|
-
return NextResponse.json({ success: true, data: created }, { status: 201 });
|
|
173
|
-
},
|
|
174
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* feat-seller — GET /api/seller/offers
|
|
3
|
-
*
|
|
4
|
-
* Returns all incoming offers for the authenticated seller.
|
|
5
|
-
* Queries the `offers` collection filtered by `sellerId == uid`.
|
|
6
|
-
*
|
|
7
|
-
* Response: `{ success: true, data: { offers: OfferDocument[], total: number } }`
|
|
8
|
-
*
|
|
9
|
-
* Consumer stub:
|
|
10
|
-
* ```ts
|
|
11
|
-
* // src/app/api/seller/offers/route.ts
|
|
12
|
-
* export { sellerOffersGET as GET } from "@mohasinac/feat-seller";
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
export declare const GET: (request: Request, context?: {
|
|
16
|
-
params: Promise<Record<string, string>>;
|
|
17
|
-
} | undefined) => Promise<Response>;
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* feat-seller — GET /api/seller/offers
|
|
3
|
-
*
|
|
4
|
-
* Returns all incoming offers for the authenticated seller.
|
|
5
|
-
* Queries the `offers` collection filtered by `sellerId == uid`.
|
|
6
|
-
*
|
|
7
|
-
* Response: `{ success: true, data: { offers: OfferDocument[], total: number } }`
|
|
8
|
-
*
|
|
9
|
-
* Consumer stub:
|
|
10
|
-
* ```ts
|
|
11
|
-
* // src/app/api/seller/offers/route.ts
|
|
12
|
-
* export { sellerOffersGET as GET } from "@mohasinac/feat-seller";
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
import { NextResponse } from "next/server.js";
|
|
16
|
-
import { getProviders } from "../../../../contracts";
|
|
17
|
-
import { createRouteHandler } from "../../../../next";
|
|
18
|
-
function numParam(url, key, fallback) {
|
|
19
|
-
const v = url.searchParams.get(key);
|
|
20
|
-
const n = v !== null ? Number(v) : NaN;
|
|
21
|
-
return Number.isFinite(n) ? n : fallback;
|
|
22
|
-
}
|
|
23
|
-
export const GET = createRouteHandler({
|
|
24
|
-
roles: ["seller", "admin", "moderator"],
|
|
25
|
-
handler: async ({ request, user }) => {
|
|
26
|
-
const url = new URL(request.url);
|
|
27
|
-
const page = Math.max(1, numParam(url, "page", 1));
|
|
28
|
-
const pageSize = Math.min(100, Math.max(1, numParam(url, "pageSize", 50)));
|
|
29
|
-
const sort = url.searchParams.get("sorts") ??
|
|
30
|
-
url.searchParams.get("sort") ??
|
|
31
|
-
"-createdAt";
|
|
32
|
-
// Optional status filter passed by client
|
|
33
|
-
const parts = [`sellerId==${user.uid}`];
|
|
34
|
-
const status = url.searchParams.get("status");
|
|
35
|
-
if (status && status !== "all")
|
|
36
|
-
parts.push(`status==${status}`);
|
|
37
|
-
const extraFilters = url.searchParams.get("filters");
|
|
38
|
-
if (extraFilters)
|
|
39
|
-
parts.push(extraFilters);
|
|
40
|
-
const { db } = getProviders();
|
|
41
|
-
if (!db)
|
|
42
|
-
return NextResponse.json({ success: false, error: "DB not configured" }, { status: 503 });
|
|
43
|
-
const repo = db.getRepository("offers");
|
|
44
|
-
const result = await repo.findAll({
|
|
45
|
-
filters: parts.join(","),
|
|
46
|
-
sort,
|
|
47
|
-
page,
|
|
48
|
-
perPage: pageSize,
|
|
49
|
-
});
|
|
50
|
-
const totalPages = Math.max(1, Math.ceil(result.total / pageSize));
|
|
51
|
-
return NextResponse.json({
|
|
52
|
-
success: true,
|
|
53
|
-
data: {
|
|
54
|
-
items: result.data,
|
|
55
|
-
total: result.total,
|
|
56
|
-
page: result.page,
|
|
57
|
-
pageSize,
|
|
58
|
-
totalPages,
|
|
59
|
-
hasMore: result.page < totalPages,
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
},
|
|
63
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { ListingLayoutProps } from "../../../ui";
|
|
3
|
-
export interface SellerAuctionsViewProps extends ListingLayoutProps {
|
|
4
|
-
renderHeader?: (onAdd: () => void) => React.ReactNode;
|
|
5
|
-
onEditClick?: (id: string) => void;
|
|
6
|
-
onDelete?: (id: string) => Promise<void>;
|
|
7
|
-
onBulkDelete?: (ids: string[]) => Promise<void>;
|
|
8
|
-
}
|
|
9
|
-
export declare function SellerAuctionsView({ children, onEditClick, onDelete, onBulkDelete, ...props }: SellerAuctionsViewProps): React.JSX.Element;
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit/client";
|
|
4
|
-
import { sortBy } from "@mohasinac/appkit/client";
|
|
5
|
-
import React, { useState, useCallback } from "react";
|
|
6
|
-
import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
|
|
7
|
-
import { ConfirmDeleteModal, FilterChipGroup, ListingLayout, RowActionMenu } from "../../../ui";
|
|
8
|
-
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
|
-
import { SELLER_AUCTION_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
10
|
-
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
|
-
import { ROUTES } from "../../../constants";
|
|
12
|
-
import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../../admin/hooks/useAdminListingData";
|
|
13
|
-
import { DataListingView } from "../../admin/components/DataListingView";
|
|
14
|
-
import { SELLER_BULK_ACTIONS, ROW_ACTION_META } from "../../products/constants/action-defs";
|
|
15
|
-
import { useAvailabilityScope } from "../../products/hooks/useAvailabilityScope";
|
|
16
|
-
const AUCTION_TYPES = ["auction"];
|
|
17
|
-
export function SellerAuctionsView({ children, onEditClick, onDelete, onBulkDelete, ...props }) {
|
|
18
|
-
// Independent of DataListingView's own internal useUrlTable() — both read
|
|
19
|
-
// and write the same URL, useUrlTable has no local state, so they stay in
|
|
20
|
-
// sync (same pattern SellerProductsView already uses for its "Show sold"
|
|
21
|
-
// toggle).
|
|
22
|
-
const scope = useAvailabilityScope(AUCTION_TYPES);
|
|
23
|
-
const [deleteTargetId, setDeleteTargetId] = useState(null);
|
|
24
|
-
const { deletingId, handleDelete: performDelete } = useEntityDelete({
|
|
25
|
-
endpoint: SELLER_ENDPOINTS.PRODUCT_BY_ID,
|
|
26
|
-
deleteFn: onDelete,
|
|
27
|
-
successMessage: "Auction deleted.",
|
|
28
|
-
fetchOptions: { credentials: "include" },
|
|
29
|
-
});
|
|
30
|
-
const handleDelete = useCallback(async (id) => {
|
|
31
|
-
await performDelete(id);
|
|
32
|
-
setDeleteTargetId(null);
|
|
33
|
-
}, [performDelete]);
|
|
34
|
-
const handleEdit = useCallback((id) => {
|
|
35
|
-
if (onEditClick)
|
|
36
|
-
onEditClick(id);
|
|
37
|
-
else
|
|
38
|
-
window.location.href = String(ROUTES.STORE.PRODUCTS_EDIT(id));
|
|
39
|
-
}, [onEditClick]);
|
|
40
|
-
if (React.Children.count(children) > 0) {
|
|
41
|
-
return (_jsx(ListingLayout, { portal: "seller", ...props, children: children }));
|
|
42
|
-
}
|
|
43
|
-
const config = {
|
|
44
|
-
portal: "seller",
|
|
45
|
-
title: "Auctions",
|
|
46
|
-
searchPlaceholder: "Search auctions by product name",
|
|
47
|
-
emptyLabel: "No auctions found",
|
|
48
|
-
filterKeys: ["status"],
|
|
49
|
-
defaultSort: sortBy("auctionEndDate", "ASC"),
|
|
50
|
-
queryKey: ["seller", "auctions", "listing"],
|
|
51
|
-
endpoint: SELLER_ENDPOINTS.AUCTIONS,
|
|
52
|
-
sortOptions: [
|
|
53
|
-
{ value: "auctionEndDate", label: "Ending soon" },
|
|
54
|
-
{ value: sortBy("auctionEndDate", "DESC"), label: "Ending latest" },
|
|
55
|
-
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
56
|
-
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
57
|
-
],
|
|
58
|
-
mapRows: (response) => toRecordArray(response.auctions).map((item, index) => ({
|
|
59
|
-
id: toStringValue(item.id, `auction-${index}`),
|
|
60
|
-
primary: toStringValue(item.productTitle ?? item.title, "Untitled auction"),
|
|
61
|
-
secondary: [
|
|
62
|
-
`Reserve: ${toCurrency(item.reservePrice)}`,
|
|
63
|
-
`Bids: ${item.bidCount ?? 0}`,
|
|
64
|
-
].join(" · "),
|
|
65
|
-
status: toStringValue(item.status, "Unknown"),
|
|
66
|
-
updatedAt: toRelativeDate(item.endsAt ?? item.updatedAt ?? item.createdAt),
|
|
67
|
-
image: toStringValue(item.mainImage, "") || undefined,
|
|
68
|
-
})),
|
|
69
|
-
getTotal: (response, mappedRows) => typeof response.meta?.total === "number"
|
|
70
|
-
? response.meta.total
|
|
71
|
-
: mappedRows.length,
|
|
72
|
-
buildFilters: (state) => {
|
|
73
|
-
// Previously missing entirely — the seller Auctions tab silently
|
|
74
|
-
// listed the entire seller catalog, not just auctions.
|
|
75
|
-
//
|
|
76
|
-
// The hide-ended clause that used to live here is gone: it hard-coded
|
|
77
|
-
// `auctionEndDate >= now`, which is an INEQUALITY pushed straight into
|
|
78
|
-
// Firestore alongside whichever sort the toolbar happened to be on —
|
|
79
|
-
// the FAILED_PRECONDITION shape of Root Cause #59. The scope param now
|
|
80
|
-
// carries it, and `listPublicProducts` decides whether it can be pushed
|
|
81
|
-
// down or has to be applied over a bounded fetch.
|
|
82
|
-
const parts = ["listingType==auction"];
|
|
83
|
-
if (state.status && state.status !== "All")
|
|
84
|
-
parts.push(sieveFilter("status", SIEVE_OP.EQ, state.status));
|
|
85
|
-
return parts.join(",");
|
|
86
|
-
},
|
|
87
|
-
buildExtraParams: () => scope.extraParams,
|
|
88
|
-
renderAboveContent: scope.renderAboveContent,
|
|
89
|
-
// Rule #7: bulk-action array sourced from the SELLER_BULK_ACTIONS preset.
|
|
90
|
-
buildBulkActions: onBulkDelete
|
|
91
|
-
? (selection) => SELLER_BULK_ACTIONS.auctions.map((id) => ({
|
|
92
|
-
id,
|
|
93
|
-
label: ROW_ACTION_META[id].label,
|
|
94
|
-
destructive: ROW_ACTION_META[id].destructive,
|
|
95
|
-
onClick: async () => {
|
|
96
|
-
await onBulkDelete(selection.selectedIds);
|
|
97
|
-
selection.clearSelection();
|
|
98
|
-
},
|
|
99
|
-
}))
|
|
100
|
-
: undefined,
|
|
101
|
-
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
102
|
-
{ label: ACTIONS.STORE["edit-listing"].label, onClick: () => handleEdit(row.id) },
|
|
103
|
-
{
|
|
104
|
-
label: ACTIONS.STORE["delete-listing"].label,
|
|
105
|
-
destructive: true,
|
|
106
|
-
onClick: () => setDeleteTargetId(row.id),
|
|
107
|
-
disabled: deletingId === row.id,
|
|
108
|
-
},
|
|
109
|
-
] })),
|
|
110
|
-
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: SELLER_AUCTION_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) })),
|
|
111
|
-
};
|
|
112
|
-
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Auction", message: "Are you sure you want to delete this auction? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
|
|
113
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
export type SellerOfferAction = {
|
|
3
|
-
action: "accept";
|
|
4
|
-
offerId: string;
|
|
5
|
-
sellerNote?: string;
|
|
6
|
-
} | {
|
|
7
|
-
action: "decline";
|
|
8
|
-
offerId: string;
|
|
9
|
-
sellerNote?: string;
|
|
10
|
-
} | {
|
|
11
|
-
action: "counter";
|
|
12
|
-
offerId: string;
|
|
13
|
-
counterAmount: number;
|
|
14
|
-
sellerNote?: string;
|
|
15
|
-
};
|
|
16
|
-
export interface SellerOffersPanelProps {
|
|
17
|
-
fetchEndpoint?: string;
|
|
18
|
-
onRespond: (input: SellerOfferAction) => Promise<void>;
|
|
19
|
-
className?: string;
|
|
20
|
-
}
|
|
21
|
-
export declare function SellerOffersPanel({ fetchEndpoint, onRespond, className, }: SellerOffersPanelProps): React.JSX.Element;
|