@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,284 +0,0 @@
1
- /**
2
- * ConversationsRepository — buyer↔seller messaging.
3
- *
4
- * One document per buyer×storeId×productId tuple. Messages are embedded in
5
- * the document as an array (good up to ~200 entries; migrate to subcollection
6
- * later if threads explode).
7
- *
8
- * All mutations are transactional so concurrent writes don't drop messages
9
- * or unread counters.
10
- */
11
- import { getAdminDb } from "../../../providers/db-firebase";
12
- import { encryptPiiFields, decryptPiiFields } from "../../../security/pii-encrypt";
13
- import { CONVERSATION_PII_FIELDS } from "../../../security/pii-schemas";
14
- import { serverLogger } from "../../../monitoring";
15
- import { CONVERSATION_FIELDS } from "../../../constants/field-names";
16
- import { CONVERSATIONS_COLLECTION, } from "../schemas/firestore";
17
- function toDate(raw) {
18
- if (raw instanceof Date)
19
- return raw;
20
- if (raw && typeof raw.toDate === "function") {
21
- return raw.toDate();
22
- }
23
- if (typeof raw === "string" || typeof raw === "number")
24
- return new Date(raw);
25
- return new Date();
26
- }
27
- function normaliseMessage(raw) {
28
- const r = (raw ?? {});
29
- return {
30
- id: String(r.id ?? ""),
31
- senderId: String(r.senderId ?? ""),
32
- senderRole: r.senderRole ?? "buyer",
33
- body: String(r.body ?? ""),
34
- isRead: r.isRead === true,
35
- sentAt: toDate(r.sentAt),
36
- attachments: Array.isArray(r.attachments)
37
- ? r.attachments
38
- : undefined,
39
- };
40
- }
41
- function normaliseDoc(id, raw) {
42
- // Decrypt here rather than at the six call sites: this is the one function
43
- // every read path already goes through, so a new reader cannot forget it.
44
- // `markRead` deliberately does NOT decrypt — it reads raw messages, flips
45
- // isRead and writes them straight back, so ciphertext round-trips untouched.
46
- const r = decryptPiiFields(raw, [...CONVERSATION_PII_FIELDS]);
47
- return {
48
- id,
49
- buyerId: String(r.buyerId ?? ""),
50
- buyerDisplayName: String(r.buyerDisplayName ?? ""),
51
- storeId: String(r.storeId ?? ""),
52
- storeName: String(r.storeName ?? ""),
53
- sellerDisplayName: String(r.sellerDisplayName ?? ""),
54
- productId: typeof r.productId === "string" ? r.productId : undefined,
55
- productTitle: typeof r.productTitle === "string" ? r.productTitle : undefined,
56
- messages: Array.isArray(r.messages) ? r.messages.map(normaliseMessage) : [],
57
- lastMessage: String(r.lastMessage ?? ""),
58
- lastMessageAt: toDate(r.lastMessageAt),
59
- unreadBuyer: typeof r.unreadBuyer === "number" ? r.unreadBuyer : 0,
60
- unreadSeller: typeof r.unreadSeller === "number" ? r.unreadSeller : 0,
61
- status: r.status ?? "active",
62
- createdAt: toDate(r.createdAt),
63
- updatedAt: toDate(r.updatedAt),
64
- };
65
- }
66
- export class ConversationFullError extends Error {
67
- constructor() {
68
- super(...arguments);
69
- this.code = "CONVERSATION_FULL";
70
- }
71
- }
72
- export class ConversationsRepository {
73
- collection() {
74
- return getAdminDb().collection(CONVERSATIONS_COLLECTION);
75
- }
76
- /**
77
- * Find an existing buyer×store×product conversation or create a new one.
78
- * Idempotent — safe to call on every "Contact Seller" click.
79
- */
80
- async findOrCreateByContext(params) {
81
- const db = getAdminDb();
82
- const coll = this.collection();
83
- const { buyerId, storeId, productId } = params;
84
- // Stable composite key: buyerId + storeId + (productId | "general")
85
- const productKey = productId ?? "general";
86
- const stableId = `conv-${buyerId}-${storeId}-${productKey}`;
87
- const ref = coll.doc(stableId);
88
- return db.runTransaction(async (tx) => {
89
- const snap = await tx.get(ref);
90
- if (snap.exists) {
91
- return normaliseDoc(snap.id, snap.data() ?? {});
92
- }
93
- const now = new Date();
94
- const newDoc = {
95
- buyerId: params.buyerId,
96
- buyerDisplayName: params.buyerDisplayName,
97
- storeId: params.storeId,
98
- storeName: params.storeName,
99
- sellerDisplayName: params.sellerDisplayName,
100
- productId: params.productId,
101
- productTitle: params.productTitle,
102
- messages: [],
103
- lastMessage: "",
104
- lastMessageAt: now,
105
- unreadBuyer: 0,
106
- unreadSeller: 0,
107
- status: "active",
108
- createdAt: now,
109
- updatedAt: now,
110
- };
111
- const encryptedDoc = encryptPiiFields(newDoc, [...CONVERSATION_PII_FIELDS]);
112
- tx.set(ref, encryptedDoc);
113
- return normaliseDoc(stableId, encryptedDoc);
114
- });
115
- }
116
- async findById(id) {
117
- const snap = await this.collection().doc(id).get();
118
- if (!snap.exists)
119
- return null;
120
- return normaliseDoc(snap.id, snap.data() ?? {});
121
- }
122
- async listByBuyer(buyerId) {
123
- const snap = await this.collection()
124
- .where(CONVERSATION_FIELDS.BUYER_ID, "==", buyerId)
125
- .orderBy(CONVERSATION_FIELDS.LAST_MESSAGE_AT, "desc")
126
- .get();
127
- return snap.docs.map((d) => normaliseDoc(d.id, d.data()));
128
- }
129
- async listByStore(storeId) {
130
- const snap = await this.collection()
131
- .where(CONVERSATION_FIELDS.STORE_ID, "==", storeId)
132
- .orderBy(CONVERSATION_FIELDS.LAST_MESSAGE_AT, "desc")
133
- .get();
134
- return snap.docs.map((d) => normaliseDoc(d.id, d.data()));
135
- }
136
- /**
137
- * Conversation ids this user may subscribe to, for the RTDB `conversationIds`
138
- * custom-token claim.
139
- *
140
- * 🛑 This claim previously had NO ISSUER ANYWHERE. The `chats/$conversationId`
141
- * rule requires `auth.token.conversationIds[$conversationId] == true`, but
142
- * `/api/realtime/token` only ever issued `chatIds`, built by
143
- * `chatRepository.getChatIdsForUser()` over the **`chatRooms`** collection —
144
- * a different collection with different document ids. So the buyer↔seller
145
- * live-message channel was denied for every user, and both subscribing hooks
146
- * swallowed the permission error (one into `setIsConnected(false)`, one into
147
- * a literal `// ignore`). Messages only ever appeared for the sender, via an
148
- * unrelated `refetch()` after send.
149
- *
150
- * `.select()` with no fields fetches ids only — the document bodies are not
151
- * needed and this runs on every token issue.
152
- *
153
- * @param limit hard cap. Firebase custom claims are limited to 1000 BYTES
154
- * total; each entry costs roughly `"<20-char id>":true,` ≈ 28 bytes, so a
155
- * user in hundreds of conversations would make `createCustomToken` throw
156
- * and take the whole route down (see the route's own comment).
157
- */
158
- async getConversationIdsForUser(userId, storeIds = [], limit = 25) {
159
- const queries = [
160
- this.collection()
161
- .where(CONVERSATION_FIELDS.BUYER_ID, "==", userId)
162
- .orderBy(CONVERSATION_FIELDS.LAST_MESSAGE_AT, "desc")
163
- .limit(limit)
164
- .select()
165
- .get(),
166
- // Firestore `in` caps at 30 values; a seller with more stores than that
167
- // is not a shape this app produces.
168
- ...(storeIds.length > 0
169
- ? [
170
- this.collection()
171
- .where(CONVERSATION_FIELDS.STORE_ID, "in", storeIds.slice(0, 30))
172
- .orderBy(CONVERSATION_FIELDS.LAST_MESSAGE_AT, "desc")
173
- .limit(limit)
174
- .select()
175
- .get(),
176
- ]
177
- : []),
178
- ];
179
- const snaps = await Promise.all(queries);
180
- const ids = new Set();
181
- for (const snap of snaps) {
182
- for (const doc of snap.docs)
183
- ids.add(doc.id);
184
- }
185
- // Most-recent-first is already the sort order; truncating here keeps the
186
- // claim inside the byte budget when a user is both buyer and seller.
187
- return [...ids].slice(0, limit);
188
- }
189
- /**
190
- * Append a message to a conversation in a transaction. Bumps the counterparty
191
- * unread counter; updates lastMessage / lastMessageAt / updatedAt.
192
- */
193
- async appendMessage(conversationId, msg) {
194
- const db = getAdminDb();
195
- const ref = this.collection().doc(conversationId);
196
- try {
197
- return await db.runTransaction(async (tx) => {
198
- const snap = await tx.get(ref);
199
- if (!snap.exists) {
200
- throw new Error("Conversation not found");
201
- }
202
- const data = snap.data() ?? {};
203
- const existing = Array.isArray(data.messages)
204
- ? data.messages.map(normaliseMessage)
205
- : [];
206
- const now = msg.sentAt ?? new Date();
207
- const newMessage = {
208
- id: msg.id ?? `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
209
- senderId: msg.senderId,
210
- senderRole: msg.senderRole,
211
- body: msg.body,
212
- isRead: false,
213
- sentAt: now,
214
- attachments: msg.attachments,
215
- };
216
- const nextMessages = [...existing, newMessage];
217
- const unreadBuyer = msg.senderRole === "seller"
218
- ? (typeof data.unreadBuyer === "number" ? data.unreadBuyer : 0) + 1
219
- : (typeof data.unreadBuyer === "number" ? data.unreadBuyer : 0);
220
- const unreadSeller = msg.senderRole === "buyer"
221
- ? (typeof data.unreadSeller === "number" ? data.unreadSeller : 0) + 1
222
- : (typeof data.unreadSeller === "number" ? data.unreadSeller : 0);
223
- const patch = {
224
- messages: nextMessages,
225
- lastMessage: msg.body,
226
- lastMessageAt: now,
227
- unreadBuyer,
228
- unreadSeller,
229
- updatedAt: now,
230
- };
231
- // Encrypt before the write; normaliseDoc decrypts on the way back out,
232
- // so the caller still receives plaintext.
233
- const encryptedPatch = encryptPiiFields(patch, [...CONVERSATION_PII_FIELDS]);
234
- tx.update(ref, encryptedPatch);
235
- return normaliseDoc(conversationId, { ...data, ...encryptedPatch });
236
- });
237
- }
238
- catch (error) {
239
- serverLogger.error("ConversationsRepository.appendMessage error", {
240
- conversationId,
241
- error,
242
- });
243
- throw error;
244
- }
245
- }
246
- /**
247
- * Zero out the unread counter for one party. Also flips `isRead` on the
248
- * embedded messages sent by the other party so the seller side can see what
249
- * the buyer has already read.
250
- */
251
- async markRead(conversationId, role) {
252
- const db = getAdminDb();
253
- const ref = this.collection().doc(conversationId);
254
- try {
255
- await db.runTransaction(async (tx) => {
256
- const snap = await tx.get(ref);
257
- if (!snap.exists)
258
- return;
259
- const data = snap.data() ?? {};
260
- const messages = Array.isArray(data.messages)
261
- ? data.messages.map(normaliseMessage)
262
- : [];
263
- // role="buyer" means the buyer just opened the chat → mark all
264
- // seller-sent messages read.
265
- const otherRole = role === "buyer" ? "seller" : "buyer";
266
- const nextMessages = messages.map((m) => m.senderRole === otherRole && !m.isRead ? { ...m, isRead: true } : m);
267
- tx.update(ref, {
268
- messages: nextMessages,
269
- [role === "buyer" ? "unreadBuyer" : "unreadSeller"]: 0,
270
- updatedAt: new Date(),
271
- });
272
- });
273
- }
274
- catch (error) {
275
- serverLogger.error("ConversationsRepository.markRead error", {
276
- conversationId,
277
- role,
278
- error,
279
- });
280
- throw error;
281
- }
282
- }
283
- }
284
- export const conversationsRepository = new ConversationsRepository();
@@ -1,34 +0,0 @@
1
- /**
2
- * Messages / Conversations Firestore Document Types & Constants
3
- *
4
- * Architecture: one `conversations` document per buyer↔seller pair on a product.
5
- * Messages are embedded as an array for seed purposes; production can migrate to
6
- * a subcollection when the thread exceeds ~200 messages.
7
- */
8
- import type { BaseDocument } from "../../../_internal/shared/types/base-document";
9
- export type ConversationMessage = {
10
- id: string;
11
- senderId: string;
12
- senderRole: "buyer" | "seller";
13
- body: string;
14
- isRead: boolean;
15
- sentAt: Date;
16
- attachments?: string[];
17
- };
18
- export interface ConversationDocument extends BaseDocument {
19
- buyerId: string;
20
- buyerDisplayName: string;
21
- storeId: string;
22
- storeName: string;
23
- sellerDisplayName: string;
24
- productId?: string;
25
- productTitle?: string;
26
- messages: ConversationMessage[];
27
- lastMessage: string;
28
- lastMessageAt: Date;
29
- unreadBuyer: number;
30
- unreadSeller: number;
31
- status: "active" | "archived" | "blocked";
32
- }
33
- export declare const CONVERSATIONS_COLLECTION: "conversations";
34
- export declare const CONVERSATIONS_INDEXED_FIELDS: readonly ["buyerId", "storeId", "productId", "status", "lastMessageAt", "createdAt"];
@@ -1,16 +0,0 @@
1
- /**
2
- * Messages / Conversations Firestore Document Types & Constants
3
- *
4
- * Architecture: one `conversations` document per buyer↔seller pair on a product.
5
- * Messages are embedded as an array for seed purposes; production can migrate to
6
- * a subcollection when the thread exceeds ~200 messages.
7
- */
8
- export const CONVERSATIONS_COLLECTION = "conversations";
9
- export const CONVERSATIONS_INDEXED_FIELDS = [
10
- "buyerId",
11
- "storeId",
12
- "productId",
13
- "status",
14
- "lastMessageAt",
15
- "createdAt",
16
- ];
@@ -1,177 +0,0 @@
1
- import { z } from "zod";
2
- export * from "./firestore";
3
- export declare const conversationMessageSenderRoleSchema: z.ZodEnum<["buyer", "seller"]>;
4
- export declare const conversationStatusSchema: z.ZodEnum<["active", "archived", "blocked"]>;
5
- export declare const conversationMessageSchema: z.ZodObject<{
6
- id: z.ZodString;
7
- senderId: z.ZodString;
8
- senderRole: z.ZodEnum<["buyer", "seller"]>;
9
- body: z.ZodString;
10
- isRead: z.ZodBoolean;
11
- sentAt: z.ZodUnion<[z.ZodDate, z.ZodString, z.ZodObject<{
12
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
13
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
14
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
15
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
16
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
17
- }, z.ZodTypeAny, "passthrough">>]>;
18
- attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
19
- }, "strip", z.ZodTypeAny, {
20
- id: string;
21
- body: string;
22
- isRead: boolean;
23
- senderId: string;
24
- senderRole: "buyer" | "seller";
25
- sentAt: string | Date | z.objectOutputType<{
26
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
27
- }, z.ZodTypeAny, "passthrough">;
28
- attachments?: string[] | undefined;
29
- }, {
30
- id: string;
31
- body: string;
32
- isRead: boolean;
33
- senderId: string;
34
- senderRole: "buyer" | "seller";
35
- sentAt: string | Date | z.objectInputType<{
36
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
37
- }, z.ZodTypeAny, "passthrough">;
38
- attachments?: string[] | undefined;
39
- }>;
40
- export declare const conversationFirestoreSchema: z.ZodObject<{
41
- createdAt: z.ZodUnion<[z.ZodDate, z.ZodString, z.ZodObject<{
42
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
43
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
44
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
45
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
46
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
47
- }, z.ZodTypeAny, "passthrough">>]>;
48
- updatedAt: z.ZodUnion<[z.ZodDate, z.ZodString, z.ZodObject<{
49
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
50
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
51
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
52
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
53
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
54
- }, z.ZodTypeAny, "passthrough">>]>;
55
- id: z.ZodString;
56
- buyerId: z.ZodString;
57
- buyerDisplayName: z.ZodString;
58
- storeId: z.ZodString;
59
- storeName: z.ZodString;
60
- sellerDisplayName: z.ZodString;
61
- productId: z.ZodOptional<z.ZodString>;
62
- productTitle: z.ZodOptional<z.ZodString>;
63
- messages: z.ZodArray<z.ZodObject<{
64
- id: z.ZodString;
65
- senderId: z.ZodString;
66
- senderRole: z.ZodEnum<["buyer", "seller"]>;
67
- body: z.ZodString;
68
- isRead: z.ZodBoolean;
69
- sentAt: z.ZodUnion<[z.ZodDate, z.ZodString, z.ZodObject<{
70
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
71
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
72
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
73
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
74
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
75
- }, z.ZodTypeAny, "passthrough">>]>;
76
- attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
77
- }, "strip", z.ZodTypeAny, {
78
- id: string;
79
- body: string;
80
- isRead: boolean;
81
- senderId: string;
82
- senderRole: "buyer" | "seller";
83
- sentAt: string | Date | z.objectOutputType<{
84
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
85
- }, z.ZodTypeAny, "passthrough">;
86
- attachments?: string[] | undefined;
87
- }, {
88
- id: string;
89
- body: string;
90
- isRead: boolean;
91
- senderId: string;
92
- senderRole: "buyer" | "seller";
93
- sentAt: string | Date | z.objectInputType<{
94
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
95
- }, z.ZodTypeAny, "passthrough">;
96
- attachments?: string[] | undefined;
97
- }>, "many">;
98
- lastMessage: z.ZodString;
99
- lastMessageAt: z.ZodUnion<[z.ZodDate, z.ZodString, z.ZodObject<{
100
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
101
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
102
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
103
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
104
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
105
- }, z.ZodTypeAny, "passthrough">>]>;
106
- unreadBuyer: z.ZodNumber;
107
- unreadSeller: z.ZodNumber;
108
- status: z.ZodEnum<["active", "archived", "blocked"]>;
109
- }, "strip", z.ZodTypeAny, {
110
- status: "archived" | "active" | "blocked";
111
- id: string;
112
- createdAt: string | Date | z.objectOutputType<{
113
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
114
- }, z.ZodTypeAny, "passthrough">;
115
- updatedAt: string | Date | z.objectOutputType<{
116
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
117
- }, z.ZodTypeAny, "passthrough">;
118
- storeId: string;
119
- storeName: string;
120
- buyerId: string;
121
- lastMessageAt: string | Date | z.objectOutputType<{
122
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
123
- }, z.ZodTypeAny, "passthrough">;
124
- lastMessage: string;
125
- messages: {
126
- id: string;
127
- body: string;
128
- isRead: boolean;
129
- senderId: string;
130
- senderRole: "buyer" | "seller";
131
- sentAt: string | Date | z.objectOutputType<{
132
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
133
- }, z.ZodTypeAny, "passthrough">;
134
- attachments?: string[] | undefined;
135
- }[];
136
- buyerDisplayName: string;
137
- sellerDisplayName: string;
138
- unreadBuyer: number;
139
- unreadSeller: number;
140
- productId?: string | undefined;
141
- productTitle?: string | undefined;
142
- }, {
143
- status: "archived" | "active" | "blocked";
144
- id: string;
145
- createdAt: string | Date | z.objectInputType<{
146
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
147
- }, z.ZodTypeAny, "passthrough">;
148
- updatedAt: string | Date | z.objectInputType<{
149
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
150
- }, z.ZodTypeAny, "passthrough">;
151
- storeId: string;
152
- storeName: string;
153
- buyerId: string;
154
- lastMessageAt: string | Date | z.objectInputType<{
155
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
156
- }, z.ZodTypeAny, "passthrough">;
157
- lastMessage: string;
158
- messages: {
159
- id: string;
160
- body: string;
161
- isRead: boolean;
162
- senderId: string;
163
- senderRole: "buyer" | "seller";
164
- sentAt: string | Date | z.objectInputType<{
165
- toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
166
- }, z.ZodTypeAny, "passthrough">;
167
- attachments?: string[] | undefined;
168
- }[];
169
- buyerDisplayName: string;
170
- sellerDisplayName: string;
171
- unreadBuyer: number;
172
- unreadSeller: number;
173
- productId?: string | undefined;
174
- productTitle?: string | undefined;
175
- }>;
176
- export type ConversationFromSchema = z.infer<typeof conversationFirestoreSchema>;
177
- export type ConversationMessageFromSchema = z.infer<typeof conversationMessageSchema>;
@@ -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,6 +0,0 @@
1
- /**
2
- * @mohasinac/appkit/features/messages/server
3
- * Server-only entry point for the messages domain.
4
- */
5
- export * from "./actions";
6
- export { ConversationsRepository, conversationsRepository, ConversationFullError, } from "./repository/conversations.repository";
@@ -1,6 +0,0 @@
1
- /**
2
- * @mohasinac/appkit/features/messages/server
3
- * Server-only entry point for the messages domain.
4
- */
5
- export * from "./actions";
6
- export { ConversationsRepository, conversationsRepository, ConversationFullError, } from "./repository/conversations.repository";
@@ -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>;