@mohasinac/appkit 4.41.0 → 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.
Files changed (166) hide show
  1. package/dist/features/shell/SectionForm.js +44 -5
  2. package/package.json +2 -2
  3. package/dist/_internal/server/features/classified/actions.d.ts +0 -12
  4. package/dist/_internal/server/features/classified/actions.js +0 -33
  5. package/dist/_internal/server/features/digital-code/actions.d.ts +0 -12
  6. package/dist/_internal/server/features/digital-code/actions.js +0 -33
  7. package/dist/_internal/server/features/homepage/data.d.ts +0 -18
  8. package/dist/_internal/server/features/homepage/data.js +0 -70
  9. package/dist/_internal/server/features/live/actions.d.ts +0 -6
  10. package/dist/_internal/server/features/live/actions.js +0 -27
  11. package/dist/_internal/server/features/messages/actions.d.ts +0 -6
  12. package/dist/_internal/server/features/messages/actions.js +0 -6
  13. package/dist/_internal/server/features/messages/data.d.ts +0 -10
  14. package/dist/_internal/server/features/messages/data.js +0 -24
  15. package/dist/_internal/server/features/messages/index.d.ts +0 -2
  16. package/dist/_internal/server/features/messages/index.js +0 -2
  17. package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +0 -12
  18. package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +0 -112
  19. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +0 -2
  20. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +0 -2
  21. package/dist/_internal/shared/features/homepage/section-gate.d.ts +0 -14
  22. package/dist/_internal/shared/features/homepage/section-gate.js +0 -51
  23. package/dist/_internal/shared/features/lottery/config-form.d.ts +0 -58
  24. package/dist/_internal/shared/features/lottery/config-form.js +0 -71
  25. package/dist/client/api/useFeatureFlags.d.ts +0 -22
  26. package/dist/client/api/useFeatureFlags.js +0 -27
  27. package/dist/features/account/address-validation.d.ts +0 -20
  28. package/dist/features/account/address-validation.js +0 -35
  29. package/dist/features/account/components/ChatList.d.ts +0 -14
  30. package/dist/features/account/components/ChatList.js +0 -9
  31. package/dist/features/account/components/ChatWindow.d.ts +0 -17
  32. package/dist/features/account/components/ChatWindow.js +0 -6
  33. package/dist/features/account/components/MessagesView.d.ts +0 -16
  34. package/dist/features/account/components/MessagesView.js +0 -5
  35. package/dist/features/account/components/UserOffersView.d.ts +0 -10
  36. package/dist/features/account/components/UserOffersView.js +0 -5
  37. package/dist/features/account/constants/addresses.d.ts +0 -13
  38. package/dist/features/account/constants/addresses.js +0 -43
  39. package/dist/features/account/hooks/useAddressForm.d.ts +0 -24
  40. package/dist/features/account/hooks/useAddressForm.js +0 -58
  41. package/dist/features/admin/actions/chat-actions.d.ts +0 -29
  42. package/dist/features/admin/actions/chat-actions.js +0 -101
  43. package/dist/features/admin/components/AdminFeatureFlagsView.d.ts +0 -9
  44. package/dist/features/admin/components/AdminFeatureFlagsView.js +0 -109
  45. package/dist/features/admin/components/AdminSiteView.d.ts +0 -10
  46. package/dist/features/admin/components/AdminSiteView.js +0 -52
  47. package/dist/features/admin/hooks/useChat.d.ts +0 -24
  48. package/dist/features/admin/hooks/useChat.js +0 -100
  49. package/dist/features/admin/permission-map.d.ts +0 -3
  50. package/dist/features/admin/permission-map.js +0 -26
  51. package/dist/features/admin/repository/chat.repository.d.ts +0 -50
  52. package/dist/features/admin/repository/chat.repository.js +0 -223
  53. package/dist/features/auth/actions/password-change-otp-actions.d.ts +0 -18
  54. package/dist/features/auth/actions/password-change-otp-actions.js +0 -88
  55. package/dist/features/auth/actions/realtime-token-actions.d.ts +0 -11
  56. package/dist/features/auth/actions/realtime-token-actions.js +0 -38
  57. package/dist/features/auth/password-change-otp.d.ts +0 -34
  58. package/dist/features/auth/password-change-otp.js +0 -56
  59. package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +0 -12
  60. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +0 -49
  61. package/dist/features/events/api/route.d.ts +0 -12
  62. package/dist/features/events/api/route.js +0 -90
  63. package/dist/features/faq/api/route.d.ts +0 -11
  64. package/dist/features/faq/api/route.js +0 -52
  65. package/dist/features/faq/hooks/useFAQs.d.ts +0 -22
  66. package/dist/features/faq/hooks/useFAQs.js +0 -41
  67. package/dist/features/media/upload/video-poster.test.d.ts +0 -1
  68. package/dist/features/media/upload/video-poster.test.js +0 -42
  69. package/dist/features/messages/actions/index.d.ts +0 -2
  70. package/dist/features/messages/actions/index.js +0 -2
  71. package/dist/features/messages/actions/messages-actions.d.ts +0 -15
  72. package/dist/features/messages/actions/messages-actions.js +0 -33
  73. package/dist/features/messages/actions/ping-rtdb.d.ts +0 -2
  74. package/dist/features/messages/actions/ping-rtdb.js +0 -26
  75. package/dist/features/messages/hooks/useConversation.d.ts +0 -22
  76. package/dist/features/messages/hooks/useConversation.js +0 -155
  77. package/dist/features/messages/hooks/useConversations.d.ts +0 -15
  78. package/dist/features/messages/hooks/useConversations.js +0 -104
  79. package/dist/features/messages/index.d.ts +0 -6
  80. package/dist/features/messages/index.js +0 -4
  81. package/dist/features/messages/realtime.d.ts +0 -32
  82. package/dist/features/messages/realtime.js +0 -39
  83. package/dist/features/messages/repository/conversations.repository.d.ts +0 -71
  84. package/dist/features/messages/repository/conversations.repository.js +0 -284
  85. package/dist/features/messages/schemas/firestore.d.ts +0 -34
  86. package/dist/features/messages/schemas/firestore.js +0 -16
  87. package/dist/features/messages/schemas/index.d.ts +0 -177
  88. package/dist/features/messages/schemas/index.js +0 -35
  89. package/dist/features/messages/server.d.ts +0 -6
  90. package/dist/features/messages/server.js +0 -6
  91. package/dist/features/products/components/MakeOfferForm.d.ts +0 -20
  92. package/dist/features/products/components/MakeOfferForm.js +0 -9
  93. package/dist/features/reviews/api/route.d.ts +0 -21
  94. package/dist/features/reviews/api/route.js +0 -174
  95. package/dist/features/seller/api/offers/route.d.ts +0 -17
  96. package/dist/features/seller/api/offers/route.js +0 -63
  97. package/dist/features/seller/components/SellerAuctionsView.d.ts +0 -9
  98. package/dist/features/seller/components/SellerAuctionsView.js +0 -113
  99. package/dist/features/seller/components/SellerOffersPanel.d.ts +0 -21
  100. package/dist/features/seller/components/SellerOffersPanel.js +0 -160
  101. package/dist/features/seller/components/SellerPreOrdersView.d.ts +0 -6
  102. package/dist/features/seller/components/SellerPreOrdersView.js +0 -148
  103. package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +0 -6
  104. package/dist/features/seller/components/SellerPrizeDrawsView.js +0 -152
  105. package/dist/features/seller/permission-map.d.ts +0 -5
  106. package/dist/features/seller/permission-map.js +0 -17
  107. package/dist/features/shell/StepForm.d.ts +0 -80
  108. package/dist/features/shell/StepForm.js +0 -136
  109. package/dist/features/stores/api/route.d.ts +0 -12
  110. package/dist/features/stores/api/route.js +0 -110
  111. package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.d.ts +0 -2
  112. package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.js +0 -33
  113. package/dist/features/tester/seed-data/authored/account-auth__testing-program.d.ts +0 -2
  114. package/dist/features/tester/seed-data/authored/account-auth__testing-program.js +0 -138
  115. package/dist/features/tester/seed-data/bids-tester-seed-data.d.ts +0 -2
  116. package/dist/features/tester/seed-data/bids-tester-seed-data.js +0 -37
  117. package/dist/features/tester/seed-data/blog-tester-seed-data.d.ts +0 -2
  118. package/dist/features/tester/seed-data/blog-tester-seed-data.js +0 -40
  119. package/dist/features/tester/seed-data/carts-tester-seed-data.d.ts +0 -3
  120. package/dist/features/tester/seed-data/carts-tester-seed-data.js +0 -70
  121. package/dist/features/tester/seed-data/categories-tester-seed-data.d.ts +0 -2
  122. package/dist/features/tester/seed-data/categories-tester-seed-data.js +0 -218
  123. package/dist/features/tester/seed-data/coupons-tester-seed-data.d.ts +0 -2
  124. package/dist/features/tester/seed-data/coupons-tester-seed-data.js +0 -76
  125. package/dist/features/tester/seed-data/events-tester-seed-data.d.ts +0 -2
  126. package/dist/features/tester/seed-data/events-tester-seed-data.js +0 -193
  127. package/dist/features/tester/seed-data/offers-tester-seed-data.d.ts +0 -2
  128. package/dist/features/tester/seed-data/offers-tester-seed-data.js +0 -191
  129. package/dist/features/tester/seed-data/orders-tester-seed-data.d.ts +0 -2
  130. package/dist/features/tester/seed-data/orders-tester-seed-data.js +0 -329
  131. package/dist/features/tester/seed-data/products-tester-seed-data.d.ts +0 -2
  132. package/dist/features/tester/seed-data/products-tester-seed-data.js +0 -815
  133. package/dist/features/tester/seed-data/stores-tester-seed-data.d.ts +0 -2
  134. package/dist/features/tester/seed-data/stores-tester-seed-data.js +0 -79
  135. package/dist/features/wishlist/components/WishlistToggleButton.d.ts +0 -12
  136. package/dist/features/wishlist/components/WishlistToggleButton.js +0 -36
  137. package/dist/monitoring/analytics.d.ts +0 -80
  138. package/dist/monitoring/analytics.js +0 -176
  139. package/dist/next/middleware/pii-redactor.d.ts +0 -32
  140. package/dist/next/middleware/pii-redactor.js +0 -84
  141. package/dist/providers/db-firebase/realtime.d.ts +0 -42
  142. package/dist/providers/db-firebase/realtime.js +0 -138
  143. package/dist/security/rbac/Can.d.ts +0 -31
  144. package/dist/security/rbac/Can.js +0 -22
  145. package/dist/security/rbac/default-roles.d.ts +0 -2
  146. package/dist/security/rbac/default-roles.js +0 -83
  147. package/dist/security/rbac/hook.d.ts +0 -20
  148. package/dist/security/rbac/hook.js +0 -30
  149. package/dist/security/rbac/index.d.ts +0 -8
  150. package/dist/security/rbac/index.js +0 -6
  151. package/dist/security/rbac/middleware.d.ts +0 -51
  152. package/dist/security/rbac/middleware.js +0 -51
  153. package/dist/security/rbac/resolver.d.ts +0 -5
  154. package/dist/security/rbac/resolver.js +0 -38
  155. package/dist/security/rbac/server.d.ts +0 -24
  156. package/dist/security/rbac/server.js +0 -42
  157. package/dist/security/rbac/types.d.ts +0 -22
  158. package/dist/security/rbac/types.js +0 -2
  159. package/dist/seed/conversations-seed-data.d.ts +0 -2
  160. package/dist/seed/conversations-seed-data.js +0 -292
  161. package/dist/utils/filter.helper.d.ts +0 -18
  162. package/dist/utils/filter.helper.js +0 -21
  163. package/dist/utils/pagination.helper.d.ts +0 -23
  164. package/dist/utils/pagination.helper.js +0 -24
  165. package/dist/utils/search-tokens.d.ts +0 -2
  166. package/dist/utils/search-tokens.js +0 -21
@@ -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;
@@ -1,160 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useState, useTransition } from "react";
4
- import { Alert, Badge, Button, Div, Heading, Input, LoginRequiredModal, Row, Spinner, Stack, Text } from "../../../ui";
5
- import { isAuthError } from "../../../utils/auth-error";
6
- import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
7
- import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
8
- import { normalizeError } from "../../../errors/normalize";
9
- function formatRupees(amount) {
10
- if (amount === undefined || amount === null)
11
- return "—";
12
- return `₹${amount.toLocaleString("en-IN")}`;
13
- }
14
- function relativeTime(date) {
15
- if (!date)
16
- return "";
17
- const d = date instanceof Date ? date : new Date(date);
18
- const diff = Date.now() - d.getTime();
19
- const mins = Math.floor(diff / 60000);
20
- if (mins < 60)
21
- return `${mins}m ago`;
22
- const hrs = Math.floor(diff / 3600000);
23
- if (hrs < 24)
24
- return `${hrs}h ago`;
25
- return `${Math.floor(diff / 86400000)}d ago`;
26
- }
27
- function expiresIn(expiresAt) {
28
- if (!expiresAt)
29
- return "";
30
- const exp = expiresAt instanceof Date ? expiresAt : new Date(expiresAt);
31
- const diff = exp.getTime() - Date.now();
32
- if (diff <= 0)
33
- return "Expired";
34
- const hrs = Math.floor(diff / 3600000);
35
- if (hrs < 1)
36
- return "< 1h left";
37
- if (hrs < 24)
38
- return `${hrs}h left`;
39
- return `${Math.floor(diff / 86400000)}d left`;
40
- }
41
- function statusVariant(status) {
42
- switch (status) {
43
- case "pending": return "warning";
44
- case "accepted": return "success";
45
- case "declined": return "danger";
46
- case "countered": return "info";
47
- case "expired": return "default";
48
- case "withdrawn": return "default";
49
- case "paid": return "success";
50
- default: return "default";
51
- }
52
- }
53
- const PLACEHOLDER_SELLER_NOTE = "Optional note to buyer";
54
- function OfferCard({ offer, onRespond, onUpdate, onNeedsLogin }) {
55
- const [uiState, setUiState] = useState("idle");
56
- const [counterInput, setCounterInput] = useState("");
57
- const [sellerNote, setSellerNote] = useState("");
58
- const [error, setError] = useState("");
59
- const [isPending, startTransition] = useTransition();
60
- const toAccepting = useCallback(() => setUiState("accepting"), []);
61
- const toCountering = useCallback(() => setUiState("countering"), []);
62
- const toDeclining = useCallback(() => setUiState("declining"), []);
63
- const toIdle = useCallback(() => setUiState("idle"), []);
64
- const isPendingOffer = offer.status === "pending";
65
- function submit(action) {
66
- setError("");
67
- startTransition(async () => {
68
- try {
69
- await onRespond(action);
70
- onUpdate(offer.id, {
71
- status: action.action === "accept"
72
- ? "accepted"
73
- : action.action === "decline"
74
- ? "declined"
75
- : "countered",
76
- });
77
- setUiState("idle");
78
- }
79
- catch (err) {
80
- void normalizeError(err);
81
- if (isAuthError(err)) {
82
- onNeedsLogin();
83
- }
84
- else {
85
- setError(err instanceof Error ? err.message : "Something went wrong.");
86
- }
87
- }
88
- });
89
- }
90
- function handleAccept() {
91
- submit({ action: "accept", offerId: offer.id, sellerNote: sellerNote || undefined });
92
- }
93
- function handleDecline() {
94
- submit({ action: "decline", offerId: offer.id, sellerNote: sellerNote || undefined });
95
- }
96
- function handleCounter() {
97
- const amount = parseInt(counterInput, 10);
98
- if (!amount || amount <= 0) {
99
- setError("Enter a valid counter amount.");
100
- return;
101
- }
102
- if (amount >= offer.listedPrice) {
103
- setError("Counter must be below the listed price.");
104
- return;
105
- }
106
- if (amount === offer.offerAmount) {
107
- setError("Counter must differ from the buyer's offer.");
108
- return;
109
- }
110
- submit({ action: "counter", offerId: offer.id, counterAmount: amount, sellerNote: sellerNote || undefined });
111
- }
112
- return (_jsxs(Stack, { surface: "card", padding: "sm", gap: "3", children: [_jsxs(Row, { align: "start", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: "truncate", color: "primary", size: "sm", weight: "semibold", children: offer.productTitle ?? "Unknown Product" }), _jsxs(Text, { className: "mt-0.5", color: "muted", size: "xs", children: [relativeTime(offer.createdAt), " \u00B7 ", isPendingOffer ? expiresIn(offer.expiresAt) : ""] })] }), _jsx(Badge, { variant: statusVariant(offer.status), size: "xs", className: "shrink-0 capitalize", children: offer.status })] }), _jsxs(Row, { wrap: true, gap: "md", children: [_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "faint", size: "xs", transform: "uppercase", children: "Listed" }), _jsx(Text, { size: "sm", weight: "medium", color: "muted", children: formatRupees(offer.listedPrice) })] }), _jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "faint", size: "xs", transform: "uppercase", children: "Offered" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: formatRupees(offer.offerAmount) })] }), offer.counterAmount != null && (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "faint", size: "xs", transform: "uppercase", children: "Your Counter" }), _jsx(Text, { className: "text-info", size: "sm", weight: "medium", children: formatRupees(offer.counterAmount) })] }))] }), offer.buyerNote && (_jsxs(Text, { className: "italic", color: "muted", size: "xs", children: ["\"", offer.buyerNote, "\""] })), isPendingOffer && uiState === "idle" && (_jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Button, { size: "sm", variant: "primary", onClick: toAccepting, disabled: isPending, children: "Accept" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: toCountering, disabled: isPending, border: "strong", children: "Counter" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: toDeclining, disabled: isPending, className: "text-error border border-error/20", children: "Decline" })] })), uiState === "accepting" && (_jsxs(Stack, { gap: "sm", children: [_jsx(Input, { placeholder: PLACEHOLDER_SELLER_NOTE, value: sellerNote, onChange: (e) => setSellerNote(e.target.value), disabled: isPending }), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { size: "sm", variant: "primary", onClick: handleAccept, disabled: isPending, children: isPending ? "Accepting…" : "Confirm Accept" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: toIdle, disabled: isPending, children: ACTIONS.STORE["cancel-form"].label })] })] })), uiState === "declining" && (_jsxs(Stack, { gap: "sm", children: [_jsx(Input, { placeholder: PLACEHOLDER_SELLER_NOTE, value: sellerNote, onChange: (e) => setSellerNote(e.target.value), disabled: isPending }), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { size: "sm", variant: "ghost", onClick: handleDecline, disabled: isPending, className: "text-error border border-error/20", children: isPending ? "Declining…" : "Confirm Decline" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: toIdle, disabled: isPending, children: ACTIONS.STORE["cancel-form"].label })] })] })), uiState === "countering" && (_jsxs(Stack, { gap: "sm", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Suggest a price between the buyer's offer (", formatRupees(offer.offerAmount), ") and your listed price (", formatRupees(offer.listedPrice), ")."] }), _jsx(Input, { type: "number", placeholder: "Your counter amount (\u20B9)", value: counterInput, onChange: (e) => setCounterInput(e.target.value), disabled: isPending }), _jsx(Input, { placeholder: PLACEHOLDER_SELLER_NOTE, value: sellerNote, onChange: (e) => setSellerNote(e.target.value), disabled: isPending }), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { size: "sm", variant: "secondary", onClick: handleCounter, disabled: isPending, children: isPending ? "Sending…" : "Send Counter" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: toIdle, disabled: isPending, children: ACTIONS.STORE["cancel-form"].label })] })] })), error && _jsx(Alert, { variant: "error", children: _jsx(Text, { size: "xs", children: error }) })] }));
113
- }
114
- export function SellerOffersPanel({ fetchEndpoint = SELLER_ENDPOINTS.OFFERS, onRespond, className = "", }) {
115
- const [offers, setOffers] = useState([]);
116
- const [loading, setLoading] = useState(true);
117
- const [fetchError, setFetchError] = useState("");
118
- const [showLoginModal, setShowLoginModal] = useState(false);
119
- const [statusFilter, setStatusFilter] = useState("all");
120
- const loadOffers = useCallback(async () => {
121
- setLoading(true);
122
- setFetchError("");
123
- try {
124
- const url = statusFilter === "all"
125
- ? fetchEndpoint
126
- : `${fetchEndpoint}?status=${statusFilter}`;
127
- const res = await fetch(url);
128
- if (!res.ok) {
129
- if (res.status === 401 || res.status === 403) {
130
- setShowLoginModal(true);
131
- return;
132
- }
133
- throw new Error(`Error ${res.status}`);
134
- }
135
- const json = (await res.json());
136
- setOffers(json.items ?? json.offers ?? []);
137
- }
138
- catch (err) {
139
- void normalizeError(err);
140
- setFetchError(err instanceof Error ? err.message : "Failed to load offers.");
141
- }
142
- finally {
143
- setLoading(false);
144
- }
145
- }, [fetchEndpoint, statusFilter]);
146
- useEffect(() => { void loadOffers(); }, [loadOffers]);
147
- function handleUpdate(id, patch) {
148
- setOffers((prev) => prev.map((o) => (o.id === id ? { ...o, ...patch } : o)));
149
- }
150
- const STATUS_FILTERS = [
151
- { value: "all", label: "All" },
152
- { value: "pending", label: "Pending" },
153
- { value: "countered", label: "Countered" },
154
- { value: "accepted", label: "Accepted" },
155
- { value: "declined", label: "Declined" },
156
- { value: "expired", label: "Expired" },
157
- ];
158
- const pending = offers.filter((o) => o.status === "pending").length;
159
- return (_jsxs(Stack, { className: `${className}`, gap: "md", children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to manage offers. Please log in or create an account to continue." }), _jsxs(Row, { align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Div, { children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", color: "primary", children: "Offers Received" }), pending > 0 && (_jsxs(Text, { className: "text-warning mt-0.5", size: "xs", children: [pending, " pending offer", pending > 1 ? "s" : "", " awaiting your response"] }))] }), _jsx(Button, { size: "sm", variant: "ghost", onClick: loadOffers, disabled: loading, border: "strong", textSize: "xs", children: loading ? "Refreshing…" : ACTIONS.STORE["refresh-offers"].label })] }), _jsx(Row, { wrap: true, gap: "xs", children: STATUS_FILTERS.map((f) => (_jsx(Button, { size: "sm", variant: statusFilter === f.value ? "primary" : "ghost", onClick: () => setStatusFilter(f.value), className: "text-[length:var(--appkit-text-xs)] px-[var(--appkit-space-2)] py-[var(--appkit-space-1)]", children: f.label }, f.value))) }), fetchError && _jsx(Alert, { variant: "error", children: _jsx(Text, { size: "sm", children: fetchError }) }), loading && (_jsx(Row, { justify: "center", padding: "y-3xl", children: _jsx(Spinner, { size: "lg" }) })), !loading && offers.length === 0 && (_jsx(Div, { className: "text-center", padding: "y-3xl", children: _jsx(Text, { size: "sm", color: "faint", children: "No offers found" }) })), !loading && offers.length > 0 && (_jsx(Stack, { gap: "3", children: offers.map((offer) => (_jsx(OfferCard, { offer: offer, onRespond: onRespond, onUpdate: handleUpdate, onNeedsLogin: () => setShowLoginModal(true) }, offer.id))) }))] }));
160
- }
@@ -1,6 +0,0 @@
1
- import React from "react";
2
- import type { ListingLayoutProps } from "../../../ui";
3
- export interface SellerPreOrdersViewProps extends ListingLayoutProps {
4
- onDelete?: (id: string) => Promise<void>;
5
- }
6
- export declare function SellerPreOrdersView({ children, onDelete, ...props }: SellerPreOrdersViewProps): React.JSX.Element;