@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.
Files changed (169) hide show
  1. package/dist/features/seller/schemas/coupon-form.d.ts +37 -37
  2. package/dist/features/seller/schemas/coupon-form.js +36 -8
  3. package/dist/features/shell/SectionForm.js +44 -5
  4. package/dist/schemas/registry.d.ts +10 -10
  5. package/package.json +2 -2
  6. package/dist/_internal/server/features/classified/actions.d.ts +0 -12
  7. package/dist/_internal/server/features/classified/actions.js +0 -33
  8. package/dist/_internal/server/features/digital-code/actions.d.ts +0 -12
  9. package/dist/_internal/server/features/digital-code/actions.js +0 -33
  10. package/dist/_internal/server/features/homepage/data.d.ts +0 -18
  11. package/dist/_internal/server/features/homepage/data.js +0 -70
  12. package/dist/_internal/server/features/live/actions.d.ts +0 -6
  13. package/dist/_internal/server/features/live/actions.js +0 -27
  14. package/dist/_internal/server/features/messages/actions.d.ts +0 -6
  15. package/dist/_internal/server/features/messages/actions.js +0 -6
  16. package/dist/_internal/server/features/messages/data.d.ts +0 -10
  17. package/dist/_internal/server/features/messages/data.js +0 -24
  18. package/dist/_internal/server/features/messages/index.d.ts +0 -2
  19. package/dist/_internal/server/features/messages/index.js +0 -2
  20. package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +0 -12
  21. package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +0 -112
  22. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +0 -2
  23. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +0 -2
  24. package/dist/_internal/shared/features/homepage/section-gate.d.ts +0 -14
  25. package/dist/_internal/shared/features/homepage/section-gate.js +0 -51
  26. package/dist/_internal/shared/features/lottery/config-form.d.ts +0 -58
  27. package/dist/_internal/shared/features/lottery/config-form.js +0 -71
  28. package/dist/client/api/useFeatureFlags.d.ts +0 -22
  29. package/dist/client/api/useFeatureFlags.js +0 -27
  30. package/dist/features/account/address-validation.d.ts +0 -20
  31. package/dist/features/account/address-validation.js +0 -35
  32. package/dist/features/account/components/ChatList.d.ts +0 -14
  33. package/dist/features/account/components/ChatList.js +0 -9
  34. package/dist/features/account/components/ChatWindow.d.ts +0 -17
  35. package/dist/features/account/components/ChatWindow.js +0 -6
  36. package/dist/features/account/components/MessagesView.d.ts +0 -16
  37. package/dist/features/account/components/MessagesView.js +0 -5
  38. package/dist/features/account/components/UserOffersView.d.ts +0 -10
  39. package/dist/features/account/components/UserOffersView.js +0 -5
  40. package/dist/features/account/constants/addresses.d.ts +0 -13
  41. package/dist/features/account/constants/addresses.js +0 -43
  42. package/dist/features/account/hooks/useAddressForm.d.ts +0 -24
  43. package/dist/features/account/hooks/useAddressForm.js +0 -58
  44. package/dist/features/admin/actions/chat-actions.d.ts +0 -29
  45. package/dist/features/admin/actions/chat-actions.js +0 -101
  46. package/dist/features/admin/components/AdminFeatureFlagsView.d.ts +0 -9
  47. package/dist/features/admin/components/AdminFeatureFlagsView.js +0 -109
  48. package/dist/features/admin/components/AdminSiteView.d.ts +0 -10
  49. package/dist/features/admin/components/AdminSiteView.js +0 -52
  50. package/dist/features/admin/hooks/useChat.d.ts +0 -24
  51. package/dist/features/admin/hooks/useChat.js +0 -100
  52. package/dist/features/admin/permission-map.d.ts +0 -3
  53. package/dist/features/admin/permission-map.js +0 -26
  54. package/dist/features/admin/repository/chat.repository.d.ts +0 -50
  55. package/dist/features/admin/repository/chat.repository.js +0 -223
  56. package/dist/features/auth/actions/password-change-otp-actions.d.ts +0 -18
  57. package/dist/features/auth/actions/password-change-otp-actions.js +0 -88
  58. package/dist/features/auth/actions/realtime-token-actions.d.ts +0 -11
  59. package/dist/features/auth/actions/realtime-token-actions.js +0 -38
  60. package/dist/features/auth/password-change-otp.d.ts +0 -34
  61. package/dist/features/auth/password-change-otp.js +0 -56
  62. package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +0 -12
  63. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +0 -49
  64. package/dist/features/events/api/route.d.ts +0 -12
  65. package/dist/features/events/api/route.js +0 -90
  66. package/dist/features/faq/api/route.d.ts +0 -11
  67. package/dist/features/faq/api/route.js +0 -52
  68. package/dist/features/faq/hooks/useFAQs.d.ts +0 -22
  69. package/dist/features/faq/hooks/useFAQs.js +0 -41
  70. package/dist/features/media/upload/video-poster.test.d.ts +0 -1
  71. package/dist/features/media/upload/video-poster.test.js +0 -42
  72. package/dist/features/messages/actions/index.d.ts +0 -2
  73. package/dist/features/messages/actions/index.js +0 -2
  74. package/dist/features/messages/actions/messages-actions.d.ts +0 -15
  75. package/dist/features/messages/actions/messages-actions.js +0 -33
  76. package/dist/features/messages/actions/ping-rtdb.d.ts +0 -2
  77. package/dist/features/messages/actions/ping-rtdb.js +0 -26
  78. package/dist/features/messages/hooks/useConversation.d.ts +0 -22
  79. package/dist/features/messages/hooks/useConversation.js +0 -155
  80. package/dist/features/messages/hooks/useConversations.d.ts +0 -15
  81. package/dist/features/messages/hooks/useConversations.js +0 -104
  82. package/dist/features/messages/index.d.ts +0 -6
  83. package/dist/features/messages/index.js +0 -4
  84. package/dist/features/messages/realtime.d.ts +0 -32
  85. package/dist/features/messages/realtime.js +0 -39
  86. package/dist/features/messages/repository/conversations.repository.d.ts +0 -71
  87. package/dist/features/messages/repository/conversations.repository.js +0 -284
  88. package/dist/features/messages/schemas/firestore.d.ts +0 -34
  89. package/dist/features/messages/schemas/firestore.js +0 -16
  90. package/dist/features/messages/schemas/index.d.ts +0 -177
  91. package/dist/features/messages/schemas/index.js +0 -35
  92. package/dist/features/messages/server.d.ts +0 -6
  93. package/dist/features/messages/server.js +0 -6
  94. package/dist/features/products/components/MakeOfferForm.d.ts +0 -20
  95. package/dist/features/products/components/MakeOfferForm.js +0 -9
  96. package/dist/features/reviews/api/route.d.ts +0 -21
  97. package/dist/features/reviews/api/route.js +0 -174
  98. package/dist/features/seller/api/offers/route.d.ts +0 -17
  99. package/dist/features/seller/api/offers/route.js +0 -63
  100. package/dist/features/seller/components/SellerAuctionsView.d.ts +0 -9
  101. package/dist/features/seller/components/SellerAuctionsView.js +0 -113
  102. package/dist/features/seller/components/SellerOffersPanel.d.ts +0 -21
  103. package/dist/features/seller/components/SellerOffersPanel.js +0 -160
  104. package/dist/features/seller/components/SellerPreOrdersView.d.ts +0 -6
  105. package/dist/features/seller/components/SellerPreOrdersView.js +0 -148
  106. package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +0 -6
  107. package/dist/features/seller/components/SellerPrizeDrawsView.js +0 -152
  108. package/dist/features/seller/permission-map.d.ts +0 -5
  109. package/dist/features/seller/permission-map.js +0 -17
  110. package/dist/features/shell/StepForm.d.ts +0 -80
  111. package/dist/features/shell/StepForm.js +0 -136
  112. package/dist/features/stores/api/route.d.ts +0 -12
  113. package/dist/features/stores/api/route.js +0 -110
  114. package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.d.ts +0 -2
  115. package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.js +0 -33
  116. package/dist/features/tester/seed-data/authored/account-auth__testing-program.d.ts +0 -2
  117. package/dist/features/tester/seed-data/authored/account-auth__testing-program.js +0 -138
  118. package/dist/features/tester/seed-data/bids-tester-seed-data.d.ts +0 -2
  119. package/dist/features/tester/seed-data/bids-tester-seed-data.js +0 -37
  120. package/dist/features/tester/seed-data/blog-tester-seed-data.d.ts +0 -2
  121. package/dist/features/tester/seed-data/blog-tester-seed-data.js +0 -40
  122. package/dist/features/tester/seed-data/carts-tester-seed-data.d.ts +0 -3
  123. package/dist/features/tester/seed-data/carts-tester-seed-data.js +0 -70
  124. package/dist/features/tester/seed-data/categories-tester-seed-data.d.ts +0 -2
  125. package/dist/features/tester/seed-data/categories-tester-seed-data.js +0 -218
  126. package/dist/features/tester/seed-data/coupons-tester-seed-data.d.ts +0 -2
  127. package/dist/features/tester/seed-data/coupons-tester-seed-data.js +0 -76
  128. package/dist/features/tester/seed-data/events-tester-seed-data.d.ts +0 -2
  129. package/dist/features/tester/seed-data/events-tester-seed-data.js +0 -193
  130. package/dist/features/tester/seed-data/offers-tester-seed-data.d.ts +0 -2
  131. package/dist/features/tester/seed-data/offers-tester-seed-data.js +0 -191
  132. package/dist/features/tester/seed-data/orders-tester-seed-data.d.ts +0 -2
  133. package/dist/features/tester/seed-data/orders-tester-seed-data.js +0 -329
  134. package/dist/features/tester/seed-data/products-tester-seed-data.d.ts +0 -2
  135. package/dist/features/tester/seed-data/products-tester-seed-data.js +0 -815
  136. package/dist/features/tester/seed-data/stores-tester-seed-data.d.ts +0 -2
  137. package/dist/features/tester/seed-data/stores-tester-seed-data.js +0 -79
  138. package/dist/features/wishlist/components/WishlistToggleButton.d.ts +0 -12
  139. package/dist/features/wishlist/components/WishlistToggleButton.js +0 -36
  140. package/dist/monitoring/analytics.d.ts +0 -80
  141. package/dist/monitoring/analytics.js +0 -176
  142. package/dist/next/middleware/pii-redactor.d.ts +0 -32
  143. package/dist/next/middleware/pii-redactor.js +0 -84
  144. package/dist/providers/db-firebase/realtime.d.ts +0 -42
  145. package/dist/providers/db-firebase/realtime.js +0 -138
  146. package/dist/security/rbac/Can.d.ts +0 -31
  147. package/dist/security/rbac/Can.js +0 -22
  148. package/dist/security/rbac/default-roles.d.ts +0 -2
  149. package/dist/security/rbac/default-roles.js +0 -83
  150. package/dist/security/rbac/hook.d.ts +0 -20
  151. package/dist/security/rbac/hook.js +0 -30
  152. package/dist/security/rbac/index.d.ts +0 -8
  153. package/dist/security/rbac/index.js +0 -6
  154. package/dist/security/rbac/middleware.d.ts +0 -51
  155. package/dist/security/rbac/middleware.js +0 -51
  156. package/dist/security/rbac/resolver.d.ts +0 -5
  157. package/dist/security/rbac/resolver.js +0 -38
  158. package/dist/security/rbac/server.d.ts +0 -24
  159. package/dist/security/rbac/server.js +0 -42
  160. package/dist/security/rbac/types.d.ts +0 -22
  161. package/dist/security/rbac/types.js +0 -2
  162. package/dist/seed/conversations-seed-data.d.ts +0 -2
  163. package/dist/seed/conversations-seed-data.js +0 -292
  164. package/dist/utils/filter.helper.d.ts +0 -18
  165. package/dist/utils/filter.helper.js +0 -21
  166. package/dist/utils/pagination.helper.d.ts +0 -23
  167. package/dist/utils/pagination.helper.js +0 -24
  168. package/dist/utils/search-tokens.d.ts +0 -2
  169. package/dist/utils/search-tokens.js +0 -21
@@ -1,136 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useCallback, useContext, useEffect, useMemo, useState } from "react";
4
- import { Check } from "lucide-react";
5
- import { Button } from "../../ui/components/Button";
6
- import { classNames } from "../../ui/style.helper";
7
- import { Div, Nav, Row, Span, Stack, Text, useToast } from "../../ui";
8
- import { normalizeError } from "../../errors/normalize";
9
- import { FormSchemaContext } from "./FormShell";
10
- /** Standalone step nav bar — use as `renderBottomBar` in FormShell. */
11
- export function StepFormActions({ currentStep, totalSteps, onNext, onPrev, completeLabel = "Publish Now", isLoading = false, disabled = false, }) {
12
- const isFirst = currentStep === 0;
13
- const isLast = currentStep === totalSteps - 1;
14
- const nextStepLabel = `Next →`;
15
- return (_jsxs(Row, { paddingX: "x-5", className: "border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", padding: "y-sm", align: "center", justify: "between", gap: "sm", children: [_jsx(_Fragment, { children: !isFirst && onPrev && (_jsx(Button, { variant: "outline", size: "sm", onClick: onPrev, disabled: disabled || isLoading, children: "\u2190 Back" })) }), _jsxs(Row, { align: "center", gap: "3", children: [_jsxs(Span, { size: "xs", color: "muted", children: [currentStep + 1, " / ", totalSteps] }), _jsx(Button, { variant: "primary", size: "sm", onClick: onNext, disabled: disabled || isLoading, isLoading: isLoading && isLast, children: isLast ? completeLabel : nextStepLabel })] })] }));
16
- }
17
- /** Step indicator bar shown above step content. */
18
- export function StepIndicator({ steps, currentStep, onStepClick, stepErrors, }) {
19
- return (_jsx(Nav, { "aria-label": "Form steps", layout: "flex", gap: "none", className: "mb-6 overflow-x-auto", children: steps.map((step, i) => {
20
- const isDone = i < currentStep;
21
- const isActive = i === currentStep;
22
- const hasError = stepErrors?.[i] === true;
23
- return (_jsxs(Row, { className: "flex-shrink-0", align: "center", gap: "none", children: [_jsxs(Button, { variant: "ghost", type: "button", onClick: () => onStepClick?.(i), gap: "sm", rounded: "lg", paddingX: "sm", paddingY: "xs", textSize: "sm", className: classNames("cursor-pointer", isActive
24
- ? "font-semibold text-[var(--appkit-color-primary)]"
25
- : isDone
26
- ? "text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]"
27
- : "text-[var(--appkit-color-text-faint)] hover:text-[var(--appkit-color-text-muted)]"), children: [_jsxs(Span, { className: "relative flex-shrink-0", children: [hasError && (_jsx(Span, { className: "absolute -top-0.5 -right-0.5 w-2 h-2 bg-[var(--appkit-color-error)] z-10", rounded: "full", "aria-label": "This step has errors" })), _jsx(Span, { className: classNames("flex-shrink-0 w-6 h-6 rounded-full flex items-center justify-center text-[length:var(--appkit-text-xs)] font-bold border-2", hasError
28
- ? "border-[var(--appkit-color-error)] bg-[var(--appkit-color-error)] text-white"
29
- : isActive
30
- ? "border-[var(--appkit-color-primary)] bg-[var(--appkit-color-primary)] text-white"
31
- : isDone
32
- ? "border-[var(--appkit-color-primary)] bg-[var(--appkit-color-primary)] text-white"
33
- : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-faint)]"), children: isDone && !hasError ? _jsx(Check, { className: "w-3 h-3" }) : hasError ? "!" : i + 1 })] }), _jsx(Span, { className: "hidden sm:inline", children: step.label })] }), i < steps.length - 1 && (_jsx(Div, { className: classNames("w-6 h-px flex-shrink-0 mx-1", i < currentStep
34
- ? "bg-[var(--appkit-color-primary)]"
35
- : "bg-[var(--appkit-color-border)]") }))] }, i));
36
- }) }));
37
- }
38
- export function StepForm({ steps, values, onChange, onComplete, completeLabel = "Publish Now", isLoading = false, formId, currentStep, onStepChange, hideActions = false, stepErrors, schema, onValidationChange, }) {
39
- const { showToast } = useToast();
40
- const inheritedSchema = useContext(FormSchemaContext);
41
- const [fieldErrors, setFieldErrors] = useState({});
42
- const [stepError, setStepError] = useState(null);
43
- const fieldToStepIndex = useMemo(() => {
44
- const map = {};
45
- steps.forEach((step, i) => {
46
- step.fields?.forEach((field) => { map[field] = i; });
47
- });
48
- return map;
49
- }, [steps]);
50
- const runValidation = useCallback((nextValues) => {
51
- const activeSchema = steps[currentStep]?.schema ?? schema ?? inheritedSchema;
52
- if (!activeSchema)
53
- return;
54
- // audit-unvalidated-safeparse-ok: bulk-replaces fieldErrors from the full
55
- // parse result (clears fields that just became valid); applyZodIssues'
56
- // incremental setFieldError-per-issue can't do that.
57
- const parsed = activeSchema.safeParse(nextValues);
58
- if (parsed.success) {
59
- setFieldErrors({});
60
- onValidationChange?.({}, fieldToStepIndex);
61
- return;
62
- }
63
- const next = {};
64
- for (const issue of parsed.error.issues) {
65
- if (!issue.path || issue.path.length === 0)
66
- continue;
67
- next[issue.path.map(String).join(".")] = issue.message;
68
- }
69
- setFieldErrors(next);
70
- onValidationChange?.(next, fieldToStepIndex);
71
- }, [steps, currentStep, schema, inheritedSchema, fieldToStepIndex, onValidationChange]);
72
- // Re-run validation whenever the active step or schema changes, so
73
- // switching steps (or arriving with pre-filled values) shows accurate
74
- // live errors immediately rather than only after the next keystroke.
75
- useEffect(() => {
76
- runValidation(values);
77
- // eslint-disable-next-line react-hooks/exhaustive-deps
78
- }, [currentStep, steps[currentStep]?.schema, schema, inheritedSchema]);
79
- const handleFieldChange = useCallback((partial) => {
80
- const merged = { ...values, ...partial };
81
- onChange(partial);
82
- runValidation(merged);
83
- }, [values, onChange, runValidation]);
84
- // Persist step to localStorage if formId provided
85
- useEffect(() => {
86
- if (formId)
87
- localStorage.setItem(`stepform:${formId}`, String(currentStep));
88
- }, [formId, currentStep]);
89
- // Restore step from localStorage on mount
90
- useEffect(() => {
91
- if (!formId)
92
- return;
93
- const saved = localStorage.getItem(`stepform:${formId}`);
94
- if (saved !== null) {
95
- const n = Number(saved);
96
- if (!isNaN(n) && n >= 0 && n < steps.length)
97
- onStepChange(n);
98
- }
99
- // eslint-disable-next-line react-hooks/exhaustive-deps
100
- }, [formId]);
101
- const goNext = useCallback(async () => {
102
- const step = steps[currentStep];
103
- if (step?.validate) {
104
- const err = step.validate(values);
105
- if (err) {
106
- setStepError(err);
107
- return;
108
- }
109
- }
110
- setStepError(null);
111
- if (currentStep < steps.length - 1) {
112
- onStepChange(currentStep + 1);
113
- }
114
- else {
115
- try {
116
- await onComplete();
117
- }
118
- catch (err) {
119
- void normalizeError(err);
120
- showToast(err instanceof Error ? err.message : "Something went wrong.", "error");
121
- }
122
- }
123
- }, [currentStep, steps, values, onComplete, onStepChange, showToast]);
124
- const goPrev = useCallback(() => {
125
- if (currentStep > 0) {
126
- setStepError(null);
127
- onStepChange(currentStep - 1);
128
- }
129
- }, [currentStep, onStepChange]);
130
- const currentStepDef = steps[currentStep];
131
- const isLast = currentStep === steps.length - 1;
132
- return (_jsxs(Stack, { children: [_jsx(StepIndicator, { steps: steps, currentStep: currentStep, stepErrors: stepErrors, onStepClick: (i) => { if (i !== currentStep) {
133
- setStepError(null);
134
- onStepChange(i);
135
- } } }), _jsx(Div, { className: "flex-1", children: currentStepDef?.render({ values, onChange: handleFieldChange, errors: fieldErrors }) }), !hideActions && stepError && (_jsx(Text, { className: "mt-3 text-[var(--appkit-color-error)]", size: "sm", children: stepError })), !hideActions && (_jsx(StepFormActions, { currentStep: currentStep, totalSteps: steps.length, onNext: () => void goNext(), onPrev: currentStep > 0 ? goPrev : undefined, completeLabel: completeLabel, isLoading: isLoading && isLast, disabled: isLoading }))] }));
136
- }
@@ -1,12 +0,0 @@
1
- /**
2
- * feat-stores — Next.js App Router API handlers (GET /api/stores)
3
- *
4
- * Consuming projects can create a 2-line stub:
5
- *
6
- * ```ts
7
- * // app/api/stores/route.ts
8
- * export { GET } from "@mohasinac/feat-stores";
9
- * ```
10
- */
11
- import { NextResponse } from "next/server.js";
12
- export declare function GET(request: Request): Promise<NextResponse>;
@@ -1,110 +0,0 @@
1
- /**
2
- * feat-stores — Next.js App Router API handlers (GET /api/stores)
3
- *
4
- * Consuming projects can create a 2-line stub:
5
- *
6
- * ```ts
7
- * // app/api/stores/route.ts
8
- * export { GET } from "@mohasinac/feat-stores";
9
- * ```
10
- */
11
- import { NextResponse } from "next/server.js";
12
- import { getProviders } from "../../../contracts";
13
- import { parseListingParams } from "../../../utils/listing-params";
14
- import { normalizeError } from "../../../errors/normalize";
15
- const DEFAULT_PAGE = 1;
16
- const DEFAULT_PAGE_SIZE = 24;
17
- const DEFAULT_SORT = "-createdAt";
18
- function param(url, key) {
19
- return url.searchParams.get(key);
20
- }
21
- const SAFE_STORE_FILTER_FIELDS = new Set([
22
- "storeName",
23
- "storeCategory",
24
- "status",
25
- "isPublic",
26
- "isFeatured",
27
- "averageRating",
28
- "stats.totalProducts",
29
- ]);
30
- function validateSieveFilters(raw, allowedFields) {
31
- return raw
32
- .split(",")
33
- .map((c) => c.trim())
34
- .filter((c) => {
35
- const m = c.match(/^([^<>=!@]+)\s*(?:==|!=|<=|>=|<|>|@=\*?)/);
36
- return m ? allowedFields.has(m[1].trim()) : false;
37
- })
38
- .join(",");
39
- }
40
- // --- GET /api/stores --------------------------------------------------------
41
- export async function GET(request) {
42
- try {
43
- const url = new URL(request.url);
44
- const std = parseListingParams(url);
45
- const page = std.page ?? DEFAULT_PAGE;
46
- const pageSize = std.pageSize ?? DEFAULT_PAGE_SIZE;
47
- // Translate user-friendly sort names to Firestore nested field paths.
48
- const rawSort = std.sorts ?? DEFAULT_SORT;
49
- const sort = rawSort
50
- .replace(/^-itemsSold$/, "-stats.itemsSold")
51
- .replace(/^itemsSold$/, "stats.itemsSold")
52
- .replace(/^-averageRating$/, "-stats.averageRating")
53
- .replace(/^averageRating$/, "stats.averageRating");
54
- const parts = ["status==active,isPublic==true"];
55
- if (std.q)
56
- parts.push(`storeName@=*${std.q}`);
57
- const category = param(url, "category");
58
- if (category)
59
- parts.push(`storeCategory==${category}`);
60
- if (std.filters) {
61
- const safe = validateSieveFilters(std.filters, SAFE_STORE_FILTER_FIELDS);
62
- if (safe)
63
- parts.push(safe);
64
- }
65
- const filters = parts.join(",");
66
- const { db } = getProviders();
67
- if (!db) {
68
- return NextResponse.json({ success: false, error: "Database provider not registered" }, { status: 503 });
69
- }
70
- const repo = db.getRepository("stores");
71
- const result = await repo.findAll({
72
- filters,
73
- sort,
74
- page,
75
- perPage: pageSize,
76
- });
77
- // Map to public-safe shape (strip sensitive fields)
78
- const items = result.data.map((s) => ({
79
- id: s.id,
80
- storeSlug: s.storeSlug,
81
- ownerId: s.ownerId,
82
- storeName: s.storeName,
83
- storeDescription: s.storeDescription,
84
- storeCategory: s.storeCategory,
85
- storeLogoURL: s.storeLogoURL,
86
- storeBannerURL: s.storeBannerURL,
87
- status: s.status,
88
- isPublic: s.isPublic,
89
- totalProducts: s.stats?.totalProducts ?? s.totalProducts ?? undefined,
90
- itemsSold: s.stats?.itemsSold ?? s.itemsSold ?? undefined,
91
- totalReviews: s.stats?.totalReviews ?? s.totalReviews ?? undefined,
92
- averageRating: s.stats?.averageRating ?? s.averageRating,
93
- createdAt: s.createdAt,
94
- }));
95
- const body = {
96
- items,
97
- total: result.total,
98
- page: result.page,
99
- pageSize: result.perPage,
100
- totalPages: result.totalPages,
101
- hasMore: result.page < result.totalPages,
102
- };
103
- return NextResponse.json({ success: true, data: body });
104
- }
105
- catch (error) {
106
- void normalizeError(error);
107
- console.error("[feat-stores] GET /api/stores failed", error);
108
- return NextResponse.json({ success: false, error: "Failed to fetch stores" }, { status: 500 });
109
- }
110
- }
@@ -1,2 +0,0 @@
1
- import type { AuthoredCase } from "./_types";
2
- export declare const authored: Record<string, AuthoredCase>;
@@ -1,33 +0,0 @@
1
- /*
2
- * WHY: Authored six-part procedures for the account-auth/testing-program checklist page.
3
- * WHAT: 1 case(s), keyed by full checklist id.
4
- *
5
- * Generated by `node tester/scripts/merge-authored.mjs --page account-auth/testing-program`, then
6
- * reviewed by a human before merge — the review is the gate, not the generation.
7
- * Hand edits are safe and expected: re-running the merge for this page replaces
8
- * the file, so re-author only when you mean to discard them.
9
- *
10
- * @tag domain:tester
11
- * @tag layer:seed
12
- * @tag pattern:none
13
- * @tag access:server-only
14
- * @tag consumers:seed-data/authored/index.ts
15
- * @tag sideEffects:none
16
- */
17
- export const authored = {
18
- "checklist-account-auth-testing-program-admin-testing-section": {
19
- roles: ["admin"],
20
- startPage: "/admin/dashboard",
21
- steps: [
22
- "Sign in as admin@letitrip.in.",
23
- "Open /admin/dashboard.",
24
- "Locate the 'Admin Panel' sidebar on the right side of the page.",
25
- "Click the 'Testing' section heading in the sidebar to expand it.",
26
- "Locate the 'Test Cases' link in the Testing section.",
27
- "Locate the 'Results' link in the Testing section.",
28
- ],
29
- expectedBehaviour: "The admin sidebar renders a 'Testing' collapsible section containing at least two links: 'Test Cases' (pointing to /admin/tester-checklist) and 'Results' (pointing to /admin/tester-feedback). Both links are clickable and navigate to their respective pages.",
30
- expectedUiState: "The Testing section is visible in the Admin Panel sidebar. It contains a 'Test Cases' link and a 'Results' link. Both links are present and labelled exactly 'Test Cases' and 'Results'.",
31
- endResult: "After reload, the sidebar still shows the Testing section with both links. No data is persisted by this case — it is a read-only navigation check.",
32
- },
33
- };
@@ -1,2 +0,0 @@
1
- import type { AuthoredCase } from "./_types";
2
- export declare const authored: Record<string, AuthoredCase>;
@@ -1,138 +0,0 @@
1
- /*
2
- * WHY: Authored six-part procedures for the account-auth/testing-program checklist page.
3
- * WHAT: 6 case(s), keyed by full checklist id.
4
- *
5
- * Written by hand, case by case, against the label each one states. There is no
6
- * generator: the authoring scripts were deleted once it was clear they were
7
- * scaffolding around work that is simply writing.
8
- *
9
- * This page used to be split across two modules, `--admin` and nothing else, a
10
- * convention the deleted authoring bot needed because it batched a page by the
11
- * identity it ran as. Hand-written, one file per page is the whole rule, and the
12
- * split only made the five buyer-side cases easy to lose track of — which is what
13
- * happened: five of six sat unauthored while the page looked started.
14
- *
15
- * @tag domain:tester
16
- * @tag layer:seed
17
- * @tag pattern:none
18
- * @tag access:server-only
19
- * @tag consumers:seed-data/authored/index.ts
20
- * @tag sideEffects:none
21
- */
22
- export const authored = {
23
- "checklist-account-auth-testing-program-admin-tester-access": {
24
- roles: ["admin"],
25
- startPage: "/user/tester",
26
- steps: [
27
- "Sign in as admin@letitrip.in / TempPass123!.",
28
- "Open /user/tester.",
29
- "Read the group headings down the page.",
30
- "Expand the 'Buying' group and then the 'Cart' page inside it.",
31
- "Expand the 'Admin (Testing)' group.",
32
- ],
33
- inputs: { email: "admin@letitrip.in" },
34
- expectedBehaviour: "An admin reaches the Tester Hub on the strength of their role alone, without an isTester flag, and sees the same catalogue a tester does — including the adminOnly cases, which a plain tester without canTestAdmin does not get.",
35
- expectedUiState: "/user/tester renders the grouped checklist rather than a 'Testers only' warning. The Buying group expands to its pages and the Admin (Testing) group is present and expandable.",
36
- expectedData: { adminOnlyGroupVisible: true },
37
- endResult: "Reloading /user/tester still shows the full checklist. Nothing is written by this case; it is a read-only access check.",
38
- },
39
- "checklist-account-auth-testing-program-admin-testing-section": {
40
- roles: ["admin"],
41
- startPage: "/admin/dashboard",
42
- steps: [
43
- "Sign in as admin@letitrip.in / TempPass123!.",
44
- "Open /admin/dashboard.",
45
- "Expand the 'Testing' section in the admin sidebar.",
46
- "Click 'Test Cases' and note where it lands.",
47
- "Go back to /admin/dashboard and expand 'Testing' again.",
48
- "Click 'Results' and note where it lands.",
49
- ],
50
- inputs: { email: "admin@letitrip.in" },
51
- expectedBehaviour: "The Testing section carries both links and each one reaches a real page — 'Test Cases' to /admin/tester-checklist, the admin-managed catalogue, and 'Results' to /admin/tester-feedback, where answers are triaged. A nav entry pointing at a page that does not exist is the failure this catches.",
52
- expectedUiState: "The sidebar shows a 'Testing' section holding exactly the links 'Test Cases' and 'Results'. Clicking each renders its page with content, not a 404 and not an empty shell.",
53
- expectedData: {
54
- testCasesHref: "/admin/tester-checklist",
55
- resultsHref: "/admin/tester-feedback",
56
- },
57
- endResult: "After reload the Testing section still holds both links and both still resolve. Read-only; nothing persists.",
58
- },
59
- "checklist-account-auth-testing-program-tester-flag-live-refresh": {
60
- roles: ["admin", "buyer"],
61
- startPage: "/user/tester",
62
- steps: [
63
- "In browser window A, sign in as karthik.new@gmail.com / TempPass123! and open /user/tester.",
64
- "Leave window A open on that page without reloading it for the rest of this case.",
65
- "In a separate browser window B, sign in as admin@letitrip.in / TempPass123!.",
66
- "In window B open /admin/users and search for karthik.new@gmail.com.",
67
- "Open that user and set the 'Is tester' flag to on, then save.",
68
- "Return to window A and navigate between /user and /user/tester using the sidebar, without reloading.",
69
- "Keep navigating that way for up to 6 minutes.",
70
- "In window B, set 'Is tester' back to off for karthik.new@gmail.com and save.",
71
- ],
72
- inputs: { testerEmail: "karthik.new@gmail.com", isTester: true },
73
- expectedBehaviour: "The client session re-reads role, isTester, canTestAdmin, disabled and storeId on the 5-minute activity ping, so a flag an admin flips reaches an already-open tab without a sign-out. Before this was fixed those fields refreshed only on login or a full page reload, so the grant was invisible for as long as the tab stayed open.",
74
- expectedUiState: "Window A starts on /user/tester showing 'Testers only' — karthik.new@gmail.com has no tester flag. Within about 5 minutes, and with no reload and no re-login, the same page starts rendering the checklist instead. What must not happen is the warning persisting indefinitely until the tab is reloaded.",
75
- expectedData: { isTester: true },
76
- endResult: "Window A gains access without reloading. The flag is set back to off in the final step so karthik.new@gmail.com stays a plain buyer for other cases.",
77
- },
78
- "checklist-account-auth-testing-program-tester-hub-answer-saves": {
79
- roles: ["buyer"],
80
- startPage: "/user/tester",
81
- steps: [
82
- "Sign in as tester@letitrip.in / TempPass123!.",
83
- "Open /user/tester.",
84
- "Expand the 'Buying' group and then the 'Cart' page.",
85
- "Click 'Yes' on the first case in that page.",
86
- "Type 'QA Comment testing-program-tester-hub-answer-saves' in that case's comment box.",
87
- "Wait for the save indicator to settle without touching the browser reload button.",
88
- "Reload the page.",
89
- "Expand the 'Buying' group and then the 'Cart' page.",
90
- ],
91
- inputs: {
92
- answer: "yes",
93
- comment: "QA Comment testing-program-tester-hub-answer-saves",
94
- },
95
- expectedBehaviour: "The answer and the comment are written against a document keyed on tester and case together, so re-answering updates that one row rather than accumulating duplicates. The page does not navigate or reload to save.",
96
- expectedUiState: "The 'Yes' control becomes selected and a saved indicator appears, with no full-page flash. After reload the same case still shows 'Yes' selected and the comment text still in its box.",
97
- expectedData: {
98
- answer: "yes",
99
- comment: "QA Comment testing-program-tester-hub-answer-saves",
100
- },
101
- endResult: "The answer and comment survive the reload. A save that appears to work and is gone after F5 is exactly the failure this case exists to catch.",
102
- },
103
- "checklist-account-auth-testing-program-tester-hub-loads": {
104
- roles: ["buyer"],
105
- startPage: "/user/tester",
106
- steps: [
107
- "Sign in as tester@letitrip.in / TempPass123!.",
108
- "Open /user/tester.",
109
- "Read the group headings down the page.",
110
- "Expand the 'Buying' group.",
111
- "Expand the 'Cart' page inside it.",
112
- "Expand a case's 'Steps' toggle.",
113
- ],
114
- inputs: { email: "tester@letitrip.in" },
115
- expectedBehaviour: "The whole catalogue loads, grouped and then sub-grouped by page, and each case can be opened to reveal its roles, start page, steps, input values, expectations and end result. A tester without canTestAdmin does not see the Admin (Testing) group at all.",
116
- expectedUiState: "Group headings are visible — Account & Auth, Buying, Selling, Content & Discovery, Community & Support, Design & UX, Public & Marketing Pages, Page Wiring, CTA & Action Rows, Addresses, Search & navigation, Money Flows. Expanding Buying reveals its pages, and expanding a case shows its procedure rather than only the one-line label.",
117
- expectedData: { adminOnlyGroupVisible: false },
118
- endResult: "Reloading keeps the checklist rendered. Read-only; nothing is written. An empty hub, or one showing labels with no procedure behind them, both fail.",
119
- },
120
- "checklist-account-auth-testing-program-tester-hub-search": {
121
- roles: ["buyer"],
122
- startPage: "/user/tester",
123
- steps: [
124
- "Sign in as tester@letitrip.in / TempPass123!.",
125
- "Open /user/tester.",
126
- "Type coupon in the search box.",
127
- "Read the results.",
128
- "Clear the box and type /store/payouts instead.",
129
- "Read the results.",
130
- "Clear the box and type zzzznope instead.",
131
- "Read the results.",
132
- ],
133
- inputs: { titleQuery: "coupon", routeQuery: "/store/payouts", nonsenseQuery: "zzzznope" },
134
- expectedBehaviour: "Search matches on the case title AND on the route it links to, so a tester who knows only the page they are on can find its cases. It filters — it does not merely highlight while still listing everything.",
135
- expectedUiState: "'coupon' narrows the list to coupon-related cases. '/store/payouts' returns the payout cases, which is the whole point of route matching — those titles do not contain the word 'payouts' as typed with a slash. 'zzzznope' returns an empty state with a readable message, NOT the full unfiltered checklist.",
136
- endResult: "The nonsense query is the control: if it returns everything, the box is not filtering and the two real queries proved nothing. Read-only; nothing persists.",
137
- },
138
- };
@@ -1,2 +0,0 @@
1
- import type { BidDocument } from "../../auctions/schemas/firestore";
2
- export declare const bidsTesterSeedData: Partial<BidDocument>[];
@@ -1,37 +0,0 @@
1
- /*
2
- * WHY: Shared tester sandbox — one winning bid on the already-ended auction-tester-sandbox-won
3
- * (kept separate from the still-biddable auction-tester-sandbox-1 so testers can still
4
- * place a live bid on the latter) so testers (and the paired orders-tester-seed-data.ts
5
- * auction-win order) can exercise the "winning an auction creates a payable order" flow.
6
- * Auto-expires in 7 days, cascading with the parent auction product.
7
- * WHAT: Exports bidsTesterSeedData — 1 Partial<BidDocument> for the seed runner.
8
- *
9
- * EXPORTS:
10
- * bidsTesterSeedData — Array of 1 Partial<BidDocument> for the seed runner
11
- *
12
- * @tag domain:auctions,bids,tester
13
- * @tag layer:seed
14
- * @tag pattern:none
15
- * @tag access:server-only
16
- * @tag consumers:seed/index.ts,seed-cli.mjs
17
- * @tag sideEffects:none
18
- */
19
- const NOW = new Date();
20
- const daysAgo = (n) => new Date(NOW.getTime() - n * 86400000);
21
- export const bidsTesterSeedData = [
22
- {
23
- id: "bid-tester-sandbox-won-tester-qa",
24
- productId: "auction-tester-sandbox-won",
25
- productTitle: "Test Auction — Already Won",
26
- userId: "user-tester-qa",
27
- userName: "QA Tester",
28
- userEmail: "tester@letitrip.in",
29
- bidAmount: 15000,
30
- currency: "INR",
31
- status: "won",
32
- isWinning: true,
33
- bidDate: daysAgo(1),
34
- createdAt: daysAgo(1),
35
- updatedAt: daysAgo(1),
36
- },
37
- ];
@@ -1,2 +0,0 @@
1
- import type { BlogPostDocument } from "../../blog/schemas";
2
- export declare const blogTesterSeedData: Partial<BlogPostDocument>[];
@@ -1,40 +0,0 @@
1
- /*
2
- * WHY: Shared tester sandbox — 1 disposable test blog post so testers can exercise the
3
- * blog reading flow. Auto-expires after 7 days (testerSandboxCleanup).
4
- * WHAT: Exports blogTesterSeedData — 1 Partial<BlogPostDocument> tagged isTestData:true.
5
- *
6
- * EXPORTS:
7
- * blogTesterSeedData — Array of 1 Partial<BlogPostDocument> for the seed runner
8
- *
9
- * @tag domain:blog,tester
10
- * @tag layer:seed
11
- * @tag pattern:none
12
- * @tag access:server-only
13
- * @tag consumers:seed/index.ts,seed-cli.mjs
14
- * @tag sideEffects:none
15
- */
16
- import { seedPhoto } from "../../../seed/_helpers/media";
17
- import { testDataExpiresAt } from "./tester-ttl";
18
- export const blogTesterSeedData = [
19
- {
20
- id: "blog-tester-sandbox-post",
21
- title: "Welcome to the Tester Sandbox",
22
- slug: "tester-sandbox-post",
23
- excerpt: "A disposable test blog post for the tester QA program — check that images, formatting, and reading flow work correctly.",
24
- content: `<h2>This is a test post</h2><p>It exists only for the tester QA program and will be deleted automatically after 7 days.</p><p>Check: does this render correctly? Are images loading? Is the layout readable?</p>`,
25
- coverImage: { type: "image", url: seedPhoto("blog-cover-tester-sandbox-post-20260101", 1200, 600), alt: "Test cover image" },
26
- category: "guides",
27
- tags: ["test"],
28
- isFeatured: false,
29
- status: "published",
30
- publishedAt: new Date(),
31
- authorId: "user-admin-letitrip",
32
- authorName: "LetItRip Admin",
33
- readTimeMinutes: 1,
34
- views: 0,
35
- isTestData: true,
36
- testDataExpiresAt: testDataExpiresAt(),
37
- createdAt: new Date(),
38
- updatedAt: new Date(),
39
- },
40
- ];
@@ -1,3 +0,0 @@
1
- import type { CartDocument } from "../../cart/schemas/firestore";
2
- /** The cart document id IS the user id — one cart per account. */
3
- export declare const cartsTesterSeedData: Partial<CartDocument>[];
@@ -1,70 +0,0 @@
1
- /*
2
- * WHY: Shared tester sandbox — the LOCKED cart line a settled auction leaves behind.
3
- * WHAT: One cart for user-tester-qa holding the win from auction-tester-sandbox-won.
4
- *
5
- * 🛑 WHY THIS FILE HAD TO EXIST.
6
- *
7
- * The sandbox seeded a won auction, its winning bid, and a delivered order made
8
- * from it — the shape settlement produced BEFORE Root Cause #60, when it wrote an
9
- * order document directly. That order was in a shape no orders list could render
10
- * and no checkout could accept, so a winner had no way to pay at all; the fix
11
- * routed a win through the CART as a locked line instead.
12
- *
13
- * The fixtures were never updated to match. So `win-auction` — the case whose
14
- * label is literally "creates a payable locked cart line, not a stuck order" —
15
- * could not pass: /user/bids offered "Pay now", it led to /checkout?lane=auction,
16
- * and the cart was empty. Found by the tester 2026-09-06, which is exactly the
17
- * kind of thing a case can only prove by being run.
18
- *
19
- * The delivered order fixture STAYS. It is the "win already completed" state and
20
- * other cases read it. This adds the missing "win awaiting payment" state.
21
- *
22
- * 🛑 `isAuctionWin` is not decoration. `laneOf()` keys on `isAuctionWin || bidId`
23
- * and ignores `listingType` and `locked` entirely — a line without it lands in the
24
- * STANDARD lane, so the Won Auctions tab stays empty while a non-removable line
25
- * sits in the ordinary cart. Both fields are set here so the lane is unambiguous.
26
- *
27
- * @tag domain:tester
28
- * @tag layer:seed
29
- * @tag pattern:none
30
- * @tag access:server-only
31
- * @tag consumers:seed/index.ts,seed/manifest.ts
32
- * @tag sideEffects:none
33
- */
34
- import { testDataExpiresAt } from "./tester-ttl";
35
- const NOW = new Date();
36
- const hoursAgo = (n) => new Date(NOW.getTime() - n * 3600000);
37
- /** The cart document id IS the user id — one cart per account. */
38
- export const cartsTesterSeedData = [
39
- {
40
- id: "user-tester-qa",
41
- userId: "user-tester-qa",
42
- items: [
43
- {
44
- itemId: "cartitem-tester-sandbox-auction-won",
45
- productId: "auction-tester-sandbox-won",
46
- productTitle: "Test Auction — Already Won",
47
- price: 15000,
48
- currency: "INR",
49
- quantity: 1,
50
- storeId: "store-tester-qa-seller",
51
- storeName: "Tester QA Seller",
52
- listingType: "auction",
53
- // Both, deliberately — see the lane note in the header.
54
- isAuctionWin: true,
55
- bidId: "bid-tester-sandbox-won-tester-qa",
56
- // A win is non-removable and fixed-quantity: settlement is the only writer
57
- // of this line, and declining to pay is handled by the forfeit sweep rather
58
- // than by the buyer deleting it.
59
- locked: true,
60
- lockedPrice: 15000,
61
- addedAt: hoursAgo(2),
62
- updatedAt: hoursAgo(2),
63
- },
64
- ],
65
- isTestData: true,
66
- testDataExpiresAt: testDataExpiresAt(),
67
- createdAt: hoursAgo(2),
68
- updatedAt: hoursAgo(2),
69
- },
70
- ];
@@ -1,2 +0,0 @@
1
- import type { CategoryDocument } from "../../categories/schemas";
2
- export declare const categoriesTesterSeedData: Partial<CategoryDocument>[];