@mohasinac/appkit 4.41.0 → 4.41.2
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/seller/schemas/coupon-form.d.ts +37 -37
- package/dist/features/seller/schemas/coupon-form.js +36 -8
- package/dist/features/shell/SectionForm.js +44 -5
- package/dist/schemas/registry.d.ts +10 -10
- package/package.json +2 -2
- 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,70 +0,0 @@
|
|
|
1
|
-
import { cache } from "react";
|
|
2
|
-
import { homepageSectionsRepository, carouselRepository, productRepository, reviewRepository, blogRepository, eventRepository, categoriesRepository, siteSettingsRepository, } from "../../../../repositories";
|
|
3
|
-
import { HOMEPAGE_FEATURED_REVIEWS_LIMIT, HOMEPAGE_RECENT_BLOG_POSTS_LIMIT, } from "../../../shared/features/homepage/config";
|
|
4
|
-
/** Section types that require a feature flag to be enabled to show on the homepage. */
|
|
5
|
-
const SECTION_FEATURE_GATE = {
|
|
6
|
-
auctions: "auctions",
|
|
7
|
-
"pre-orders": "preOrders",
|
|
8
|
-
events: "events",
|
|
9
|
-
"event-raffles": "events",
|
|
10
|
-
"prize-draws": "events",
|
|
11
|
-
"blog-articles": "blog",
|
|
12
|
-
};
|
|
13
|
-
export const getHomepageSections = cache(async () => {
|
|
14
|
-
const [sections, settings] = await Promise.all([
|
|
15
|
-
homepageSectionsRepository.getEnabledSections().catch(() => []),
|
|
16
|
-
siteSettingsRepository.getSingleton().catch(() => null),
|
|
17
|
-
]);
|
|
18
|
-
const flags = settings?.featureFlags;
|
|
19
|
-
if (!flags)
|
|
20
|
-
return sections;
|
|
21
|
-
return sections.filter((s) => {
|
|
22
|
-
const flagKey = SECTION_FEATURE_GATE[s.type];
|
|
23
|
-
if (!flagKey)
|
|
24
|
-
return true;
|
|
25
|
-
return flags[flagKey] !== false;
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
export const getHeroCarouselSlides = cache(async () => {
|
|
29
|
-
return carouselRepository.getActiveSlides().catch(() => []);
|
|
30
|
-
});
|
|
31
|
-
/**
|
|
32
|
-
* One parallel fetch returning every piece of data the homepage sections need.
|
|
33
|
-
* Page components call this once; React.cache() deduplicates per request.
|
|
34
|
-
*/
|
|
35
|
-
export const getHomepageInitial = cache(async () => {
|
|
36
|
-
const [rawSections, settings, carouselSlides, featuredProducts, activeAuctions, activePreOrders, activeBrands, featuredReviews, recentBlogPosts, activeEvents, featuredCategories,] = await Promise.all([
|
|
37
|
-
homepageSectionsRepository.getEnabledSections().catch(() => []),
|
|
38
|
-
siteSettingsRepository.getSingleton().catch(() => null),
|
|
39
|
-
carouselRepository.getActiveSlides().catch(() => []),
|
|
40
|
-
productRepository.findFeatured().catch(() => []),
|
|
41
|
-
productRepository.findActiveAuctions().catch(() => []),
|
|
42
|
-
productRepository.findActivePreOrders().catch(() => []),
|
|
43
|
-
categoriesRepository.findActiveBrands().catch(() => []),
|
|
44
|
-
reviewRepository.findFeatured(HOMEPAGE_FEATURED_REVIEWS_LIMIT).catch(() => []),
|
|
45
|
-
blogRepository.listPublished({}, { page: 1, pageSize: HOMEPAGE_RECENT_BLOG_POSTS_LIMIT, sorts: "-publishedAt" }).then((r) => r.items ?? []).catch(() => []),
|
|
46
|
-
eventRepository.listActive().catch(() => []),
|
|
47
|
-
categoriesRepository.getFeaturedCategories().catch(() => []),
|
|
48
|
-
]);
|
|
49
|
-
const flags = settings?.featureFlags;
|
|
50
|
-
const sections = flags
|
|
51
|
-
? rawSections.filter((s) => {
|
|
52
|
-
const flagKey = SECTION_FEATURE_GATE[s.type];
|
|
53
|
-
if (!flagKey)
|
|
54
|
-
return true;
|
|
55
|
-
return flags[flagKey] !== false;
|
|
56
|
-
})
|
|
57
|
-
: rawSections;
|
|
58
|
-
return {
|
|
59
|
-
sections,
|
|
60
|
-
carouselSlides,
|
|
61
|
-
featuredProducts,
|
|
62
|
-
activeAuctions,
|
|
63
|
-
activePreOrders,
|
|
64
|
-
activeBrands,
|
|
65
|
-
featuredReviews,
|
|
66
|
-
recentBlogPosts,
|
|
67
|
-
activeEvents,
|
|
68
|
-
featuredCategories,
|
|
69
|
-
};
|
|
70
|
-
});
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type ActionResult } from "@mohasinac/appkit/server";
|
|
2
|
-
import type { ConversationDocument } from "../../../../features/messages/schemas/firestore";
|
|
3
|
-
export interface StartLiveConversationInput {
|
|
4
|
-
productId: string;
|
|
5
|
-
}
|
|
6
|
-
export declare function startLiveConversationAction(input: StartLiveConversationInput): Promise<ActionResult<ConversationDocument>>;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"use server";
|
|
2
|
-
import { wrapAction } from "@mohasinac/appkit/server";
|
|
3
|
-
import { conversationsRepository } from "../../../../features/messages/repository/conversations.repository";
|
|
4
|
-
import { requireRoleUser } from "../../../../providers/auth-firebase/helpers";
|
|
5
|
-
import { storeRepository, productRepository } from "../../../../repositories";
|
|
6
|
-
export async function startLiveConversationAction(input) {
|
|
7
|
-
return wrapAction(async () => {
|
|
8
|
-
const user = await requireRoleUser(["user", "buyer", "seller", "admin"]);
|
|
9
|
-
const product = await productRepository.findByIdOrSlug(input.productId);
|
|
10
|
-
if (!product || product.listingType !== "live") {
|
|
11
|
-
throw new Error("Product not found or not a live-item listing");
|
|
12
|
-
}
|
|
13
|
-
const store = await storeRepository.findById(product.storeId);
|
|
14
|
-
if (!store) {
|
|
15
|
-
throw new Error("Store not found");
|
|
16
|
-
}
|
|
17
|
-
return conversationsRepository.findOrCreateByContext({
|
|
18
|
-
buyerId: user.uid,
|
|
19
|
-
buyerDisplayName: user.name ?? user.email ?? user.uid,
|
|
20
|
-
storeId: product.storeId,
|
|
21
|
-
storeName: store.storeName,
|
|
22
|
-
sellerDisplayName: store.storeName,
|
|
23
|
-
productId: product.id,
|
|
24
|
-
productTitle: product.title,
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Messages server actions — thin re-exports. Keeps RTDB ping-channel behaviour
|
|
3
|
-
* (firestore canonical + RTDB notification) in the source action so consumers
|
|
4
|
-
* cannot bypass it.
|
|
5
|
-
*/
|
|
6
|
-
export { sendMessage as sendMessageAction, markConversationRead as markConversationReadAction, MESSAGE_MAX_LENGTH, } from "../../../../features/messages/actions/messages-actions";
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Messages server actions — thin re-exports. Keeps RTDB ping-channel behaviour
|
|
3
|
-
* (firestore canonical + RTDB notification) in the source action so consumers
|
|
4
|
-
* cannot bypass it.
|
|
5
|
-
*/
|
|
6
|
-
export { sendMessage as sendMessageAction, markConversationRead as markConversationReadAction, MESSAGE_MAX_LENGTH, } from "../../../../features/messages/actions/messages-actions";
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Messages data layer — server-only fetchers with React.cache dedup.
|
|
3
|
-
* RTDB ping channel is preserved (the actions module owns that side effect).
|
|
4
|
-
*/
|
|
5
|
-
export interface MessagesDataOptions {
|
|
6
|
-
_reserved?: never;
|
|
7
|
-
}
|
|
8
|
-
export declare const listConversationsForBuyer: (buyerId: string, _opts?: MessagesDataOptions) => Promise<import("../../../..").ConversationDocument[]>;
|
|
9
|
-
export declare const listConversationsForStore: (storeId: string, _opts?: MessagesDataOptions) => Promise<import("../../../..").ConversationDocument[]>;
|
|
10
|
-
export declare const getConversation: (convId: string, _opts?: MessagesDataOptions) => Promise<import("../../../..").ConversationDocument | null>;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Messages data layer — server-only fetchers with React.cache dedup.
|
|
3
|
-
* RTDB ping channel is preserved (the actions module owns that side effect).
|
|
4
|
-
*/
|
|
5
|
-
import { cache } from "react";
|
|
6
|
-
import { listConversationsForBuyer as _listConversationsForBuyer, listConversationsForStore as _listConversationsForStore, getConversation as _getConversation, } from "../../../../features/messages/actions/messages-actions";
|
|
7
|
-
export const listConversationsForBuyer = cache(async (buyerId, _opts) => {
|
|
8
|
-
void _opts;
|
|
9
|
-
if (!buyerId)
|
|
10
|
-
return [];
|
|
11
|
-
return _listConversationsForBuyer(buyerId);
|
|
12
|
-
});
|
|
13
|
-
export const listConversationsForStore = cache(async (storeId, _opts) => {
|
|
14
|
-
void _opts;
|
|
15
|
-
if (!storeId)
|
|
16
|
-
return [];
|
|
17
|
-
return _listConversationsForStore(storeId);
|
|
18
|
-
});
|
|
19
|
-
export const getConversation = cache(async (convId, _opts) => {
|
|
20
|
-
void _opts;
|
|
21
|
-
if (!convId)
|
|
22
|
-
return null;
|
|
23
|
-
return _getConversation(convId);
|
|
24
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { JobContext } from "../runtime/types";
|
|
2
|
-
/**
|
|
3
|
-
* Reverts every live tester-sandbox fixture back to its canonical seeded
|
|
4
|
-
* shape (undoing edits made through admin/seller forms) and prunes any
|
|
5
|
-
* isTestData doc that isn't one of the known fixtures (something a tester
|
|
6
|
-
* created during QA, e.g. a cloned product). Runs every 4 hours — distinct
|
|
7
|
-
* from `runTesterSandboxCleanup`, which only deletes docs whose 7-day
|
|
8
|
-
* `testDataExpiresAt` TTL has actually lapsed and never touches still-live
|
|
9
|
-
* docs' fields. Scoped to the tester sandbox only (`isTestData: true`) —
|
|
10
|
-
* the permanent Beyblade catalog real visitors browse is never touched.
|
|
11
|
-
*/
|
|
12
|
-
export declare function runTesterSandboxRefresh(ctx: JobContext): Promise<void>;
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { batchDelete, getTestDataRefs } from "../handlers/_helpers";
|
|
2
|
-
import { BID_FIELDS } from "../../../../constants/field-names";
|
|
3
|
-
import { encryptPiiFields } from "../../../../security/pii-encrypt";
|
|
4
|
-
import { BID_PII_FIELDS, OFFER_PII_FIELDS, } from "../../../../security/pii-schemas";
|
|
5
|
-
import { SANDBOX_COLLECTIONS } from "./testerSandboxCleanup";
|
|
6
|
-
// Direct imports from each defining module (not the seed-data barrel) —
|
|
7
|
-
// this is internal appkit code, not part of the package's public API surface.
|
|
8
|
-
import { categoriesTesterSeedData } from "../../../../features/tester/seed-data/categories-tester-seed-data";
|
|
9
|
-
import { storesTesterSeedData } from "../../../../features/tester/seed-data/stores-tester-seed-data";
|
|
10
|
-
import { productsTesterSeedData } from "../../../../features/tester/seed-data/products-tester-seed-data";
|
|
11
|
-
import { blogTesterSeedData } from "../../../../features/tester/seed-data/blog-tester-seed-data";
|
|
12
|
-
import { eventsTesterSeedData } from "../../../../features/tester/seed-data/events-tester-seed-data";
|
|
13
|
-
import { offersTesterSeedData } from "../../../../features/tester/seed-data/offers-tester-seed-data";
|
|
14
|
-
import { bidsTesterSeedData } from "../../../../features/tester/seed-data/bids-tester-seed-data";
|
|
15
|
-
/**
|
|
16
|
-
* PII per sandbox collection — the same map `seed-cli.mjs` applies.
|
|
17
|
-
*
|
|
18
|
-
* This job wrote every document with a bare `batch.set(...)`, while its own
|
|
19
|
-
* comment claimed to mirror seed-cli's loadGeneric. loadGeneric ENCRYPTS;
|
|
20
|
-
* this did not. So the 4-hourly refresh re-seeded sandbox PII in plaintext and
|
|
21
|
-
* quietly undid the backfill for those fixtures — caught by reading the raw
|
|
22
|
-
* documents after a deploy, where `bids` had 37 encrypted rows and one
|
|
23
|
-
* plaintext one that had not existed at backfill time.
|
|
24
|
-
*
|
|
25
|
-
* A scheduled job writing straight to Firestore bypasses `applyWriteHooks`
|
|
26
|
-
* exactly like the hand-rolled repository overrides did. Same defect, one
|
|
27
|
-
* level up.
|
|
28
|
-
*/
|
|
29
|
-
const SANDBOX_PII = {
|
|
30
|
-
offers: OFFER_PII_FIELDS,
|
|
31
|
-
bids: BID_PII_FIELDS,
|
|
32
|
-
products: ["sellerName", "sellerEmail"],
|
|
33
|
-
};
|
|
34
|
-
function encryptForSandbox(collection, data) {
|
|
35
|
-
const fields = SANDBOX_PII[collection];
|
|
36
|
-
return fields ? encryptPiiFields(data, [...fields]) : data;
|
|
37
|
-
}
|
|
38
|
-
const SEED_BY_COLLECTION = {
|
|
39
|
-
categories: categoriesTesterSeedData,
|
|
40
|
-
stores: storesTesterSeedData,
|
|
41
|
-
products: productsTesterSeedData,
|
|
42
|
-
blogPosts: blogTesterSeedData,
|
|
43
|
-
events: eventsTesterSeedData,
|
|
44
|
-
offers: offersTesterSeedData,
|
|
45
|
-
};
|
|
46
|
-
const BID_CHUNK_SIZE = 30; // Firestore `in` query cap
|
|
47
|
-
/**
|
|
48
|
-
* Reverts every live tester-sandbox fixture back to its canonical seeded
|
|
49
|
-
* shape (undoing edits made through admin/seller forms) and prunes any
|
|
50
|
-
* isTestData doc that isn't one of the known fixtures (something a tester
|
|
51
|
-
* created during QA, e.g. a cloned product). Runs every 4 hours — distinct
|
|
52
|
-
* from `runTesterSandboxCleanup`, which only deletes docs whose 7-day
|
|
53
|
-
* `testDataExpiresAt` TTL has actually lapsed and never touches still-live
|
|
54
|
-
* docs' fields. Scoped to the tester sandbox only (`isTestData: true`) —
|
|
55
|
-
* the permanent Beyblade catalog real visitors browse is never touched.
|
|
56
|
-
*/
|
|
57
|
-
export async function runTesterSandboxRefresh(ctx) {
|
|
58
|
-
let reverted = 0;
|
|
59
|
-
let deletedExtras = 0;
|
|
60
|
-
for (const collection of SANDBOX_COLLECTIONS) {
|
|
61
|
-
const seedDocs = SEED_BY_COLLECTION[collection] ?? [];
|
|
62
|
-
const validIds = new Set(seedDocs.map((d) => d.id).filter((id) => Boolean(id)));
|
|
63
|
-
if (seedDocs.length > 0) {
|
|
64
|
-
const batch = ctx.db.batch();
|
|
65
|
-
for (const doc of seedDocs) {
|
|
66
|
-
const { id, ...data } = doc;
|
|
67
|
-
if (!id)
|
|
68
|
-
continue;
|
|
69
|
-
// merge:true mirrors seed-cli.mjs's loadGeneric — the seed payload
|
|
70
|
-
// carries every canonical field, so this fully overwrites anything
|
|
71
|
-
// a tester edited through the UI.
|
|
72
|
-
batch.set(ctx.db.collection(collection).doc(id), encryptForSandbox(collection, data), { merge: true });
|
|
73
|
-
reverted++;
|
|
74
|
-
}
|
|
75
|
-
await batch.commit();
|
|
76
|
-
}
|
|
77
|
-
const testDataRefs = await getTestDataRefs(ctx.db, collection, null);
|
|
78
|
-
const extraRefs = testDataRefs.filter((ref) => !validIds.has(ref.id));
|
|
79
|
-
deletedExtras += await batchDelete(ctx, extraRefs);
|
|
80
|
-
}
|
|
81
|
-
// Bids aren't isTestData-flagged themselves — scoped by productId
|
|
82
|
-
// referencing a sandbox product instead, mirroring runTesterSandboxCleanup's
|
|
83
|
-
// cascade convention.
|
|
84
|
-
const sandboxProductIds = (SEED_BY_COLLECTION.products ?? [])
|
|
85
|
-
.map((d) => d.id)
|
|
86
|
-
.filter((id) => Boolean(id));
|
|
87
|
-
const validBidIds = new Set(bidsTesterSeedData.map((d) => d.id).filter((id) => Boolean(id)));
|
|
88
|
-
if (bidsTesterSeedData.length > 0) {
|
|
89
|
-
const bidBatch = ctx.db.batch();
|
|
90
|
-
for (const bid of bidsTesterSeedData) {
|
|
91
|
-
const { id, ...data } = bid;
|
|
92
|
-
if (!id)
|
|
93
|
-
continue;
|
|
94
|
-
bidBatch.set(ctx.db.collection("bids").doc(id), encryptForSandbox("bids", data), { merge: true });
|
|
95
|
-
reverted++;
|
|
96
|
-
}
|
|
97
|
-
await bidBatch.commit();
|
|
98
|
-
}
|
|
99
|
-
const extraBidRefs = [];
|
|
100
|
-
for (let i = 0; i < sandboxProductIds.length; i += BID_CHUNK_SIZE) {
|
|
101
|
-
const chunk = sandboxProductIds.slice(i, i + BID_CHUNK_SIZE);
|
|
102
|
-
if (chunk.length === 0)
|
|
103
|
-
continue;
|
|
104
|
-
const snap = await ctx.db.collection("bids").where(BID_FIELDS.PRODUCT_ID, "in", chunk).get();
|
|
105
|
-
for (const doc of snap.docs) {
|
|
106
|
-
if (!validBidIds.has(doc.id))
|
|
107
|
-
extraBidRefs.push(doc.ref);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
deletedExtras += await batchDelete(ctx, extraBidRefs);
|
|
111
|
-
ctx.logger.info("Tester sandbox refresh complete", { reverted, deletedExtras });
|
|
112
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { SectionType } from "../../../../features/homepage/schemas/firestore";
|
|
2
|
-
import type { FeatureFlagKey } from "../../../../features/admin/schemas/firestore";
|
|
3
|
-
/** Section types that require a feature flag to be enabled to show on the homepage. */
|
|
4
|
-
export declare const SECTION_FEATURE_GATE: Partial<Record<SectionType, FeatureFlagKey>>;
|
|
5
|
-
/**
|
|
6
|
-
* Drop sections whose feature flag is off.
|
|
7
|
-
*
|
|
8
|
-
* A section type with no gate entry always passes, and a MISSING flag value
|
|
9
|
-
* counts as enabled (`!== false`) — the flags map is partial, and a section
|
|
10
|
-
* must not vanish just because an admin has never opened Site Settings.
|
|
11
|
-
*/
|
|
12
|
-
export declare function filterSectionsByFeatureFlags<T extends {
|
|
13
|
-
type: string;
|
|
14
|
-
}>(sections: readonly T[], flags: Partial<Record<FeatureFlagKey, unknown>> | null | undefined): T[];
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* WHY: The only code in the repo that gated homepage sections on
|
|
3
|
-
* `siteSettings.featureFlags` lived inside `getHomepageInitial`, a
|
|
4
|
-
* function with ZERO call sites — the live homepage
|
|
5
|
-
* (`MarketplaceHomepageView`) reads `homepageSectionsRepository`
|
|
6
|
-
* directly. So turning off the `auctions` flag hid auctions everywhere
|
|
7
|
-
* except the homepage, which kept advertising them.
|
|
8
|
-
*
|
|
9
|
-
* `getHomepageInitial` itself was deleted rather than wired up: its other
|
|
10
|
-
* unique calls, `findActiveAuctions()` / `findActivePreOrders()`, are
|
|
11
|
-
* unbounded `.get()` collection scans with no `.limit()`, which Rule #6
|
|
12
|
-
* forbids. The gate is the only part worth keeping, so it moved here.
|
|
13
|
-
*
|
|
14
|
-
* EXPORTS: SECTION_FEATURE_GATE, filterSectionsByFeatureFlags
|
|
15
|
-
*
|
|
16
|
-
* @tag domain:homepage
|
|
17
|
-
* @tag layer:shared
|
|
18
|
-
* @tag pattern:none
|
|
19
|
-
* @tag access:isomorphic
|
|
20
|
-
* @tag consumers:MarketplaceHomepageView
|
|
21
|
-
* @tag sideEffects:none
|
|
22
|
-
*/
|
|
23
|
-
/** Section types that require a feature flag to be enabled to show on the homepage. */
|
|
24
|
-
export const SECTION_FEATURE_GATE = {
|
|
25
|
-
auctions: "auctions",
|
|
26
|
-
"pre-orders": "preOrders",
|
|
27
|
-
events: "events",
|
|
28
|
-
"event-raffles": "events",
|
|
29
|
-
"prize-draws": "events",
|
|
30
|
-
"blog-articles": "blog",
|
|
31
|
-
};
|
|
32
|
-
/**
|
|
33
|
-
* Drop sections whose feature flag is off.
|
|
34
|
-
*
|
|
35
|
-
* A section type with no gate entry always passes, and a MISSING flag value
|
|
36
|
-
* counts as enabled (`!== false`) — the flags map is partial, and a section
|
|
37
|
-
* must not vanish just because an admin has never opened Site Settings.
|
|
38
|
-
*/
|
|
39
|
-
export function filterSectionsByFeatureFlags(sections,
|
|
40
|
-
// Not `Partial<Record<FeatureFlagKey, boolean>>`: two of the flag keys
|
|
41
|
-
// (`listingTypes`, `categoryTypes`) hold nested per-type objects rather than
|
|
42
|
-
// booleans. Neither is a gate key, so the `=== false` test below simply
|
|
43
|
-
// never fires for them.
|
|
44
|
-
flags) {
|
|
45
|
-
if (!flags)
|
|
46
|
-
return [...sections];
|
|
47
|
-
return sections.filter((section) => {
|
|
48
|
-
const flagKey = SECTION_FEATURE_GATE[section.type];
|
|
49
|
-
return !flagKey || flags[flagKey] !== false;
|
|
50
|
-
});
|
|
51
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
export declare const lotteryConfigFormSchema: z.ZodEffects<z.ZodObject<{
|
|
3
|
-
pricingMode: z.ZodEnum<["uniform", "variable"]>;
|
|
4
|
-
uniformPrice: z.ZodOptional<z.ZodNumber>;
|
|
5
|
-
slots: z.ZodArray<z.ZodObject<{
|
|
6
|
-
slotNumber: z.ZodNumber;
|
|
7
|
-
name: z.ZodOptional<z.ZodString>;
|
|
8
|
-
price: z.ZodOptional<z.ZodNumber>;
|
|
9
|
-
image: z.ZodOptional<z.ZodString>;
|
|
10
|
-
}, "strip", z.ZodTypeAny, {
|
|
11
|
-
slotNumber: number;
|
|
12
|
-
image?: string | undefined;
|
|
13
|
-
name?: string | undefined;
|
|
14
|
-
price?: number | undefined;
|
|
15
|
-
}, {
|
|
16
|
-
slotNumber: number;
|
|
17
|
-
image?: string | undefined;
|
|
18
|
-
name?: string | undefined;
|
|
19
|
-
price?: number | undefined;
|
|
20
|
-
}>, "many">;
|
|
21
|
-
}, "strip", z.ZodTypeAny, {
|
|
22
|
-
slots: {
|
|
23
|
-
slotNumber: number;
|
|
24
|
-
image?: string | undefined;
|
|
25
|
-
name?: string | undefined;
|
|
26
|
-
price?: number | undefined;
|
|
27
|
-
}[];
|
|
28
|
-
pricingMode: "uniform" | "variable";
|
|
29
|
-
uniformPrice?: number | undefined;
|
|
30
|
-
}, {
|
|
31
|
-
slots: {
|
|
32
|
-
slotNumber: number;
|
|
33
|
-
image?: string | undefined;
|
|
34
|
-
name?: string | undefined;
|
|
35
|
-
price?: number | undefined;
|
|
36
|
-
}[];
|
|
37
|
-
pricingMode: "uniform" | "variable";
|
|
38
|
-
uniformPrice?: number | undefined;
|
|
39
|
-
}>, {
|
|
40
|
-
slots: {
|
|
41
|
-
slotNumber: number;
|
|
42
|
-
image?: string | undefined;
|
|
43
|
-
name?: string | undefined;
|
|
44
|
-
price?: number | undefined;
|
|
45
|
-
}[];
|
|
46
|
-
pricingMode: "uniform" | "variable";
|
|
47
|
-
uniformPrice?: number | undefined;
|
|
48
|
-
}, {
|
|
49
|
-
slots: {
|
|
50
|
-
slotNumber: number;
|
|
51
|
-
image?: string | undefined;
|
|
52
|
-
name?: string | undefined;
|
|
53
|
-
price?: number | undefined;
|
|
54
|
-
}[];
|
|
55
|
-
pricingMode: "uniform" | "variable";
|
|
56
|
-
uniformPrice?: number | undefined;
|
|
57
|
-
}>;
|
|
58
|
-
export type LotteryConfigFormValues = z.infer<typeof lotteryConfigFormSchema>;
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* WHY: `LotteryAdminEditView` rendered `<Form>` with no schema. A lottery
|
|
3
|
-
* config is priced money: `uniformPrice` was
|
|
4
|
-
* `Math.round(parseFloat(x) * 100) / 100 || 0`, which turns any
|
|
5
|
-
* unparseable input into **zero** rather than an error — so a typo in the
|
|
6
|
-
* price box published a free lottery.
|
|
7
|
-
*
|
|
8
|
-
* `slots` is the other half: each row carries a `slotNumber`, a name and
|
|
9
|
-
* a price, and a duplicate slot number makes two buyers think they own
|
|
10
|
-
* the same slot.
|
|
11
|
-
*
|
|
12
|
-
* WHAT: The config contract.
|
|
13
|
-
*
|
|
14
|
-
* @tag domain:lottery
|
|
15
|
-
* @tag layer:schema
|
|
16
|
-
* @tag pattern:none
|
|
17
|
-
* @tag access:isomorphic
|
|
18
|
-
* @tag consumers:LotteryAdminEditView
|
|
19
|
-
* @tag sideEffects:none
|
|
20
|
-
*/
|
|
21
|
-
import { z } from "zod";
|
|
22
|
-
import { annotate } from "../../../../features/shell/field-ui-meta";
|
|
23
|
-
const slotSchema = z.object({
|
|
24
|
-
slotNumber: z.coerce.number().int().min(1, "Slot numbers start at 1."),
|
|
25
|
-
name: z.string().trim().max(120).optional(),
|
|
26
|
-
price: z.coerce.number().min(0, "A slot price cannot be negative.").optional(),
|
|
27
|
-
image: z.string().trim().optional(),
|
|
28
|
-
});
|
|
29
|
-
export const lotteryConfigFormSchema = z
|
|
30
|
-
.object({
|
|
31
|
-
pricingMode: annotate(z.enum(["uniform", "variable"]), {
|
|
32
|
-
section: "pricing", sectionLabel: "Pricing", sectionRequired: true, quick: true, order: 1, row: "pair",
|
|
33
|
-
}),
|
|
34
|
-
/*
|
|
35
|
-
* 🛑 Coerced with a real error, NOT `|| 0`. The previous
|
|
36
|
-
* `parseFloat(x) || 0` silently turned "12,00" or "twelve" into a free
|
|
37
|
-
* lottery — the failure was indistinguishable from deliberately pricing
|
|
38
|
-
* it at zero.
|
|
39
|
-
*/
|
|
40
|
-
uniformPrice: annotate(z.coerce
|
|
41
|
-
.number({ invalid_type_error: "Enter the slot price as a number." })
|
|
42
|
-
.min(0, "A price cannot be negative.")
|
|
43
|
-
.max(10000000, "That looks like a typo.")
|
|
44
|
-
.optional(), { section: "pricing", order: 2, row: "pair", kind: "number" }),
|
|
45
|
-
slots: annotate(z.array(slotSchema).min(1, "A lottery needs at least one slot.").max(1000), {
|
|
46
|
-
section: "slots", sectionLabel: "Slots", sectionRequired: true, order: 1, row: "full", kind: "list",
|
|
47
|
-
}),
|
|
48
|
-
})
|
|
49
|
-
.superRefine((v, ctx) => {
|
|
50
|
-
// Uniform pricing with no price is the free-lottery case the `|| 0` used
|
|
51
|
-
// to create silently.
|
|
52
|
-
if (v.pricingMode === "uniform" && (v.uniformPrice === undefined || v.uniformPrice <= 0)) {
|
|
53
|
-
ctx.addIssue({
|
|
54
|
-
code: z.ZodIssueCode.custom,
|
|
55
|
-
path: ["uniformPrice"],
|
|
56
|
-
message: "Set a slot price above zero, or switch to variable pricing.",
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
// Two buyers cannot own the same slot number.
|
|
60
|
-
const seen = new Set();
|
|
61
|
-
for (const [i, s] of v.slots.entries()) {
|
|
62
|
-
if (seen.has(s.slotNumber)) {
|
|
63
|
-
ctx.addIssue({
|
|
64
|
-
code: z.ZodIssueCode.custom,
|
|
65
|
-
path: ["slots", i, "slotNumber"],
|
|
66
|
-
message: `Slot ${s.slotNumber} appears more than once.`,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
seen.add(s.slotNumber);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Consumer app's flag names aren't known to appkit (they're project-specific,
|
|
3
|
-
* e.g. "PAYOUTS" | "COUPONS" | ...) — this stays a loose string-keyed map so
|
|
4
|
-
* appkit doesn't hardcode a consumer's roadmap. Cast the result where a
|
|
5
|
-
* consumer wants a narrower, project-specific union.
|
|
6
|
-
*/
|
|
7
|
-
export type FeatureFlagState = Record<string, boolean>;
|
|
8
|
-
/**
|
|
9
|
-
* Client-safe feature-flag check — `getFlag()`/`withFeatureGuard` are
|
|
10
|
-
* server-only, so `"use client"` components have no way to know a flag is
|
|
11
|
-
* off before firing a request against its (guarded) API. This hook fetches
|
|
12
|
-
* the public flag snapshot once (cached for the query's lifetime) so a
|
|
13
|
-
* component can skip the fetch and skip rendering entirely when disabled,
|
|
14
|
-
* instead of eating a 404 from the guarded route.
|
|
15
|
-
*
|
|
16
|
-
* `flags` defaults to an empty object while loading, so callers should treat
|
|
17
|
-
* a missing key as `false` (fail-closed) rather than assuming it's enabled.
|
|
18
|
-
*/
|
|
19
|
-
export declare function useFeatureFlags(): {
|
|
20
|
-
flags: FeatureFlagState;
|
|
21
|
-
isLoading: boolean;
|
|
22
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { useApiQuery } from "./useApiQuery";
|
|
3
|
-
import { apiClient } from "../../http";
|
|
4
|
-
import { API_ENDPOINTS } from "../../constants/api-endpoints";
|
|
5
|
-
/**
|
|
6
|
-
* Client-safe feature-flag check — `getFlag()`/`withFeatureGuard` are
|
|
7
|
-
* server-only, so `"use client"` components have no way to know a flag is
|
|
8
|
-
* off before firing a request against its (guarded) API. This hook fetches
|
|
9
|
-
* the public flag snapshot once (cached for the query's lifetime) so a
|
|
10
|
-
* component can skip the fetch and skip rendering entirely when disabled,
|
|
11
|
-
* instead of eating a 404 from the guarded route.
|
|
12
|
-
*
|
|
13
|
-
* `flags` defaults to an empty object while loading, so callers should treat
|
|
14
|
-
* a missing key as `false` (fail-closed) rather than assuming it's enabled.
|
|
15
|
-
*/
|
|
16
|
-
export function useFeatureFlags() {
|
|
17
|
-
const query = useApiQuery({
|
|
18
|
-
queryKey: ["feature-flags"],
|
|
19
|
-
queryFn: () => apiClient.get(API_ENDPOINTS.CONFIG.FEATURE_FLAGS),
|
|
20
|
-
staleTime: 60000,
|
|
21
|
-
silentBackgroundFailures: true,
|
|
22
|
-
});
|
|
23
|
-
return {
|
|
24
|
-
flags: query.data ?? {},
|
|
25
|
-
isLoading: query.isLoading,
|
|
26
|
-
};
|
|
27
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export interface AddressValidationFormData {
|
|
2
|
-
fullName: string;
|
|
3
|
-
phoneNumber: string;
|
|
4
|
-
pincode: string;
|
|
5
|
-
addressLine1: string;
|
|
6
|
-
addressLine2?: string;
|
|
7
|
-
landmark?: string;
|
|
8
|
-
city: string;
|
|
9
|
-
state: string;
|
|
10
|
-
addressType: string;
|
|
11
|
-
isDefault: boolean;
|
|
12
|
-
}
|
|
13
|
-
export interface AddressValidationOptions {
|
|
14
|
-
validatePhoneNumber?: (value: string) => boolean;
|
|
15
|
-
validatePostalCode?: (value: string) => boolean;
|
|
16
|
-
requiredFieldMessage?: string;
|
|
17
|
-
invalidPhoneMessage?: string;
|
|
18
|
-
invalidPostalCodeMessage?: string;
|
|
19
|
-
}
|
|
20
|
-
export declare function validateAddressForm(formData: AddressValidationFormData, options?: AddressValidationOptions): Record<string, string>;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { ERROR_MESSAGES } from "../../errors";
|
|
2
|
-
export function validateAddressForm(formData, options) {
|
|
3
|
-
const errors = {};
|
|
4
|
-
const requiredFieldMessage = options?.requiredFieldMessage ?? ERROR_MESSAGES.VALIDATION.REQUIRED_FIELD;
|
|
5
|
-
if (!formData.fullName?.trim()) {
|
|
6
|
-
errors.fullName = requiredFieldMessage;
|
|
7
|
-
}
|
|
8
|
-
if (!formData.phoneNumber?.trim()) {
|
|
9
|
-
errors.phoneNumber = requiredFieldMessage;
|
|
10
|
-
}
|
|
11
|
-
else if (options?.validatePhoneNumber &&
|
|
12
|
-
!options.validatePhoneNumber(formData.phoneNumber)) {
|
|
13
|
-
errors.phoneNumber =
|
|
14
|
-
options.invalidPhoneMessage ?? ERROR_MESSAGES.VALIDATION.INVALID_PHONE;
|
|
15
|
-
}
|
|
16
|
-
if (!formData.pincode?.trim()) {
|
|
17
|
-
errors.pincode = requiredFieldMessage;
|
|
18
|
-
}
|
|
19
|
-
else if (options?.validatePostalCode &&
|
|
20
|
-
!options.validatePostalCode(formData.pincode)) {
|
|
21
|
-
errors.pincode =
|
|
22
|
-
options.invalidPostalCodeMessage ??
|
|
23
|
-
ERROR_MESSAGES.VALIDATION.INVALID_INPUT;
|
|
24
|
-
}
|
|
25
|
-
if (!formData.addressLine1?.trim()) {
|
|
26
|
-
errors.addressLine1 = requiredFieldMessage;
|
|
27
|
-
}
|
|
28
|
-
if (!formData.city?.trim()) {
|
|
29
|
-
errors.city = requiredFieldMessage;
|
|
30
|
-
}
|
|
31
|
-
if (!formData.state?.trim()) {
|
|
32
|
-
errors.state = requiredFieldMessage;
|
|
33
|
-
}
|
|
34
|
-
return errors;
|
|
35
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
export interface ChatListLabels {
|
|
3
|
-
title?: string;
|
|
4
|
-
}
|
|
5
|
-
export interface ChatListProps {
|
|
6
|
-
labels?: ChatListLabels;
|
|
7
|
-
isLoading?: boolean;
|
|
8
|
-
hasItems: boolean;
|
|
9
|
-
renderLoading?: () => React.ReactNode;
|
|
10
|
-
renderEmptyState?: () => React.ReactNode;
|
|
11
|
-
renderList: () => React.ReactNode;
|
|
12
|
-
className?: string;
|
|
13
|
-
}
|
|
14
|
-
export declare function ChatList({ labels, isLoading, hasItems, renderLoading, renderEmptyState, renderList, className, }: ChatListProps): React.JSX.Element;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Div, Heading } from "../../../ui";
|
|
3
|
-
export function ChatList({ labels = {}, isLoading = false, hasItems, renderLoading, renderEmptyState, renderList, className = "", }) {
|
|
4
|
-
return (_jsxs(Div, { className: className, children: [labels.title && (_jsx(Heading, { level: 4, className: "mb-3", weight: "semibold", children: labels.title })), isLoading
|
|
5
|
-
? (renderLoading?.() ?? null)
|
|
6
|
-
: hasItems
|
|
7
|
-
? renderList()
|
|
8
|
-
: (renderEmptyState?.() ?? null)] }));
|
|
9
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
export interface ChatWindowLabels {
|
|
3
|
-
title?: string;
|
|
4
|
-
connected?: string;
|
|
5
|
-
disconnected?: string;
|
|
6
|
-
}
|
|
7
|
-
export interface ChatWindowProps {
|
|
8
|
-
labels?: ChatWindowLabels;
|
|
9
|
-
isConnected?: boolean;
|
|
10
|
-
isLoading?: boolean;
|
|
11
|
-
error?: React.ReactNode;
|
|
12
|
-
renderLoadingIndicator?: () => React.ReactNode;
|
|
13
|
-
renderMessages: () => React.ReactNode;
|
|
14
|
-
renderInput: () => React.ReactNode;
|
|
15
|
-
className?: string;
|
|
16
|
-
}
|
|
17
|
-
export declare function ChatWindow({ labels, isConnected, isLoading, error, renderLoadingIndicator, renderMessages, renderInput, className, }: ChatWindowProps): React.JSX.Element;
|