@mohasinac/appkit 4.30.6 → 4.30.8
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/constants/field-names.d.ts +2 -0
- package/dist/constants/field-names.js +2 -0
- package/dist/features/admin/components/AdminAddressesView.js +1 -1
- package/dist/features/admin/components/AdminAllEventEntriesView.js +1 -1
- package/dist/features/admin/components/AdminBidsView.js +1 -1
- package/dist/features/admin/components/AdminBlogView.js +1 -1
- package/dist/features/admin/components/AdminBrandsView.js +1 -1
- package/dist/features/admin/components/AdminCarouselView.js +3 -1
- package/dist/features/admin/components/AdminCartsView.js +1 -1
- package/dist/features/admin/components/AdminCategoriesView.js +1 -1
- package/dist/features/admin/components/AdminContactView.js +1 -1
- package/dist/features/admin/components/AdminCouponsView.js +1 -1
- package/dist/features/admin/components/AdminFeaturesView.js +1 -1
- package/dist/features/admin/components/AdminGroupedListingsView.js +1 -1
- package/dist/features/admin/components/AdminHistoryView.js +1 -1
- package/dist/features/admin/components/AdminNewsletterView.js +1 -1
- package/dist/features/admin/components/AdminOffersView.js +1 -1
- package/dist/features/admin/components/AdminOrdersView.js +11 -1
- package/dist/features/admin/components/AdminPaymentMethodsView.js +1 -1
- package/dist/features/admin/components/AdminReturnRequestsView.js +1 -1
- package/dist/features/admin/components/AdminSessionsView.js +1 -1
- package/dist/features/admin/components/AdminStoreAddressesView.js +1 -1
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +1 -1
- package/dist/features/admin/components/AdminWishlistsView.js +1 -1
- package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
- package/dist/features/events/components/AdminEventsView.js +1 -1
- package/dist/features/orders/repository/orders.repository.d.ts +15 -1
- package/dist/features/orders/repository/orders.repository.js +24 -2
- package/dist/features/orders/schemas/firestore.d.ts +3 -1
- package/dist/features/orders/schemas/firestore.js +1 -0
- package/dist/features/seller/components/SellerCouponsView.js +1 -1
- package/dist/features/seller/components/SellerGroupedListingsView.js +1 -1
- package/dist/features/seller/components/SellerOffersView.js +1 -1
- package/dist/features/seller/components/SellerOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPayoutsView.js +1 -1
- package/dist/features/seller/components/SellerStoreCategoriesView.js +1 -1
- package/dist/features/shipments/components/AdminShipmentsView.js +1 -1
- package/dist/features/tester/components/AdminTesterFeedbackListView.js +1 -1
- package/dist/seed/_helpers/search-txt-wrappers.d.ts +2 -0
- package/dist/seed/_helpers/search-txt-wrappers.js +4 -1
- package/dist/seed/orders-seed-data.js +4 -1
- package/dist/utils/search-txt-builders.d.ts +11 -0
- package/dist/utils/search-txt-builders.js +17 -0
- package/package.json +1 -1
|
@@ -184,6 +184,8 @@ export declare const PRODUCT_FIELDS: {
|
|
|
184
184
|
};
|
|
185
185
|
export declare const PRODUCT_STATUS_TRANSITIONS: Record<string, readonly string[]>;
|
|
186
186
|
export declare const ORDER_FIELDS: {
|
|
187
|
+
/** Word-prefix search tokens. Derived on write by `buildSearchTxtFor`. */
|
|
188
|
+
readonly SEARCH_TXT: "searchTxt";
|
|
187
189
|
readonly ID: "id";
|
|
188
190
|
readonly PRODUCT_ID: "productId";
|
|
189
191
|
readonly PRODUCT_TITLE: "productTitle";
|
|
@@ -210,6 +210,8 @@ export const PRODUCT_STATUS_TRANSITIONS = {
|
|
|
210
210
|
// ORDER FIELDS
|
|
211
211
|
// ============================================================================
|
|
212
212
|
export const ORDER_FIELDS = {
|
|
213
|
+
/** Word-prefix search tokens. Derived on write by `buildSearchTxtFor`. */
|
|
214
|
+
SEARCH_TXT: "searchTxt",
|
|
213
215
|
ID: "id",
|
|
214
216
|
PRODUCT_ID: "productId",
|
|
215
217
|
PRODUCT_TITLE: "productTitle",
|
|
@@ -51,7 +51,7 @@ function buildAddressConfig(onAction) {
|
|
|
51
51
|
return {
|
|
52
52
|
portal: "admin",
|
|
53
53
|
title: "Address Management",
|
|
54
|
-
|
|
54
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
55
55
|
emptyLabel: "No addresses found",
|
|
56
56
|
// URL-synced via filterKeys (2026-08-21). The ban-status chips used to
|
|
57
57
|
// live outside this config in local useState, so the selection was not in
|
|
@@ -32,7 +32,7 @@ export function AdminAllEventEntriesView({ children, ...props }) {
|
|
|
32
32
|
const config = {
|
|
33
33
|
portal: "admin",
|
|
34
34
|
title: "Event Entries",
|
|
35
|
-
|
|
35
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
36
36
|
emptyLabel: "No entries found",
|
|
37
37
|
filterKeys: ["status"],
|
|
38
38
|
defaultSort: sortBy("submittedAt", "DESC"),
|
|
@@ -41,7 +41,7 @@ export function AdminBidsView({ children, ...props }) {
|
|
|
41
41
|
const config = {
|
|
42
42
|
portal: "admin",
|
|
43
43
|
title: "Bids",
|
|
44
|
-
|
|
44
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
45
45
|
emptyLabel: "No bids found",
|
|
46
46
|
filterKeys: ["status"],
|
|
47
47
|
defaultSort: sortBy("bidDate", "DESC"),
|
|
@@ -21,7 +21,7 @@ export function AdminBlogView({ children, ...props }) {
|
|
|
21
21
|
const config = {
|
|
22
22
|
portal: "admin",
|
|
23
23
|
title: "Blog Posts",
|
|
24
|
-
|
|
24
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
25
25
|
emptyLabel: "No blog posts found",
|
|
26
26
|
filterKeys: ["status", "isFeatured"],
|
|
27
27
|
defaultSort: sortBy("publishedAt", "DESC"),
|
|
@@ -33,7 +33,7 @@ const COLUMNS = [
|
|
|
33
33
|
const ADMIN_BRANDS_CONFIG = {
|
|
34
34
|
portal: "admin",
|
|
35
35
|
title: "Brands",
|
|
36
|
-
|
|
36
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
37
37
|
emptyLabel: "No brands found",
|
|
38
38
|
filterKeys: ["isActive"],
|
|
39
39
|
defaultSort: sortBy("order", "ASC"),
|
|
@@ -145,5 +145,7 @@ export function AdminCarouselView({ children, onBulkDelete, ...props }) {
|
|
|
145
145
|
] })),
|
|
146
146
|
},
|
|
147
147
|
];
|
|
148
|
-
return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput,
|
|
148
|
+
return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput,
|
|
149
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
150
|
+
onSearchChange: setSearchInput, onSearchCommit: commitSearch, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), _jsx(DataTable, { rows: displayRows, columns: columns, isLoading: isLoading, emptyLabel: "No carousel slides \u2014 use 'New slide' to add one", rowHrefTemplate: "/admin/carousel/{id}/edit" })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Status" }), _jsx(Row, { wrap: true, gap: "sm", children: [{ label: "All", value: "" }, { label: "Active", value: "true" }, { label: "Inactive", value: "false" }].map((opt) => (_jsx(Button, { variant: "ghost", type: "button", onClick: () => setPendingFilters((p) => ({ ...p, active: opt.value })), className: `rounded-full px-[var(--appkit-space-3)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium border transition-colors min-h-0 h-auto ${(pendingFilters.active || "") === opt.value ? "bg-primary text-white border-primary" : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-surface-hover"}`, children: opt.label }, opt.label))) })] }) }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Carousel Slide", message: "Delete this carousel slide? This cannot be undone.", onConfirm: () => { deleteMutation.mutate(deleteTargetId); setDeleteTargetId(null); }, onClose: () => setDeleteTargetId(null), isDeleting: deleteMutation.isPending }))] }));
|
|
149
151
|
}
|
|
@@ -32,7 +32,7 @@ function toCartItemEntries(raw) {
|
|
|
32
32
|
const ADMIN_CARTS_CONFIG = {
|
|
33
33
|
portal: "admin",
|
|
34
34
|
title: "Carts",
|
|
35
|
-
|
|
35
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
36
36
|
emptyLabel: "No carts found",
|
|
37
37
|
filterKeys: ["ownership"],
|
|
38
38
|
defaultSort: sortBy("updatedAt", "DESC"),
|
|
@@ -32,7 +32,7 @@ const COLUMNS = [
|
|
|
32
32
|
const ADMIN_CATEGORIES_CONFIG = {
|
|
33
33
|
portal: "admin",
|
|
34
34
|
title: "Categories",
|
|
35
|
-
|
|
35
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
36
36
|
emptyLabel: "No categories found",
|
|
37
37
|
filterKeys: ["isActive", "isFeatured"],
|
|
38
38
|
defaultSort: sortBy("name", "ASC"),
|
|
@@ -49,7 +49,7 @@ export function AdminContactView({ children, onBulkMarkRead, onBulkArchive, onBu
|
|
|
49
49
|
const config = {
|
|
50
50
|
portal: "admin",
|
|
51
51
|
title: "Contact Submissions",
|
|
52
|
-
|
|
52
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
53
53
|
emptyLabel: "No contact submissions found",
|
|
54
54
|
filterKeys: ["status"],
|
|
55
55
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -52,7 +52,7 @@ export function AdminCouponsView({ children, onBulkArchive, onBulkDelete, ...pro
|
|
|
52
52
|
const config = {
|
|
53
53
|
portal: "admin",
|
|
54
54
|
title: "Coupons",
|
|
55
|
-
|
|
55
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
56
56
|
emptyLabel: "No coupons found",
|
|
57
57
|
filterKeys: ["type", "showExpired"],
|
|
58
58
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -41,7 +41,7 @@ export function AdminFeaturesView({ children, ...props }) {
|
|
|
41
41
|
const config = {
|
|
42
42
|
portal: "admin",
|
|
43
43
|
title: "Features",
|
|
44
|
-
|
|
44
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
45
45
|
emptyLabel: "No features found",
|
|
46
46
|
filterKeys: [],
|
|
47
47
|
defaultSort: sortBy("displayOrder", "ASC"),
|
|
@@ -52,7 +52,7 @@ export function AdminGroupedListingsView({ children, ...props }) {
|
|
|
52
52
|
const config = React.useMemo(() => ({
|
|
53
53
|
portal: "admin",
|
|
54
54
|
title: "Grouped Listings",
|
|
55
|
-
|
|
55
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
56
56
|
emptyLabel: "No grouped listings",
|
|
57
57
|
filterKeys: [],
|
|
58
58
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -15,7 +15,7 @@ import { DataListingView } from "./DataListingView";
|
|
|
15
15
|
const ADMIN_HISTORY_CONFIG = {
|
|
16
16
|
portal: "admin",
|
|
17
17
|
title: "History",
|
|
18
|
-
|
|
18
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
19
19
|
emptyLabel: "No user history records found",
|
|
20
20
|
filterKeys: [],
|
|
21
21
|
defaultSort: sortBy("updatedAt", "DESC"),
|
|
@@ -87,7 +87,7 @@ export function AdminNewsletterView({ children, onBulkUnsubscribe, ...props }) {
|
|
|
87
87
|
const config = {
|
|
88
88
|
portal: "admin",
|
|
89
89
|
title: "Newsletter",
|
|
90
|
-
|
|
90
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
91
91
|
emptyLabel: "No subscribers found",
|
|
92
92
|
filterKeys: ["status"],
|
|
93
93
|
defaultSort: sortBy("subscribedAt", "DESC"),
|
|
@@ -103,7 +103,7 @@ export function AdminOffersView({ children, ...props }) {
|
|
|
103
103
|
const config = {
|
|
104
104
|
portal: "admin",
|
|
105
105
|
title: "Offers",
|
|
106
|
-
|
|
106
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
107
107
|
emptyLabel: "No offers found",
|
|
108
108
|
filterKeys: ["status"],
|
|
109
109
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -85,7 +85,17 @@ export function AdminOrdersView({ children, ...props }) {
|
|
|
85
85
|
const config = {
|
|
86
86
|
portal: "admin",
|
|
87
87
|
title: "Orders",
|
|
88
|
-
|
|
88
|
+
// Restored: `orders` has searchTxt and `/api/admin/orders` reads `q`.
|
|
89
|
+
//
|
|
90
|
+
// Matches the product, the store and the tracking number — deliberately NOT
|
|
91
|
+
// the buyer's name or email. Those are ORDER_PII_FIELDS, encrypted at rest,
|
|
92
|
+
// and a searchTxt for a name IS that name re-encoded (D1). The placeholder
|
|
93
|
+
// says what it matches so it does not promise the buyer lookup it cannot do.
|
|
94
|
+
search: {
|
|
95
|
+
placeholder: "Product, store, or tracking number",
|
|
96
|
+
mode: "partial",
|
|
97
|
+
fields: ["productTitle", "storeName", "trackingNumber"],
|
|
98
|
+
},
|
|
89
99
|
emptyLabel: "No orders found",
|
|
90
100
|
filterKeys: ["status", "paymentReview"],
|
|
91
101
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -52,7 +52,7 @@ onOpen) {
|
|
|
52
52
|
return {
|
|
53
53
|
portal: "admin",
|
|
54
54
|
title: "Payment Methods",
|
|
55
|
-
|
|
55
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
56
56
|
emptyLabel: "No payment methods found",
|
|
57
57
|
// URL-synced via filterKeys (2026-08-21) — see the matching note in
|
|
58
58
|
// AdminAddressesView. banStatus is a query param, not a Sieve clause,
|
|
@@ -46,7 +46,7 @@ export function AdminReturnRequestsView(_props) {
|
|
|
46
46
|
const config = {
|
|
47
47
|
portal: "admin",
|
|
48
48
|
title: "Return Requests",
|
|
49
|
-
|
|
49
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
50
50
|
emptyLabel: "No return requests",
|
|
51
51
|
filterKeys: [],
|
|
52
52
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -41,7 +41,7 @@ export function AdminSessionsView({ children, ...props }) {
|
|
|
41
41
|
const config = {
|
|
42
42
|
portal: "admin",
|
|
43
43
|
title: "Sessions",
|
|
44
|
-
|
|
44
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
45
45
|
emptyLabel: "No sessions found",
|
|
46
46
|
filterKeys: ["isActive"],
|
|
47
47
|
defaultSort: sortBy("lastActivity", "DESC"),
|
|
@@ -9,7 +9,7 @@ import { RecordDetailModal } from "../../../ui";
|
|
|
9
9
|
const ADMIN_STORE_ADDRESSES_CONFIG = {
|
|
10
10
|
portal: "admin",
|
|
11
11
|
title: "Store Addresses",
|
|
12
|
-
|
|
12
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
13
13
|
emptyLabel: "No store addresses found",
|
|
14
14
|
filterKeys: [],
|
|
15
15
|
defaultSort: sortBy("storeId", "ASC"),
|
|
@@ -11,7 +11,7 @@ export function AdminSublistingCategoriesView({ onBulkDelete, }) {
|
|
|
11
11
|
const config = {
|
|
12
12
|
portal: "admin",
|
|
13
13
|
title: "Sub-listing Categories",
|
|
14
|
-
|
|
14
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
15
15
|
emptyLabel: "No sub-listing categories found",
|
|
16
16
|
filterKeys: [],
|
|
17
17
|
defaultSort: sortBy("name", "ASC"),
|
|
@@ -11,7 +11,7 @@ import { DataListingView } from "./DataListingView";
|
|
|
11
11
|
const ADMIN_WISHLISTS_CONFIG = {
|
|
12
12
|
portal: "admin",
|
|
13
13
|
title: "Wishlists",
|
|
14
|
-
|
|
14
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
15
15
|
emptyLabel: "No user wishlists found",
|
|
16
16
|
filterKeys: [],
|
|
17
17
|
defaultSort: sortBy("updatedAt", "DESC"),
|
|
@@ -41,7 +41,7 @@ export function AdminCatalogueApprovalsView() {
|
|
|
41
41
|
const config = {
|
|
42
42
|
portal: "admin",
|
|
43
43
|
title: "Catalogue Approvals",
|
|
44
|
-
|
|
44
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
45
45
|
emptyLabel: "No pending catalogue requests",
|
|
46
46
|
filterKeys: [],
|
|
47
47
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -33,7 +33,7 @@ export function AdminEventsView({ children, ...props }) {
|
|
|
33
33
|
const config = {
|
|
34
34
|
portal: "admin",
|
|
35
35
|
title: "Events",
|
|
36
|
-
|
|
36
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
37
37
|
emptyLabel: "No events found",
|
|
38
38
|
filterKeys: ["status", "type"],
|
|
39
39
|
defaultSort: sortBy("startsAt", "DESC"),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { JsonValue } from "../../../schemas/types";
|
|
1
2
|
import type { DocumentReference, WriteBatch } from "firebase-admin/firestore";
|
|
2
3
|
import type { FirebaseSieveResult, SieveModel } from "../../../providers/db-firebase";
|
|
3
4
|
import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
|
|
@@ -153,6 +154,10 @@ declare class OrderRepository extends BaseRepository<OrderDocument> {
|
|
|
153
154
|
listForSeller(productIds: string[], model: SieveModel): Promise<FirebaseSieveResult<OrderDocument>>;
|
|
154
155
|
listForUser(userId: string, model: SieveModel): Promise<FirebaseSieveResult<OrderDocument>>;
|
|
155
156
|
static readonly ADMIN_SIEVE_FIELDS: {
|
|
157
|
+
searchTxt: {
|
|
158
|
+
canFilter: boolean;
|
|
159
|
+
canSort: boolean;
|
|
160
|
+
};
|
|
156
161
|
id: {
|
|
157
162
|
canFilter: boolean;
|
|
158
163
|
canSort: boolean;
|
|
@@ -244,7 +249,16 @@ declare class OrderRepository extends BaseRepository<OrderDocument> {
|
|
|
244
249
|
canSort: boolean;
|
|
245
250
|
};
|
|
246
251
|
};
|
|
247
|
-
|
|
252
|
+
/**
|
|
253
|
+
* Derived on every write path via `applyWriteHooks`.
|
|
254
|
+
*
|
|
255
|
+
* Fed by product/store/tracking only — the three ORDER_PII_FIELDS and the
|
|
256
|
+
* shipping address are deliberately absent (see buildOrderSearchTxt).
|
|
257
|
+
*/
|
|
258
|
+
protected buildSearchTxtFor(data: Record<string, JsonValue>): string[] | null;
|
|
259
|
+
listAll(model: SieveModel, opts?: {
|
|
260
|
+
search?: string;
|
|
261
|
+
}): Promise<FirebaseSieveResult<OrderDocument>>;
|
|
248
262
|
/**
|
|
249
263
|
* Append one refund event to the order and mark it non-contestable.
|
|
250
264
|
* If `becomeRefunded` is true, also transitions the order status to "refunded".
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { buildOrderSearchTxt } from "../../../utils/search-txt-builders";
|
|
2
|
+
import { planSearchTxt, refineSearchTxt, emptySearchResult, } from "../../../utils/search-txt-query";
|
|
1
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
2
4
|
import { DatabaseError, NotFoundError } from "../../../errors";
|
|
3
5
|
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
@@ -286,11 +288,30 @@ class OrderRepository extends BaseRepository {
|
|
|
286
288
|
maxPageSize: 200,
|
|
287
289
|
});
|
|
288
290
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
+
/**
|
|
292
|
+
* Derived on every write path via `applyWriteHooks`.
|
|
293
|
+
*
|
|
294
|
+
* Fed by product/store/tracking only — the three ORDER_PII_FIELDS and the
|
|
295
|
+
* shipping address are deliberately absent (see buildOrderSearchTxt).
|
|
296
|
+
*/
|
|
297
|
+
buildSearchTxtFor(data) {
|
|
298
|
+
return buildOrderSearchTxt(data);
|
|
299
|
+
}
|
|
300
|
+
async listAll(model, opts) {
|
|
301
|
+
const plan = planSearchTxt(opts?.search);
|
|
302
|
+
// A search that yielded no usable term must return NOTHING, not everything.
|
|
303
|
+
if (plan.empty)
|
|
304
|
+
return emptySearchResult();
|
|
305
|
+
let baseQuery = this.getCollection();
|
|
306
|
+
if (plan.head) {
|
|
307
|
+
baseQuery = baseQuery.where(ORDER_FIELDS.SEARCH_TXT, "array-contains", plan.head);
|
|
308
|
+
}
|
|
309
|
+
const result = await this.sieveQuery(model, OrderRepository.ADMIN_SIEVE_FIELDS, {
|
|
310
|
+
baseQuery,
|
|
291
311
|
defaultPageSize: 50,
|
|
292
312
|
maxPageSize: 200,
|
|
293
313
|
});
|
|
314
|
+
return refineSearchTxt(result, plan.rest);
|
|
294
315
|
}
|
|
295
316
|
/**
|
|
296
317
|
* Append one refund event to the order and mark it non-contestable.
|
|
@@ -604,6 +625,7 @@ OrderRepository.SELLER_SIEVE_FIELDS = {
|
|
|
604
625
|
couponCode: { canFilter: true, canSort: false },
|
|
605
626
|
};
|
|
606
627
|
OrderRepository.ADMIN_SIEVE_FIELDS = {
|
|
628
|
+
searchTxt: { canFilter: true, canSort: false },
|
|
607
629
|
id: { canFilter: true, canSort: false },
|
|
608
630
|
userId: { canFilter: true, canSort: false },
|
|
609
631
|
userName: { canFilter: true, canSort: true },
|
|
@@ -240,6 +240,8 @@ export interface AppliedOrderDiscount {
|
|
|
240
240
|
storeId?: string;
|
|
241
241
|
}
|
|
242
242
|
export interface OrderDocument extends BaseDocument {
|
|
243
|
+
/** Word-prefix search tokens. Derived on write; never fed by a PII field. */
|
|
244
|
+
searchTxt?: string[];
|
|
243
245
|
productId: string;
|
|
244
246
|
productTitle: string;
|
|
245
247
|
userId: string;
|
|
@@ -522,7 +524,7 @@ export interface OrderPaymentRecord {
|
|
|
522
524
|
};
|
|
523
525
|
}
|
|
524
526
|
export declare const ORDER_COLLECTION: "orders";
|
|
525
|
-
export declare const ORDER_INDEXED_FIELDS: readonly ["userId", "productId", "storeId", "status", "paymentStatus", "payoutStatus", "shippingMethod", "orderDate", "createdAt", "assignedWorkerId", "pickedAt", "packedAt"];
|
|
527
|
+
export declare const ORDER_INDEXED_FIELDS: readonly ["searchTxt", "userId", "productId", "storeId", "status", "paymentStatus", "payoutStatus", "shippingMethod", "orderDate", "createdAt", "assignedWorkerId", "pickedAt", "packedAt"];
|
|
526
528
|
export declare const DEFAULT_ORDER_DATA: Partial<OrderDocument>;
|
|
527
529
|
export declare const ORDER_PUBLIC_FIELDS: readonly ["id", "productId", "productTitle", "quantity", "unitPrice", "totalPrice", "currency", "status", "paymentStatus", "shippingAddress", "trackingNumber", "notes", "orderDate", "shippingDate", "deliveryDate", "createdAt"];
|
|
528
530
|
export declare const ORDER_UPDATABLE_FIELDS: readonly ["notes", "shippingAddress"];
|
|
@@ -49,7 +49,7 @@ export function SellerCouponsView({ onCreateClick, onEditClick, onToggle, onDele
|
|
|
49
49
|
const config = {
|
|
50
50
|
portal: "seller",
|
|
51
51
|
title: "Coupons",
|
|
52
|
-
|
|
52
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
53
53
|
emptyLabel: "No coupons found — create your first coupon",
|
|
54
54
|
filterKeys: ["isActive"],
|
|
55
55
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -54,7 +54,7 @@ export function SellerGroupedListingsView({ onCreateClick, onEditClick, onDelete
|
|
|
54
54
|
const config = {
|
|
55
55
|
portal: "seller",
|
|
56
56
|
title: "Grouped Listings",
|
|
57
|
-
|
|
57
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
58
58
|
emptyLabel: "No grouped listings yet",
|
|
59
59
|
filterKeys: [],
|
|
60
60
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -22,7 +22,7 @@ export function SellerOffersView({ children, onAcceptOffer, onRejectOffer, onCou
|
|
|
22
22
|
const config = {
|
|
23
23
|
portal: "seller",
|
|
24
24
|
title: "Offers",
|
|
25
|
-
|
|
25
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
26
26
|
emptyLabel: "No offers received",
|
|
27
27
|
filterKeys: ["status"],
|
|
28
28
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -294,7 +294,7 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
294
294
|
const config = {
|
|
295
295
|
portal: "seller",
|
|
296
296
|
title: "Orders",
|
|
297
|
-
|
|
297
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
298
298
|
emptyLabel: "No orders yet",
|
|
299
299
|
filterKeys: ["status"],
|
|
300
300
|
defaultSort: DEFAULT_SORT,
|
|
@@ -95,7 +95,7 @@ export function SellerPayoutsView({ children, ...props }) {
|
|
|
95
95
|
const config = {
|
|
96
96
|
portal: "seller",
|
|
97
97
|
title: "Payouts",
|
|
98
|
-
|
|
98
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
99
99
|
emptyLabel: "No payouts found",
|
|
100
100
|
filterKeys: ["status", "showAllPayouts"],
|
|
101
101
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -64,7 +64,7 @@ export function SellerStoreCategoriesView({ onCreateClick, onEditClick, onDelete
|
|
|
64
64
|
const config = {
|
|
65
65
|
portal: "seller",
|
|
66
66
|
title: "Store Categories",
|
|
67
|
-
|
|
67
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
68
68
|
emptyLabel: "No categories yet — add your first storefront category",
|
|
69
69
|
filterKeys: [],
|
|
70
70
|
defaultSort: DEFAULT_SORT,
|
|
@@ -28,7 +28,7 @@ export function AdminShipmentsView({ children, ...props }) {
|
|
|
28
28
|
const config = {
|
|
29
29
|
portal: "admin",
|
|
30
30
|
title: "Procurement Shipments",
|
|
31
|
-
|
|
31
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
32
32
|
emptyLabel: "No shipments found",
|
|
33
33
|
filterKeys: ["status"],
|
|
34
34
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -60,7 +60,7 @@ export function AdminTesterFeedbackListView(_props) {
|
|
|
60
60
|
const config = {
|
|
61
61
|
portal: "admin",
|
|
62
62
|
title: "All Submissions",
|
|
63
|
-
|
|
63
|
+
// Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
|
|
64
64
|
emptyLabel: "No feedback submitted yet",
|
|
65
65
|
filterKeys: ["answer", "status"],
|
|
66
66
|
defaultSort: sortBy(TESTER_CHECKLIST_RESPONSE_FIELDS.CREATED_AT, "DESC"),
|
|
@@ -2,7 +2,9 @@ import type { StoreDocument } from "../../features/stores/schemas/firestore";
|
|
|
2
2
|
import type { EventDocument } from "../../features/events/schemas/firestore";
|
|
3
3
|
import type { BlogPostDocument } from "../../features/blog/schemas/firestore";
|
|
4
4
|
import type { ReviewDocument } from "../../features/reviews/schemas/firestore";
|
|
5
|
+
import type { OrderDocument } from "../../features/orders/schemas/firestore";
|
|
5
6
|
export declare function withStoreSearchTxt<T extends Partial<StoreDocument>>(p: T): T;
|
|
6
7
|
export declare function withEventSearchTxt<T extends Partial<EventDocument>>(p: T): T;
|
|
7
8
|
export declare function withBlogSearchTxt<T extends Partial<BlogPostDocument>>(p: T): T;
|
|
8
9
|
export declare function withReviewSearchTxt<T extends Partial<ReviewDocument>>(p: T): T;
|
|
10
|
+
export declare function withOrderSearchTxt<T extends Partial<OrderDocument>>(p: T): T;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildStoreSearchTxt, buildEventSearchTxt, buildBlogSearchTxt, buildReviewSearchTxt, } from "../../utils/search-txt-builders";
|
|
1
|
+
import { buildStoreSearchTxt, buildEventSearchTxt, buildBlogSearchTxt, buildReviewSearchTxt, buildOrderSearchTxt, } from "../../utils/search-txt-builders";
|
|
2
2
|
export function withStoreSearchTxt(p) {
|
|
3
3
|
return { ...p, searchTxt: buildStoreSearchTxt(p) };
|
|
4
4
|
}
|
|
@@ -11,3 +11,6 @@ export function withBlogSearchTxt(p) {
|
|
|
11
11
|
export function withReviewSearchTxt(p) {
|
|
12
12
|
return { ...p, searchTxt: buildReviewSearchTxt(p) };
|
|
13
13
|
}
|
|
14
|
+
export function withOrderSearchTxt(p) {
|
|
15
|
+
return { ...p, searchTxt: buildOrderSearchTxt(p) };
|
|
16
|
+
}
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
* @tag consumers:seed/index.ts,seed/runner.ts
|
|
37
37
|
* @tag sideEffects:none
|
|
38
38
|
*/
|
|
39
|
+
import { withOrderSearchTxt } from "./_helpers/search-txt-wrappers";
|
|
39
40
|
import { seedExtMedia } from "./_helpers/media";
|
|
40
41
|
const NOW = new Date();
|
|
41
42
|
const daysAgo = (n) => new Date(NOW.getTime() - n * 86400000);
|
|
@@ -723,4 +724,6 @@ export const ordersSeedData = [
|
|
|
723
724
|
// it used to be 48 for the two manual-payment orders, and now also absorbs
|
|
724
725
|
// the six acquisition-path orders.
|
|
725
726
|
...[..._rawOrdersSeedData, ...expandedOrders].slice(0, 42),
|
|
726
|
-
|
|
727
|
+
// Derived through the wrapper, never written per record — an inline literal
|
|
728
|
+
// is how five product seed files shipped their last fixture with no tokens.
|
|
729
|
+
].map(withOrderImages).map(withOrderSearchTxt);
|
|
@@ -3,6 +3,7 @@ import type { StoreDocument } from "../features/stores/schemas/firestore";
|
|
|
3
3
|
import type { EventDocument } from "../features/events/schemas/firestore";
|
|
4
4
|
import type { BlogPostDocument } from "../features/blog/schemas/firestore";
|
|
5
5
|
import type { ReviewDocument } from "../features/reviews/schemas/firestore";
|
|
6
|
+
import type { OrderDocument } from "../features/orders/schemas/firestore";
|
|
6
7
|
/** products — title, description, brand, taxonomy, condition, card/grading, specs. */
|
|
7
8
|
export declare function buildProductSearchTxt(p: Partial<ProductDocument>): string[];
|
|
8
9
|
/** stores — name, description, category. No payout details, no contact. */
|
|
@@ -21,3 +22,13 @@ export declare function buildEventSearchTxt(e: Partial<EventDocument>): string[]
|
|
|
21
22
|
export declare function buildBlogSearchTxt(b: Partial<BlogPostDocument>): string[];
|
|
22
23
|
/** reviews — title, comment, productTitle. `userName` is PII and excluded. */
|
|
23
24
|
export declare function buildReviewSearchTxt(r: Partial<ReviewDocument>): string[];
|
|
25
|
+
/**
|
|
26
|
+
* orders — what an admin or seller actually looks an order up BY: the product,
|
|
27
|
+
* the store, the tracking number.
|
|
28
|
+
*
|
|
29
|
+
* 🛑 `userName`, `userEmail` and `sellerEmail` are ORDER_PII_FIELDS and are
|
|
30
|
+
* absent. `shippingAddress` is absent too — it is not in that registry, but it
|
|
31
|
+
* is an address, and a searchTxt for an address IS that address re-encoded.
|
|
32
|
+
* Indexing any of them would undo the encryption they are stored under (D1).
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildOrderSearchTxt(o: Partial<OrderDocument>): string[];
|
|
@@ -84,3 +84,20 @@ export function buildBlogSearchTxt(b) {
|
|
|
84
84
|
export function buildReviewSearchTxt(r) {
|
|
85
85
|
return buildSearchTxt([r.title, stripHtml(r.comment), r.productTitle]);
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* orders — what an admin or seller actually looks an order up BY: the product,
|
|
89
|
+
* the store, the tracking number.
|
|
90
|
+
*
|
|
91
|
+
* 🛑 `userName`, `userEmail` and `sellerEmail` are ORDER_PII_FIELDS and are
|
|
92
|
+
* absent. `shippingAddress` is absent too — it is not in that registry, but it
|
|
93
|
+
* is an address, and a searchTxt for an address IS that address re-encoded.
|
|
94
|
+
* Indexing any of them would undo the encryption they are stored under (D1).
|
|
95
|
+
*/
|
|
96
|
+
export function buildOrderSearchTxt(o) {
|
|
97
|
+
return buildSearchTxt([
|
|
98
|
+
o.productTitle,
|
|
99
|
+
o.storeName,
|
|
100
|
+
o.trackingNumber,
|
|
101
|
+
(o.items ?? []).map((i) => i.productTitle),
|
|
102
|
+
]);
|
|
103
|
+
}
|