@mohasinac/appkit 4.29.1 → 4.30.0
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/_internal/client/features/layout/SidebarCollapseToggle.js +1 -1
- package/dist/_internal/shared/features/blog/schema.d.ts +2 -2
- package/dist/_internal/shared/features/blog/schema.js +2 -1
- package/dist/_internal/shared/features/brands/schema.d.ts +2 -2
- package/dist/_internal/shared/features/brands/schema.js +3 -1
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +3 -3
- package/dist/_internal/shared/features/categories/bundle-schemas.js +2 -1
- package/dist/_internal/shared/features/events/schema.d.ts +2 -2
- package/dist/_internal/shared/features/events/schema.js +2 -1
- package/dist/_internal/shared/features/products/schema.d.ts +8 -8
- package/dist/_internal/shared/features/products/schema.js +3 -2
- package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
- package/dist/_internal/shared/features/reviews/schema.js +2 -1
- package/dist/_internal/shared/types/action-result.d.ts +0 -2
- package/dist/client/api/surface-error.d.ts +9 -0
- package/dist/client/api/surface-error.js +29 -3
- package/dist/client/api/useApiMutation.d.ts +10 -0
- package/dist/client/api/useApiMutation.js +2 -1
- package/dist/core/server-action.d.ts +3 -14
- package/dist/core/server-action.js +11 -2
- package/dist/errors/error-envelope.d.ts +73 -0
- package/dist/errors/error-envelope.js +96 -0
- package/dist/errors/error-handler.js +14 -7
- package/dist/features/account/components/NotificationBell.js +1 -1
- package/dist/features/account/components/UserSidebar.js +2 -2
- package/dist/features/account/components/UserSupportView.js +2 -6
- package/dist/features/account/hooks/useProfile.d.ts +6 -0
- package/dist/features/account/hooks/useProfile.js +1 -0
- package/dist/features/admin/components/AdminAdEditorView.js +1 -3
- package/dist/features/admin/components/AdminAddressClustersView.js +1 -3
- package/dist/features/admin/components/AdminAddressEditorView.js +2 -4
- package/dist/features/admin/components/AdminAddressesView.js +1 -3
- package/dist/features/admin/components/AdminAllEventEntriesView.js +1 -3
- package/dist/features/admin/components/AdminBidsView.js +1 -3
- package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
- package/dist/features/admin/components/AdminBrandEditorView.js +4 -5
- package/dist/features/admin/components/AdminCarouselGroupEditorView.js +1 -3
- package/dist/features/admin/components/AdminCategoryEditorView.js +2 -4
- package/dist/features/admin/components/AdminContactEditorView.js +1 -3
- package/dist/features/admin/components/AdminContactView.js +2 -6
- package/dist/features/admin/components/AdminCouponEditorView.js +2 -4
- package/dist/features/admin/components/AdminEmployeeEditorView.js +3 -9
- package/dist/features/admin/components/AdminFaqEditorView.js +2 -4
- package/dist/features/admin/components/AdminFeatureEditorView.js +0 -9
- package/dist/features/admin/components/AdminFeatureFlagsView.js +2 -4
- package/dist/features/admin/components/AdminNavEditorView.js +1 -3
- package/dist/features/admin/components/AdminNavigationView.js +3 -3
- package/dist/features/admin/components/AdminNewsletterView.js +1 -3
- package/dist/features/admin/components/AdminNotificationsView.js +2 -6
- package/dist/features/admin/components/AdminOffersView.js +1 -3
- package/dist/features/admin/components/AdminOrderEditorView.js +1 -3
- package/dist/features/admin/components/AdminPaymentClustersView.js +1 -3
- package/dist/features/admin/components/AdminPaymentMethodsView.js +1 -3
- package/dist/features/admin/components/AdminPayoutMarkPaidModal.js +2 -4
- package/dist/features/admin/components/AdminProductEditorView.js +2 -4
- package/dist/features/admin/components/AdminReturnRequestsView.js +2 -6
- package/dist/features/admin/components/AdminReviewsView.js +2 -6
- package/dist/features/admin/components/AdminScammerEditorView.js +2 -6
- package/dist/features/admin/components/AdminSectionsView.js +2 -6
- package/dist/features/admin/components/AdminSessionsView.js +1 -3
- package/dist/features/admin/components/AdminSidebar.js +2 -2
- package/dist/features/admin/components/AdminSiteSettingsView.js +1 -1
- package/dist/features/admin/components/AdminStoreEditorView.js +1 -3
- package/dist/features/admin/components/AdminStoresView.js +2 -6
- package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +2 -6
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +7 -21
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +2 -4
- package/dist/features/admin/components/AdminUserEditorView.js +6 -18
- package/dist/features/admin/components/AdminUsersView.js +2 -6
- package/dist/features/admin/components/BrandQuickCreateForm.js +2 -4
- package/dist/features/admin/components/CategoryQuickCreateForm.js +2 -4
- package/dist/features/auctions/components/PlaceBidFormClient.js +1 -1
- package/dist/features/auth/components/ForgotPasswordView.js +1 -1
- package/dist/features/auth/components/LoginForm.js +1 -1
- package/dist/features/auth/components/RegisterForm.js +1 -1
- package/dist/features/auth/components/ResetPasswordView.js +1 -1
- package/dist/features/auth/components/SocialAuthButtons.js +1 -1
- package/dist/features/auth/repository/session.repository.js +13 -1
- package/dist/features/blog/actions/blog-actions.d.ts +14 -14
- package/dist/features/blog/actions/blog-actions.js +6 -8
- package/dist/features/blog/schemas/index.d.ts +1 -1
- package/dist/features/blog/schemas/index.js +2 -4
- package/dist/features/categories/api/[id]/route.js +2 -1
- package/dist/features/categories/api/route.js +2 -1
- package/dist/features/events/schemas/index.d.ts +1 -1
- package/dist/features/events/schemas/index.js +2 -4
- package/dist/features/faq/components/ContactCTA.js +1 -1
- package/dist/features/filters/AsyncFacetSection.js +1 -1
- package/dist/features/filters/FilterFacetSection.js +1 -1
- package/dist/features/filters/RangeFilter.js +1 -1
- package/dist/features/filters/SwitchFilter.js +1 -1
- package/dist/features/homepage/api/carousel/[id]/route.js +4 -3
- package/dist/features/homepage/api/carousel/route.js +4 -3
- package/dist/features/homepage/api/route.js +3 -2
- package/dist/features/homepage/components/GoogleReviewsSection.js +1 -1
- package/dist/features/homepage/components/HeroBanner.js +1 -1
- package/dist/features/homepage/components/HeroCarousel.js +1 -1
- package/dist/features/homepage/components/SocialPostCard.js +1 -1
- package/dist/features/homepage/components/WhatsAppCommunitySection.js +1 -1
- package/dist/features/layout/AppLayoutShell.js +4 -4
- package/dist/features/layout/BottomNavbar.js +2 -2
- package/dist/features/layout/MainNavbar.js +1 -1
- package/dist/features/layout/TitleBarLayout.js +24 -12
- package/dist/features/media/AvatarUpload.js +28 -10
- package/dist/features/media/modals/ImageCropModal.js +1 -1
- package/dist/features/media/modals/ImageEditor.js +1 -1
- package/dist/features/products/components/MakeOfferButton.js +1 -1
- package/dist/features/reviews/api/[id]/route.js +3 -2
- package/dist/features/reviews/api/route.js +3 -2
- package/dist/features/search/components/Search.js +2 -2
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +3 -3
- package/dist/features/seller/components/SellerSidebar.js +2 -2
- package/dist/features/store-extensions/components/ReviewDecisionModal.js +1 -1
- package/dist/features/stores/repository/store.repository.d.ts +21 -0
- package/dist/features/stores/repository/store.repository.js +32 -0
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +0 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -1
- package/dist/next/api/api-response.d.ts +29 -12
- package/dist/next/api/api-response.js +9 -8
- package/dist/next/api/apiHandler.d.ts +6 -1
- package/dist/next/api/apiHandler.js +13 -1
- package/dist/next/api/routeHandler.js +6 -6
- package/dist/next/middleware/pii-scrubber.js +5 -2
- package/dist/providers/db-firebase/base.repository.d.ts +14 -0
- package/dist/providers/db-firebase/base.repository.js +22 -0
- package/dist/providers/db-firebase/sieve.d.ts +14 -1
- package/dist/providers/db-firebase/sieve.js +3 -1
- package/dist/security/pii-encrypt.d.ts +0 -4
- package/dist/security/pii-encrypt.js +79 -13
- package/dist/security/pii-mask.d.ts +39 -1
- package/dist/security/pii-mask.js +33 -4
- package/dist/security/pii-redact.d.ts +0 -13
- package/dist/security/pii-redact.js +3 -1
- package/dist/security/pii-schemas.d.ts +0 -2
- package/dist/security/pii-schemas.js +16 -2
- package/dist/security/settings-encryption.js +31 -3
- package/dist/seed/test-utils.js +6 -2
- package/dist/ui/DataTable.js +2 -2
- package/dist/ui/components/CollapsibleSection.js +1 -1
- package/dist/ui/components/ImageGallery.js +1 -1
- package/dist/ui/components/ListingLayout.js +1 -1
- package/dist/ui/forms/FormShell.d.ts +0 -19
- package/dist/ui/forms/FormShell.js +4 -23
- package/dist/ui/forms/apply-zod-issues.d.ts +34 -0
- package/dist/ui/forms/apply-zod-issues.js +38 -0
- package/dist/ui/forms/index.d.ts +2 -1
- package/dist/ui/forms/index.js +2 -1
- package/dist/ui/icons/icon-registry.d.ts +8 -0
- package/dist/ui/icons/icon-registry.js +13 -1
- package/dist/utils/action-response.d.ts +1 -1
- package/dist/utils/media-url.d.ts +49 -0
- package/dist/utils/media-url.js +72 -1
- package/dist/validation/schemas.d.ts +15 -2
- package/dist/validation/schemas.js +18 -22
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +10 -2
|
@@ -113,6 +113,7 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, photoU
|
|
|
113
113
|
const invalidate = () => queryClient.invalidateQueries({ queryKey: ["admin", "users"] });
|
|
114
114
|
// --- Mutations ------------------------------------------------------------
|
|
115
115
|
const saveMutation = useApiMutation({
|
|
116
|
+
errorMessage: "Failed to update user.",
|
|
116
117
|
mutationFn: async () => {
|
|
117
118
|
// ST-2 — build publicProfile partial only when something changed; keeps
|
|
118
119
|
// the PATCH payload minimal and avoids overwriting unrelated subkeys.
|
|
@@ -150,11 +151,9 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, photoU
|
|
|
150
151
|
invalidate();
|
|
151
152
|
onClose();
|
|
152
153
|
},
|
|
153
|
-
onError: (err) => {
|
|
154
|
-
showToast(err?.message ?? "Failed to update user.", "error");
|
|
155
|
-
},
|
|
156
154
|
});
|
|
157
155
|
const deleteMutation = useApiMutation({
|
|
156
|
+
errorMessage: "Failed to delete user.",
|
|
158
157
|
mutationFn: async () => {
|
|
159
158
|
await apiClient.delete(ADMIN_ENDPOINTS.USER_BY_ID(userId));
|
|
160
159
|
},
|
|
@@ -164,11 +163,9 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, photoU
|
|
|
164
163
|
setDeleteOpen(false);
|
|
165
164
|
onClose();
|
|
166
165
|
},
|
|
167
|
-
onError: (err) => {
|
|
168
|
-
showToast(err?.message ?? "Failed to delete user.", "error");
|
|
169
|
-
},
|
|
170
166
|
});
|
|
171
167
|
const hardBanMutation = useApiMutation({
|
|
168
|
+
errorMessage: "Failed to ban user.",
|
|
172
169
|
mutationFn: async (reason) => {
|
|
173
170
|
await apiClient.post(ADMIN_ENDPOINTS.USER_HARD_BAN(userId), { reason });
|
|
174
171
|
},
|
|
@@ -179,11 +176,9 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, photoU
|
|
|
179
176
|
setHardBanReasonInput("");
|
|
180
177
|
onClose();
|
|
181
178
|
},
|
|
182
|
-
onError: (err) => {
|
|
183
|
-
showToast(err?.message ?? "Failed to ban user.", "error");
|
|
184
|
-
},
|
|
185
179
|
});
|
|
186
180
|
const unbanMutation = useApiMutation({
|
|
181
|
+
errorMessage: "Failed to unban user.",
|
|
187
182
|
mutationFn: async () => {
|
|
188
183
|
await apiClient.post(ADMIN_ENDPOINTS.USER_UNBAN(userId), {});
|
|
189
184
|
},
|
|
@@ -192,11 +187,9 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, photoU
|
|
|
192
187
|
invalidate();
|
|
193
188
|
onClose();
|
|
194
189
|
},
|
|
195
|
-
onError: (err) => {
|
|
196
|
-
showToast(err?.message ?? "Failed to unban user.", "error");
|
|
197
|
-
},
|
|
198
190
|
});
|
|
199
191
|
const softBanMutation = useApiMutation({
|
|
192
|
+
errorMessage: "Failed to apply soft ban.",
|
|
200
193
|
mutationFn: async (payload) => {
|
|
201
194
|
await apiClient.post(ADMIN_ENDPOINTS.USER_SOFT_BAN(userId), payload);
|
|
202
195
|
},
|
|
@@ -208,11 +201,9 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, photoU
|
|
|
208
201
|
setSoftBanReason("");
|
|
209
202
|
setSoftBanExpiry("");
|
|
210
203
|
},
|
|
211
|
-
onError: (err) => {
|
|
212
|
-
showToast(err?.message ?? "Failed to apply soft ban.", "error");
|
|
213
|
-
},
|
|
214
204
|
});
|
|
215
205
|
const liftSoftBanMutation = useApiMutation({
|
|
206
|
+
errorMessage: "Failed to lift soft ban.",
|
|
216
207
|
mutationFn: async (action) => {
|
|
217
208
|
await apiClient.delete(ADMIN_ENDPOINTS.USER_SOFT_BAN_LIFT(userId, action));
|
|
218
209
|
},
|
|
@@ -220,9 +211,6 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, photoU
|
|
|
220
211
|
showToast("Soft ban lifted.", "success");
|
|
221
212
|
invalidate();
|
|
222
213
|
},
|
|
223
|
-
onError: (err) => {
|
|
224
|
-
showToast(err?.message ?? "Failed to lift soft ban.", "error");
|
|
225
|
-
},
|
|
226
214
|
});
|
|
227
215
|
const isHardBanned = currentIsHardBanned ?? false;
|
|
228
216
|
const softBans = currentSoftBans ?? [];
|
|
@@ -52,6 +52,7 @@ export function AdminUsersView({ children, ...props }) {
|
|
|
52
52
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
53
53
|
}, [banJobEvent.status]);
|
|
54
54
|
const banUser = useApiMutation({
|
|
55
|
+
errorMessage: "Failed to start hard-ban.",
|
|
55
56
|
mutationFn: () => {
|
|
56
57
|
if (!banTargetId)
|
|
57
58
|
throw new Error("No user selected");
|
|
@@ -60,9 +61,6 @@ export function AdminUsersView({ children, ...props }) {
|
|
|
60
61
|
onSuccess: (result) => {
|
|
61
62
|
banJobEvent.subscribe(result.jobId, result.customToken);
|
|
62
63
|
},
|
|
63
|
-
onError: () => {
|
|
64
|
-
toast.showToast("Failed to start hard-ban.", "error");
|
|
65
|
-
},
|
|
66
64
|
});
|
|
67
65
|
const bulkUsers = useBulkAction({
|
|
68
66
|
mutationFn: (payload) => apiClient.post(ADMIN_ENDPOINTS.USERS_BULK, payload),
|
|
@@ -75,14 +73,12 @@ export function AdminUsersView({ children, ...props }) {
|
|
|
75
73
|
},
|
|
76
74
|
});
|
|
77
75
|
const unbanUser = useApiMutation({
|
|
76
|
+
errorMessage: "Failed to lift ban.",
|
|
78
77
|
mutationFn: (uid) => apiClient.post(ADMIN_ENDPOINTS.USER_UNBAN(uid), {}),
|
|
79
78
|
onSuccess: () => {
|
|
80
79
|
toast.showToast("Ban lifted.", "success");
|
|
81
80
|
void queryClient.invalidateQueries({ queryKey: ["admin", "users", "listing"] });
|
|
82
81
|
},
|
|
83
|
-
onError: () => {
|
|
84
|
-
toast.showToast("Failed to lift ban.", "error");
|
|
85
|
-
},
|
|
86
82
|
});
|
|
87
83
|
const handleManageBulkClick = (selection) => {
|
|
88
84
|
const rowId = selection.selectedIds[0];
|
|
@@ -8,7 +8,7 @@ import { apiClient } from "../../../http";
|
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
9
|
import { quickCreateTaxonomySchema } from "../schemas/small-forms";
|
|
10
10
|
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
11
|
-
import { applyZodIssues } from "../../../ui/forms/
|
|
11
|
+
import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
|
|
12
12
|
function toBrandSlug(str) {
|
|
13
13
|
const base = str.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
14
14
|
return base.startsWith("brand-") ? base : `brand-${base}`;
|
|
@@ -19,6 +19,7 @@ export function BrandQuickCreateForm({ onSaved, onCancel }) {
|
|
|
19
19
|
const [isActive, setIsActive] = React.useState(true);
|
|
20
20
|
const { showToast } = useToast();
|
|
21
21
|
const mutation = useApiMutation({
|
|
22
|
+
errorMessage: "Failed to create brand.",
|
|
22
23
|
mutationFn: async () => apiClient.post(ADMIN_ENDPOINTS.BRANDS, {
|
|
23
24
|
name,
|
|
24
25
|
slug: toBrandSlug(name),
|
|
@@ -29,9 +30,6 @@ export function BrandQuickCreateForm({ onSaved, onCancel }) {
|
|
|
29
30
|
const id = res?.data?.id ?? res?.id ?? "";
|
|
30
31
|
onSaved(id, name);
|
|
31
32
|
},
|
|
32
|
-
onError: (err) => {
|
|
33
|
-
showToast(err?.message ?? "Failed to create brand.", "error");
|
|
34
|
-
},
|
|
35
33
|
});
|
|
36
34
|
return (_jsx(Form, { schema: quickCreateTaxonomySchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormErrorSummary, {}), _jsx(Input, { label: "Brand name", value: name, onChange: (e) => setName(e.target.value), required: true, placeholder: "e.g. Hot Wheels", autoFocus: true }), _jsx(Input, { label: "Description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Optional brief description" }), _jsx(Toggle, { label: "Active", checked: isActive, onChange: setIsActive }), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: mutation.isPending, disabled: mutation.isPending, onClick: () => {
|
|
37
35
|
clearErrors();
|
|
@@ -8,7 +8,7 @@ import { apiClient } from "../../../http";
|
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
9
|
import { quickCreateTaxonomySchema } from "../schemas/small-forms";
|
|
10
10
|
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
11
|
-
import { applyZodIssues } from "../../../ui/forms/
|
|
11
|
+
import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
|
|
12
12
|
function toCategorySlug(str) {
|
|
13
13
|
return str.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
14
14
|
}
|
|
@@ -18,6 +18,7 @@ export function CategoryQuickCreateForm({ onSaved, onCancel }) {
|
|
|
18
18
|
const [isActive, setIsActive] = React.useState(true);
|
|
19
19
|
const { showToast } = useToast();
|
|
20
20
|
const mutation = useApiMutation({
|
|
21
|
+
errorMessage: "Failed to create category.",
|
|
21
22
|
mutationFn: async () => apiClient.post(ADMIN_ENDPOINTS.CATEGORIES, {
|
|
22
23
|
name,
|
|
23
24
|
slug: toCategorySlug(name),
|
|
@@ -28,9 +29,6 @@ export function CategoryQuickCreateForm({ onSaved, onCancel }) {
|
|
|
28
29
|
const id = res?.data?.id ?? res?.id ?? "";
|
|
29
30
|
onSaved(id, name);
|
|
30
31
|
},
|
|
31
|
-
onError: (err) => {
|
|
32
|
-
showToast(err?.message ?? "Failed to create category.", "error");
|
|
33
|
-
},
|
|
34
32
|
});
|
|
35
33
|
return (_jsx(Form, { schema: quickCreateTaxonomySchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormErrorSummary, {}), _jsx(Input, { label: "Category name", value: name, onChange: (e) => setName(e.target.value), required: true, placeholder: "e.g. Trading Cards", autoFocus: true }), _jsx(Input, { label: "Description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Optional brief description" }), _jsx(Toggle, { label: "Active", checked: isActive, onChange: setIsActive }), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: mutation.isPending, disabled: mutation.isPending, onClick: () => {
|
|
36
34
|
clearErrors();
|
|
@@ -7,7 +7,7 @@ import { isAuthError } from "../../../utils/auth-error";
|
|
|
7
7
|
import { Button, CountdownDisplay, Div, LoginRequiredModal, Modal, Row, Span, Stack, Text } from "../../../ui";
|
|
8
8
|
import { Form } from "../../../ui/components/Form";
|
|
9
9
|
import { FieldInput } from "../../../ui/forms/FieldInput";
|
|
10
|
-
import { applyZodIssues } from "../../../ui/forms/
|
|
10
|
+
import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
|
|
11
11
|
import { placeBidSchema } from "../schemas/bid-input";
|
|
12
12
|
import { useLiveAuctionBid } from "../hooks/useLiveAuctionBid";
|
|
13
13
|
import { AUCTION_BUYOUT_WINDOW_MINUTES } from "../../../_internal/shared/checkout/lanes";
|
|
@@ -5,7 +5,7 @@ import { useState } from "react";
|
|
|
5
5
|
import { Alert, Button, Div, Heading, Row, Stack, Text, useToast } from "../../../ui";
|
|
6
6
|
import { Form } from "../../../ui/components/Form";
|
|
7
7
|
import { FieldInput } from "../../../ui/forms/FieldInput";
|
|
8
|
-
import { applyZodIssues } from "../../../ui/forms/
|
|
8
|
+
import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
|
|
9
9
|
import { forgotPasswordSchema } from "../schemas";
|
|
10
10
|
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
11
11
|
export function ForgotPasswordView({ onSubmit, isLoading = false, error, success, labels = {}, renderBackLink, className = "", }) {
|
|
@@ -6,7 +6,7 @@ import { Alert, Button, Div, Heading, Row, SiteMark, Stack, Text, useToast } fro
|
|
|
6
6
|
import { Form } from "../../../ui/components/Form";
|
|
7
7
|
import { FieldInput } from "../../../ui/forms/FieldInput";
|
|
8
8
|
import { FieldCheckbox } from "../../../ui/forms/FieldCheckbox";
|
|
9
|
-
import { applyZodIssues } from "../../../ui/forms/
|
|
9
|
+
import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
|
|
10
10
|
import { loginSchema } from "../schemas";
|
|
11
11
|
import { SocialAuthButtons } from "./SocialAuthButtons";
|
|
12
12
|
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
@@ -7,7 +7,7 @@ import { Alert, Button, Div, Heading, Row, SiteMark, Stack, Text, useToast } fro
|
|
|
7
7
|
import { Form } from "../../../ui/components/Form";
|
|
8
8
|
import { FieldInput } from "../../../ui/forms/FieldInput";
|
|
9
9
|
import { FieldCheckbox } from "../../../ui/forms/FieldCheckbox";
|
|
10
|
-
import { applyZodIssues } from "../../../ui/forms/
|
|
10
|
+
import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
|
|
11
11
|
import { registerSchema, registerPasswordSchema } from "../schemas";
|
|
12
12
|
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
13
13
|
const registerClientSchema = registerSchema.extend({
|
|
@@ -6,7 +6,7 @@ import { z } from "zod";
|
|
|
6
6
|
import { Alert, Button, Div, Heading, Row, Stack, Text, useToast } from "../../../ui";
|
|
7
7
|
import { Form } from "../../../ui/components/Form";
|
|
8
8
|
import { FieldInput } from "../../../ui/forms/FieldInput";
|
|
9
|
-
import { applyZodIssues } from "../../../ui/forms/
|
|
9
|
+
import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
|
|
10
10
|
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
11
11
|
const resetClientSchema = z.object({
|
|
12
12
|
password: z.string().min(6, "Password must be at least 6 characters"),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Button, Div, Row, Span } from "../../../ui";
|
|
3
3
|
export function SocialAuthButtons({ onGoogle, disabled, dividerLabel, googleLabel, }) {
|
|
4
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: "relative", children: [_jsx(Row, { className: "absolute inset-0", align: "center", children: _jsx(Div, { className: "w-full border-t border-neutral-200" }) }), _jsx(Row, { textSize: "sm", className: "relative", justify: "center", children: _jsx(Span, { padding: "x-xs", surface: "default", color: "muted", children: dividerLabel }) })] }), _jsx(Div, { layout: "grid", gap: "3", className: "grid-cols-1", children: _jsxs(Button, { rounded: "md", type: "button", onClick: onGoogle, disabled: disabled, variant: "outline", paddingX: "md", paddingY: "sm", textSize: "sm", weight: "medium", className: "inline- justify-[center] border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text)] dark:text-[var(--appkit-color-text)] hover:bg-surface-hover disabled:cursor-not-allowed disabled:opacity-50", children: [_jsxs("svg", { className: "mr-2 h-5 w-5", viewBox: "0 0 24 24", "aria-hidden": "true", children: [_jsx("path", { fill: "currentColor", d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" }), _jsx("path", { fill: "currentColor", d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" }), _jsx("path", { fill: "currentColor", d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" }), _jsx("path", { fill: "currentColor", d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" })] }), googleLabel] }) })] }));
|
|
4
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: "relative", children: [_jsx(Row, { className: "absolute inset-0", align: "center", children: _jsx(Div, { className: "w-full border-t border-neutral-200" }) }), _jsx(Row, { textSize: "sm", className: "relative", justify: "center", children: _jsx(Span, { padding: "x-xs", surface: "default", color: "muted", children: dividerLabel }) })] }), _jsx(Div, { layout: "grid", gap: "3", className: "grid-cols-1", children: _jsxs(Button, { rounded: "md", type: "button", onClick: onGoogle, disabled: disabled, variant: "outline", paddingX: "md", paddingY: "sm", textSize: "sm", weight: "medium", className: "inline- justify-[center] border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text)] dark:text-[var(--appkit-color-text)] hover:bg-surface-hover disabled:cursor-not-allowed disabled:opacity-50", children: [_jsxs("svg", { className: "mr-2 h-5 w-5 shrink-0", viewBox: "0 0 24 24", "aria-hidden": "true", children: [_jsx("path", { fill: "currentColor", d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" }), _jsx("path", { fill: "currentColor", d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" }), _jsx("path", { fill: "currentColor", d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" }), _jsx("path", { fill: "currentColor", d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" })] }), googleLabel] }) })] }));
|
|
5
5
|
}
|
|
@@ -57,8 +57,20 @@ export class SessionRepository extends BaseRepository {
|
|
|
57
57
|
async updateActivity(sessionId, data) {
|
|
58
58
|
const updateData = {
|
|
59
59
|
lastActivity: new Date(),
|
|
60
|
-
...(data?.location && { location: data.location }),
|
|
61
60
|
};
|
|
61
|
+
// Firestore `.update()` REPLACES a map field wholesale — it does not merge.
|
|
62
|
+
// Passing `location` straight through therefore destroyed whichever of
|
|
63
|
+
// `{ country, city }` the caller happened to omit, and `location.city` is
|
|
64
|
+
// PII-encrypted, so the loss is unrecoverable. Same footgun the profile
|
|
65
|
+
// route documents for `uiPreferences`.
|
|
66
|
+
//
|
|
67
|
+
// Latent rather than live: both current callers pass no `data` at all, so
|
|
68
|
+
// this branch never runs today. It costs one read, and only on the branch
|
|
69
|
+
// that supplies a location — the hot activity ping stays a single write.
|
|
70
|
+
if (data?.location) {
|
|
71
|
+
const current = await this.findById(sessionId); // mapDoc → already decrypted
|
|
72
|
+
updateData.location = { ...(current?.location ?? {}), ...data.location };
|
|
73
|
+
}
|
|
62
74
|
try {
|
|
63
75
|
await this.update(sessionId, updateData);
|
|
64
76
|
}
|
|
@@ -11,10 +11,10 @@ export declare const createBlogPostSchema: z.ZodObject<{
|
|
|
11
11
|
isFeatured: z.ZodDefault<z.ZodBoolean>;
|
|
12
12
|
status: z.ZodDefault<z.ZodEnum<["draft", "published", "archived"]>>;
|
|
13
13
|
coverImage: z.ZodOptional<z.ZodNullable<z.ZodUnion<[z.ZodObject<{
|
|
14
|
-
url: z.ZodString
|
|
14
|
+
url: z.ZodEffects<z.ZodString, string, string>;
|
|
15
15
|
type: z.ZodEnum<["image", "video", "file"]>;
|
|
16
16
|
alt: z.ZodOptional<z.ZodString>;
|
|
17
|
-
thumbnailUrl: z.ZodOptional<z.ZodString
|
|
17
|
+
thumbnailUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
18
18
|
}, "strip", z.ZodTypeAny, {
|
|
19
19
|
type: "video" | "image" | "file";
|
|
20
20
|
url: string;
|
|
@@ -25,15 +25,15 @@ export declare const createBlogPostSchema: z.ZodObject<{
|
|
|
25
25
|
url: string;
|
|
26
26
|
alt?: string | undefined;
|
|
27
27
|
thumbnailUrl?: string | undefined;
|
|
28
|
-
}>, z.ZodEffects<z.ZodString, {
|
|
28
|
+
}>, z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, {
|
|
29
29
|
url: string;
|
|
30
30
|
type: "image";
|
|
31
31
|
}, string>]>>>;
|
|
32
32
|
contentImages: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
33
|
-
url: z.ZodString
|
|
33
|
+
url: z.ZodEffects<z.ZodString, string, string>;
|
|
34
34
|
type: z.ZodEnum<["image", "video", "file"]>;
|
|
35
35
|
alt: z.ZodOptional<z.ZodString>;
|
|
36
|
-
thumbnailUrl: z.ZodOptional<z.ZodString
|
|
36
|
+
thumbnailUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
37
37
|
}, "strip", z.ZodTypeAny, {
|
|
38
38
|
type: "video" | "image" | "file";
|
|
39
39
|
url: string;
|
|
@@ -46,10 +46,10 @@ export declare const createBlogPostSchema: z.ZodObject<{
|
|
|
46
46
|
thumbnailUrl?: string | undefined;
|
|
47
47
|
}>, "many">>>;
|
|
48
48
|
additionalImages: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
49
|
-
url: z.ZodString
|
|
49
|
+
url: z.ZodEffects<z.ZodString, string, string>;
|
|
50
50
|
type: z.ZodEnum<["image", "video", "file"]>;
|
|
51
51
|
alt: z.ZodOptional<z.ZodString>;
|
|
52
|
-
thumbnailUrl: z.ZodOptional<z.ZodString
|
|
52
|
+
thumbnailUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
53
53
|
}, "strip", z.ZodTypeAny, {
|
|
54
54
|
type: "video" | "image" | "file";
|
|
55
55
|
url: string;
|
|
@@ -150,10 +150,10 @@ export declare const updateBlogPostSchema: z.ZodObject<{
|
|
|
150
150
|
isFeatured: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
151
151
|
status: z.ZodOptional<z.ZodDefault<z.ZodEnum<["draft", "published", "archived"]>>>;
|
|
152
152
|
coverImage: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodUnion<[z.ZodObject<{
|
|
153
|
-
url: z.ZodString
|
|
153
|
+
url: z.ZodEffects<z.ZodString, string, string>;
|
|
154
154
|
type: z.ZodEnum<["image", "video", "file"]>;
|
|
155
155
|
alt: z.ZodOptional<z.ZodString>;
|
|
156
|
-
thumbnailUrl: z.ZodOptional<z.ZodString
|
|
156
|
+
thumbnailUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
157
157
|
}, "strip", z.ZodTypeAny, {
|
|
158
158
|
type: "video" | "image" | "file";
|
|
159
159
|
url: string;
|
|
@@ -164,15 +164,15 @@ export declare const updateBlogPostSchema: z.ZodObject<{
|
|
|
164
164
|
url: string;
|
|
165
165
|
alt?: string | undefined;
|
|
166
166
|
thumbnailUrl?: string | undefined;
|
|
167
|
-
}>, z.ZodEffects<z.ZodString, {
|
|
167
|
+
}>, z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, {
|
|
168
168
|
url: string;
|
|
169
169
|
type: "image";
|
|
170
170
|
}, string>]>>>>;
|
|
171
171
|
contentImages: z.ZodOptional<z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
172
|
-
url: z.ZodString
|
|
172
|
+
url: z.ZodEffects<z.ZodString, string, string>;
|
|
173
173
|
type: z.ZodEnum<["image", "video", "file"]>;
|
|
174
174
|
alt: z.ZodOptional<z.ZodString>;
|
|
175
|
-
thumbnailUrl: z.ZodOptional<z.ZodString
|
|
175
|
+
thumbnailUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
176
176
|
}, "strip", z.ZodTypeAny, {
|
|
177
177
|
type: "video" | "image" | "file";
|
|
178
178
|
url: string;
|
|
@@ -185,10 +185,10 @@ export declare const updateBlogPostSchema: z.ZodObject<{
|
|
|
185
185
|
thumbnailUrl?: string | undefined;
|
|
186
186
|
}>, "many">>>>;
|
|
187
187
|
additionalImages: z.ZodOptional<z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
188
|
-
url: z.ZodString
|
|
188
|
+
url: z.ZodEffects<z.ZodString, string, string>;
|
|
189
189
|
type: z.ZodEnum<["image", "video", "file"]>;
|
|
190
190
|
alt: z.ZodOptional<z.ZodString>;
|
|
191
|
-
thumbnailUrl: z.ZodOptional<z.ZodString
|
|
191
|
+
thumbnailUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
192
192
|
}, "strip", z.ZodTypeAny, {
|
|
193
193
|
type: "video" | "image" | "file";
|
|
194
194
|
url: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { sortBy } from "@mohasinac/appkit";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { mediaUrlSchema } from "../../../validation/schemas";
|
|
3
4
|
import { serverLogger } from "../../../monitoring";
|
|
4
5
|
import { blogRepository } from "../repository/blog.repository";
|
|
5
6
|
import { coerceMediaField, getMediaUrl } from "../../../utils";
|
|
@@ -8,23 +9,20 @@ import { finalizeStagedMediaField, finalizeStagedMediaObject, finalizeStagedMedi
|
|
|
8
9
|
const singleImageMediaSchema = z
|
|
9
10
|
.union([
|
|
10
11
|
z.object({
|
|
11
|
-
url:
|
|
12
|
+
url: mediaUrlSchema,
|
|
12
13
|
type: z.enum(["image", "video", "file"]),
|
|
13
14
|
alt: z.string().optional(),
|
|
14
|
-
thumbnailUrl:
|
|
15
|
+
thumbnailUrl: mediaUrlSchema.optional(),
|
|
15
16
|
}),
|
|
16
|
-
|
|
17
|
-
.string()
|
|
18
|
-
.url()
|
|
19
|
-
.transform((url) => ({ url, type: "image" })),
|
|
17
|
+
mediaUrlSchema.transform((url) => ({ url, type: "image" })),
|
|
20
18
|
])
|
|
21
19
|
.nullable()
|
|
22
20
|
.optional();
|
|
23
21
|
const mediaFieldSchema = z.object({
|
|
24
|
-
url:
|
|
22
|
+
url: mediaUrlSchema,
|
|
25
23
|
type: z.enum(["image", "video", "file"]),
|
|
26
24
|
alt: z.string().optional(),
|
|
27
|
-
thumbnailUrl:
|
|
25
|
+
thumbnailUrl: mediaUrlSchema.optional(),
|
|
28
26
|
});
|
|
29
27
|
export const createBlogPostSchema = z.object({
|
|
30
28
|
title: z.string().min(1),
|
|
@@ -267,7 +267,7 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
267
267
|
thumbnailUrl?: string | undefined;
|
|
268
268
|
youtubeId?: string | undefined;
|
|
269
269
|
duration?: number | undefined;
|
|
270
|
-
}>, z.ZodEffects<z.ZodString, {
|
|
270
|
+
}>, z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, {
|
|
271
271
|
url: string;
|
|
272
272
|
type: "image";
|
|
273
273
|
}, string>]>>>, {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./firestore";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { mediaUrlSchema } from "../../../validation/schemas";
|
|
3
4
|
import { mediaFieldSchema } from "../../media/types/index";
|
|
4
5
|
import { auditTimestampsShape, firestoreDateSchema } from "../../../schemas/firestore-helpers";
|
|
5
6
|
// ─── Firestore document schema (W2) ───────────────────────────────────────────
|
|
@@ -31,10 +32,7 @@ export const blogPostFirestoreSchema = z.object({
|
|
|
31
32
|
const blogCoverImageSchema = z
|
|
32
33
|
.union([
|
|
33
34
|
mediaFieldSchema,
|
|
34
|
-
|
|
35
|
-
.string()
|
|
36
|
-
.url()
|
|
37
|
-
.transform((url) => ({ url, type: "image" })),
|
|
35
|
+
mediaUrlSchema.transform((url) => ({ url, type: "image" })),
|
|
38
36
|
])
|
|
39
37
|
.nullable()
|
|
40
38
|
.optional()
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { NextResponse } from "next/server.js";
|
|
12
12
|
import { z } from "zod";
|
|
13
|
+
import { mediaUrlSchema } from "../../../../validation/schemas";
|
|
13
14
|
import { getProviders } from "../../../../contracts";
|
|
14
15
|
import { createRouteHandler } from "../../../../next";
|
|
15
16
|
import { normalizeError } from "../../../../errors/normalize";
|
|
@@ -48,7 +49,7 @@ const categoryUpdateSchema = z
|
|
|
48
49
|
tier: z.number().int().min(0).optional(),
|
|
49
50
|
order: z.number().int().min(0).optional(),
|
|
50
51
|
description: z.string().optional(),
|
|
51
|
-
imageUrl:
|
|
52
|
+
imageUrl: mediaUrlSchema.optional(),
|
|
52
53
|
isFeatured: z.boolean().optional(),
|
|
53
54
|
showOnHomepage: z.boolean().optional(),
|
|
54
55
|
isActive: z.boolean().optional(),
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { NextResponse } from "next/server.js";
|
|
16
16
|
import { z } from "zod";
|
|
17
|
+
import { mediaUrlSchema } from "../../../validation/schemas";
|
|
17
18
|
import { getProviders } from "../../../contracts";
|
|
18
19
|
import { createRouteHandler } from "../../../next";
|
|
19
20
|
import { normalizeError } from "../../../errors/normalize";
|
|
@@ -177,7 +178,7 @@ const categoryCreateSchema = z
|
|
|
177
178
|
tier: z.number().int().min(0).optional(),
|
|
178
179
|
order: z.number().int().min(0).optional(),
|
|
179
180
|
description: z.string().optional(),
|
|
180
|
-
imageUrl:
|
|
181
|
+
imageUrl: mediaUrlSchema.optional(),
|
|
181
182
|
isFeatured: z.boolean().optional(),
|
|
182
183
|
showOnHomepage: z.boolean().optional(),
|
|
183
184
|
})
|
|
@@ -467,7 +467,7 @@ export declare const eventItemSchema: z.ZodObject<{
|
|
|
467
467
|
thumbnailUrl?: string | undefined;
|
|
468
468
|
youtubeId?: string | undefined;
|
|
469
469
|
duration?: number | undefined;
|
|
470
|
-
}>, z.ZodEffects<z.ZodString, {
|
|
470
|
+
}>, z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, {
|
|
471
471
|
url: string;
|
|
472
472
|
type: "image";
|
|
473
473
|
}, string>]>>>, {
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
export * from "./firestore";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { mediaUrlSchema } from "../../../validation/schemas";
|
|
3
4
|
import { mediaFieldSchema } from "../../media/types/index";
|
|
4
5
|
const eventCoverImageSchema = z
|
|
5
6
|
.union([
|
|
6
7
|
mediaFieldSchema,
|
|
7
|
-
|
|
8
|
-
.string()
|
|
9
|
-
.url()
|
|
10
|
-
.transform((url) => ({ url, type: "image" })),
|
|
8
|
+
mediaUrlSchema.transform((url) => ({ url, type: "image" })),
|
|
11
9
|
])
|
|
12
10
|
.nullable()
|
|
13
11
|
.optional()
|
|
@@ -6,5 +6,5 @@ const __P = {
|
|
|
6
6
|
const CLS_CONTACT_CARD = "flex items-center gap-[var(--appkit-space-3)] rounded-lg border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-elevated)] px-[var(--appkit-space-4)] py-[var(--appkit-space-3)] hover:border-primary/40 transition-colors";
|
|
7
7
|
const CLS_CONTACT_LABEL = "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)]";
|
|
8
8
|
export function ContactCTA({ email, phone, contactHref, labels, }) {
|
|
9
|
-
return (_jsxs(Card, { variant: "outlined", padding: "lg", className: "text-center", children: [_jsx(Row, { className: "mb-6", justify: "center", children: _jsx(Div, { className: `bg-primary/10 ${__P.p8}`, rounded: "full", children: _jsx("svg", { className: "h-12 w-12 text-primary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" }) }) }) }), _jsx(Heading, { level: 2, className: "mb-3", size: "2xl", weight: "semibold", children: labels?.title ?? "Need More Help?" }), _jsx(Text, { className: "mx-auto mb-6 max-w-2xl", color: "muted", children: labels?.description ?? "Our support team is here to help you." }), _jsxs(Div, { layout: "grid", gap: "4", className: "mb-8 grid-cols-1 md:grid-cols-3", children: [_jsxs(TextLink, { href: `mailto:${email}`, variant: "none", className: CLS_CONTACT_CARD, children: [_jsx(Text, { className: CLS_CONTACT_LABEL, children: labels?.emailUs ?? "Email Us" }), _jsx(Text, { size: "xs", color: "muted", children: email })] }), _jsxs(TextLink, { href: `tel:${phone}`, variant: "none", className: CLS_CONTACT_CARD, children: [_jsx(Text, { className: CLS_CONTACT_LABEL, children: labels?.callUs ?? "Call Us" }), _jsx(Text, { size: "xs", color: "muted", children: phone })] }), _jsxs(TextLink, { href: contactHref, className: CLS_CONTACT_CARD, children: [_jsx(Text, { className: CLS_CONTACT_LABEL, children: labels?.contactForm ?? "Contact Form" }), _jsx(Text, { size: "xs", color: "muted", children: labels?.submitRequest ?? "Submit a request" })] })] }), _jsxs(TextLink, { rounded: "xl", href: contactHref, layout: "inline-flex", align: "center", gap: "sm", className: "bg-primary p-[1.5rem] text-white transition-colors hover:bg-primary/90", weight: "medium", children: [_jsx(Span, { children: labels?.contactTeam ?? "Contact Team" }), _jsx("svg", { className: "h-5 w-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })] })] }));
|
|
9
|
+
return (_jsxs(Card, { variant: "outlined", padding: "lg", className: "text-center", children: [_jsx(Row, { className: "mb-6", justify: "center", children: _jsx(Div, { className: `bg-primary/10 ${__P.p8}`, rounded: "full", children: _jsx("svg", { className: "h-12 w-12 text-primary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" }) }) }) }), _jsx(Heading, { level: 2, className: "mb-3", size: "2xl", weight: "semibold", children: labels?.title ?? "Need More Help?" }), _jsx(Text, { className: "mx-auto mb-6 max-w-2xl", color: "muted", children: labels?.description ?? "Our support team is here to help you." }), _jsxs(Div, { layout: "grid", gap: "4", className: "mb-8 grid-cols-1 md:grid-cols-3", children: [_jsxs(TextLink, { href: `mailto:${email}`, variant: "none", className: CLS_CONTACT_CARD, children: [_jsx(Text, { className: CLS_CONTACT_LABEL, children: labels?.emailUs ?? "Email Us" }), _jsx(Text, { size: "xs", color: "muted", children: email })] }), _jsxs(TextLink, { href: `tel:${phone}`, variant: "none", className: CLS_CONTACT_CARD, children: [_jsx(Text, { className: CLS_CONTACT_LABEL, children: labels?.callUs ?? "Call Us" }), _jsx(Text, { size: "xs", color: "muted", children: phone })] }), _jsxs(TextLink, { href: contactHref, className: CLS_CONTACT_CARD, children: [_jsx(Text, { className: CLS_CONTACT_LABEL, children: labels?.contactForm ?? "Contact Form" }), _jsx(Text, { size: "xs", color: "muted", children: labels?.submitRequest ?? "Submit a request" })] })] }), _jsxs(TextLink, { rounded: "xl", href: contactHref, layout: "inline-flex", align: "center", gap: "sm", className: "bg-primary p-[1.5rem] text-white transition-colors hover:bg-primary/90", weight: "medium", children: [_jsx(Span, { children: labels?.contactTeam ?? "Contact Team" }), _jsx("svg", { className: "h-5 w-5 shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })] })] }));
|
|
10
10
|
}
|
|
@@ -60,7 +60,7 @@ export function AsyncFacetSection({ title, selected, onChange, loadOptions, sele
|
|
|
60
60
|
const selectedLabels = selected
|
|
61
61
|
.map((v) => labelMap.current.get(v) ?? v)
|
|
62
62
|
.slice(0, 3);
|
|
63
|
-
return (_jsxs(Div, { className: cn("p-[var(--appkit-space-4)] border-b border-[var(--appkit-color-border)] last:border-b-0", className), children: [_jsxs(Row, { gap: "xs", children: [_jsxs(Button, { type: "button", onClick: () => setCollapsed((c) => !c), variant: "ghost", size: "sm", className: "flex-1 justify-between text-[length:var(--appkit-text-sm)] font-semibold text-[var(--appkit-color-text)] py-[var(--appkit-space-1)] hover:opacity-80 transition-opacity", "aria-expanded": !collapsed, children: [_jsxs(Row, { as: Span, gap: "xs", children: [title, hasValue && (_jsx(Span, { size: "xs", className: CLS_BADGE_COUNT, children: selected.length }))] }), _jsx("svg", { className: cn("w-4 h-4 text-[var(--appkit-color-text-muted)] transition-transform duration-200", collapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), onClear && hasValue && (_jsx(Button, { type: "button", onClick: onClear, variant: "ghost", size: "sm", className: CLS_CLEAR_ICON, "aria-label": "Clear filter", children: _jsx("svg", { className: "w-3 h-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), collapsed && hasValue && (_jsxs(Row, { wrap: true, gap: "xs", className: "mt-2", children: [selectedLabels.map((label) => (_jsx(Span, { layout: "inline-flex", size: "xs", border: "default", rounded: "full", padding: "pill-xs", surface: "subtle", color: "muted", children: label }, label))), selected.length > 3 && (_jsxs(Span, { layout: "inline-flex", size: "xs", rounded: "full", padding: "pill-xs", color: "faint", children: ["+", selected.length - 3, " more"] }))] })), _jsx(Div, { className: "mt-2", children: _jsx(Input, { type: "search", placeholder: "Search\u2026", value: query, onChange: (e) => handleQueryChange(e.target.value), className: "w-full rounded-md border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-input)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text)] focus:outline-none focus:ring-2 focus:ring-primary-500/20" }) }), !collapsed && (_jsxs(Stack, { className: "mt-3", gap: "xs", children: [options.map((opt) => {
|
|
63
|
+
return (_jsxs(Div, { className: cn("p-[var(--appkit-space-4)] border-b border-[var(--appkit-color-border)] last:border-b-0", className), children: [_jsxs(Row, { gap: "xs", children: [_jsxs(Button, { type: "button", onClick: () => setCollapsed((c) => !c), variant: "ghost", size: "sm", className: "flex-1 justify-between text-[length:var(--appkit-text-sm)] font-semibold text-[var(--appkit-color-text)] py-[var(--appkit-space-1)] hover:opacity-80 transition-opacity", "aria-expanded": !collapsed, children: [_jsxs(Row, { as: Span, gap: "xs", children: [title, hasValue && (_jsx(Span, { size: "xs", className: CLS_BADGE_COUNT, children: selected.length }))] }), _jsx("svg", { className: cn("w-4 h-4 shrink-0 text-[var(--appkit-color-text-muted)] transition-transform duration-200", collapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), onClear && hasValue && (_jsx(Button, { type: "button", onClick: onClear, variant: "ghost", size: "sm", className: CLS_CLEAR_ICON, "aria-label": "Clear filter", children: _jsx("svg", { className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), collapsed && hasValue && (_jsxs(Row, { wrap: true, gap: "xs", className: "mt-2", children: [selectedLabels.map((label) => (_jsx(Span, { layout: "inline-flex", size: "xs", border: "default", rounded: "full", padding: "pill-xs", surface: "subtle", color: "muted", children: label }, label))), selected.length > 3 && (_jsxs(Span, { layout: "inline-flex", size: "xs", rounded: "full", padding: "pill-xs", color: "faint", children: ["+", selected.length - 3, " more"] }))] })), _jsx(Div, { className: "mt-2", children: _jsx(Input, { type: "search", placeholder: "Search\u2026", value: query, onChange: (e) => handleQueryChange(e.target.value), className: "w-full rounded-md border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-input)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text)] focus:outline-none focus:ring-2 focus:ring-primary-500/20" }) }), !collapsed && (_jsxs(Stack, { className: "mt-3", gap: "xs", children: [options.map((opt) => {
|
|
64
64
|
const isSelected = selected.includes(opt.value);
|
|
65
65
|
return (_jsxs(Row, { gap: "2.5", className: "cursor-pointer group", padding: "y-2xs", children: [_jsx(Checkbox, { bare: true, type: selectionMode === "single" ? "radio" : "checkbox", checked: isSelected, onChange: () => toggle(opt.value), "aria-label": opt.label, className: "flex-shrink-0 w-4 h-4 rounded border-[var(--appkit-color-border)] cursor-pointer text-primary-600 dark:text-secondary-500" }), _jsx(Span, { size: "sm", className: "flex-1 group-hover:text-[var(--appkit-color-text)] transition-colors truncate", color: "muted", children: opt.label }), opt.count !== undefined && (_jsx(Span, { size: "xs", className: "tabular-nums flex-shrink-0", color: "faint", children: opt.count }))] }, opt.value));
|
|
66
66
|
}), options.length === 0 && !loading && (_jsx(Text, { paddingY: "2xs", color: "faint", size: "xs", children: "No results" })), loading && (_jsx(Text, { paddingY: "2xs", className: "animate-pulse", color: "faint", size: "xs", children: "Loading\u2026" })), hasMore && !loading && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", textSize: "xs", textColor: "faint", className: "w-full text-left hover:text-[var(--appkit-color-text)] py-[0.25rem]", onClick: () => void load(query, page + 1), children: "Load more\u2026" }))] }))] }));
|
|
@@ -83,7 +83,7 @@ export function FilterFacetSection({ title, options, selected, onChange, selecti
|
|
|
83
83
|
}), filtered.length === 0 && (_jsx(Text, { paddingY: "xs", paddingX: "x-sm", color: "faint", size: "xs", children: "No results" }))] }), hasValue && (_jsx(Div, { border: "subtle", className: "border-t p-[0.25rem]", children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: CLS_CLEAR_BTN, onClick: () => { onChange([]); onClear?.(); setDropdownOpen(false); }, children: "Clear" }) }))] }))] }));
|
|
84
84
|
}
|
|
85
85
|
// ── Accordion variant (default) ─────────────────────────────────────────
|
|
86
|
-
return (_jsxs(Div, { className: cn("p-[var(--appkit-space-4)] border-b border-[var(--appkit-color-border)] last:border-b-0", className), children: [_jsxs(Row, { gap: "xs", children: [_jsxs(Button, { type: "button", onClick: handleToggle, variant: "ghost", size: "sm", className: "flex-1 justify-[space-between] text-[0.875rem] font-[600] text-[var(--appkit-color-text)] py-[0.25rem] hover:opacity-80 transition-opacity", "aria-expanded": !isCollapsed, children: [_jsxs(Row, { as: Span, gap: "xs", children: [title, hasValue && (_jsx(Span, { size: "xs", className: CLS_BADGE_COUNT, children: selected.length }))] }), _jsx("svg", { className: cn("w-4 h-4 text-[var(--appkit-color-text-muted)] transition-transform duration-200", isCollapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), onClear && hasValue && (_jsx(Button, { type: "button", onClick: onClear, variant: "ghost", size: "sm", className: CLS_CLEAR_ICON, "aria-label": "Clear filter", children: _jsx("svg", { className: "w-3 h-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), isCollapsed && hasValue && (_jsxs(Row, { wrap: true, gap: "xs", className: "mt-2", children: [selectedLabels.map((label) => (_jsx(Span, { layout: "inline-flex", size: "xs", className: "border border-[var(--appkit-color-border)]", rounded: "full", padding: "pill-xs", surface: "subtle", color: "muted", children: label }, label))), selected.length > 3 && (_jsxs(Span, { layout: "inline-flex", size: "xs", rounded: "full", padding: "pill-xs", color: "faint", children: ["+", selected.length - 3, " more"] }))] })), searchable && (_jsx(Div, { className: "mt-2", children: _jsx(Input, { type: "search", placeholder: "Search\u2026", value: search, onChange: (e) => handleSearch(e.target.value), className: "w-full rounded-md border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-input)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text)] focus:outline-none focus:ring-2 focus:ring-primary-500/20 " }) })), !isCollapsed && (_jsxs(Stack, { className: "mt-3", gap: "xs", children: [filtered.map((option) => {
|
|
86
|
+
return (_jsxs(Div, { className: cn("p-[var(--appkit-space-4)] border-b border-[var(--appkit-color-border)] last:border-b-0", className), children: [_jsxs(Row, { gap: "xs", children: [_jsxs(Button, { type: "button", onClick: handleToggle, variant: "ghost", size: "sm", className: "flex-1 justify-[space-between] text-[0.875rem] font-[600] text-[var(--appkit-color-text)] py-[0.25rem] hover:opacity-80 transition-opacity", "aria-expanded": !isCollapsed, children: [_jsxs(Row, { as: Span, gap: "xs", children: [title, hasValue && (_jsx(Span, { size: "xs", className: CLS_BADGE_COUNT, children: selected.length }))] }), _jsx("svg", { className: cn("w-4 h-4 shrink-0 text-[var(--appkit-color-text-muted)] transition-transform duration-200", isCollapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), onClear && hasValue && (_jsx(Button, { type: "button", onClick: onClear, variant: "ghost", size: "sm", className: CLS_CLEAR_ICON, "aria-label": "Clear filter", children: _jsx("svg", { className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), isCollapsed && hasValue && (_jsxs(Row, { wrap: true, gap: "xs", className: "mt-2", children: [selectedLabels.map((label) => (_jsx(Span, { layout: "inline-flex", size: "xs", className: "border border-[var(--appkit-color-border)]", rounded: "full", padding: "pill-xs", surface: "subtle", color: "muted", children: label }, label))), selected.length > 3 && (_jsxs(Span, { layout: "inline-flex", size: "xs", rounded: "full", padding: "pill-xs", color: "faint", children: ["+", selected.length - 3, " more"] }))] })), searchable && (_jsx(Div, { className: "mt-2", children: _jsx(Input, { type: "search", placeholder: "Search\u2026", value: search, onChange: (e) => handleSearch(e.target.value), className: "w-full rounded-md border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-input)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text)] focus:outline-none focus:ring-2 focus:ring-primary-500/20 " }) })), !isCollapsed && (_jsxs(Stack, { className: "mt-3", gap: "xs", children: [filtered.map((option) => {
|
|
87
87
|
const isSelected = selected.includes(option.value);
|
|
88
88
|
return (_jsxs(Row, { gap: "2.5", className: "cursor-pointer group", padding: "y-2xs", children: [selectionMode === "single" ? (_jsx(Checkbox, { bare: true, type: "radio", checked: isSelected, onChange: () => toggle(option.value), "aria-label": option.label, className: cn("flex-shrink-0 border-[var(--appkit-color-border)] cursor-pointer", "w-4 h-4 rounded-full text-primary-600 dark:text-secondary-500", "focus:ring-primary-500/30 dark:focus:ring-secondary-400/30") })) : (_jsx(Checkbox, { bare: true, type: "checkbox", checked: isSelected, onChange: () => toggle(option.value), "aria-label": option.label, className: cn("flex-shrink-0 border-[var(--appkit-color-border)] cursor-pointer", "w-4 h-4 rounded text-primary-600 dark:text-secondary-500 checked:bg-primary-600 dark:checked:bg-secondary-500", "focus:ring-primary-500/30 dark:focus:ring-secondary-400/30") })), _jsx(Span, { size: "sm", className: "flex-1 group-hover:text-[var(--appkit-color-text)] transition-colors truncate", color: "muted", children: option.label }), option.count !== undefined && (_jsx(Span, { size: "xs", className: "tabular-nums flex-shrink-0", color: "faint", children: option.count }))] }, option.value));
|
|
89
89
|
}), filtered.length === 0 && (_jsx(Text, { paddingY: "2xs", color: "faint", size: "xs", children: "No results" }))] }))] }));
|
|
@@ -26,5 +26,5 @@ export function RangeFilter({ title, minLabel, maxLabel, minValue, maxValue, onM
|
|
|
26
26
|
maxBound !== undefined;
|
|
27
27
|
const hasValue = !!(minValue || maxValue);
|
|
28
28
|
const inputClass = "w-full rounded-md border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-input)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text)] focus:outline-none focus:ring-2 focus:ring-primary-500/20 ";
|
|
29
|
-
return (_jsxs(Div, { role: "group", "aria-labelledby": `rf-${title}`, className: cn("p-[var(--appkit-space-4)] border-b border-[var(--appkit-color-border)] last:border-b-0", className), children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsxs(Button, { type: "button", id: `rf-${title}`, onClick: handleToggle, variant: "ghost", size: "sm", className: "flex-1 justify-[space-between] text-[0.875rem] font-[600] text-[var(--appkit-color-text)] py-[0.25rem] hover:opacity-80 transition-opacity", "aria-expanded": !isCollapsed, children: [_jsxs(Span, { layout: "flex", gap: "md", children: [title, hasValue && (_jsx(Span, { size: "xs", className: CLS_BADGE_COUNT, children: "1" }))] }), _jsx("svg", { className: cn("w-4 h-4 text-[var(--appkit-color-text-muted)] transition-transform duration-200", isCollapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), onClear && hasValue && (_jsx(Button, { type: "button", onClick: onClear, variant: "ghost", size: "sm", className: CLS_CLEAR_BTN, "aria-label": "Clear", children: _jsx("svg", { className: "w-3 h-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), !isCollapsed && (_jsxs(Stack, { gap: "3", className: "mt-3", children: [canShowSlider && (_jsx(DualSlider, { minValue: minValue, maxValue: maxValue, onMinChange: onMinChange, onMaxChange: onMaxChange, minBound: minBound, maxBound: maxBound, step: step, prefix: prefix })), _jsxs(Row, { align: "end", gap: "sm", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [minLabel && (_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: minLabel })), _jsxs(Div, { className: "relative", children: [prefix && (_jsx(Span, { size: "xs", className: "absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none", color: "faint", children: prefix })), _jsx(Input, { type: type, value: minValue, onChange: (e) => onMinChange(e.target.value), placeholder: minPlaceholder ?? (type === "date" ? "From" : "Min"), className: cn(inputClass, prefix ? "pl-5" : "") })] })] }), _jsx(Span, { size: "xs", className: "flex-shrink-0 pb-[0.375rem]", color: "faint", children: "-" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [maxLabel && (_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: maxLabel })), _jsxs(Div, { className: "relative", children: [prefix && (_jsx(Span, { size: "xs", className: "absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none", color: "faint", children: prefix })), _jsx(Input, { type: type, value: maxValue, onChange: (e) => onMaxChange(e.target.value), placeholder: maxPlaceholder ?? (type === "date" ? "To" : "Max"), className: cn(inputClass, prefix ? "pl-5" : "") })] })] })] })] }))] }));
|
|
29
|
+
return (_jsxs(Div, { role: "group", "aria-labelledby": `rf-${title}`, className: cn("p-[var(--appkit-space-4)] border-b border-[var(--appkit-color-border)] last:border-b-0", className), children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsxs(Button, { type: "button", id: `rf-${title}`, onClick: handleToggle, variant: "ghost", size: "sm", className: "flex-1 justify-[space-between] text-[0.875rem] font-[600] text-[var(--appkit-color-text)] py-[0.25rem] hover:opacity-80 transition-opacity", "aria-expanded": !isCollapsed, children: [_jsxs(Span, { layout: "flex", gap: "md", children: [title, hasValue && (_jsx(Span, { size: "xs", className: CLS_BADGE_COUNT, children: "1" }))] }), _jsx("svg", { className: cn("w-4 h-4 shrink-0 text-[var(--appkit-color-text-muted)] transition-transform duration-200", isCollapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), onClear && hasValue && (_jsx(Button, { type: "button", onClick: onClear, variant: "ghost", size: "sm", className: CLS_CLEAR_BTN, "aria-label": "Clear", children: _jsx("svg", { className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), !isCollapsed && (_jsxs(Stack, { gap: "3", className: "mt-3", children: [canShowSlider && (_jsx(DualSlider, { minValue: minValue, maxValue: maxValue, onMinChange: onMinChange, onMaxChange: onMaxChange, minBound: minBound, maxBound: maxBound, step: step, prefix: prefix })), _jsxs(Row, { align: "end", gap: "sm", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [minLabel && (_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: minLabel })), _jsxs(Div, { className: "relative", children: [prefix && (_jsx(Span, { size: "xs", className: "absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none", color: "faint", children: prefix })), _jsx(Input, { type: type, value: minValue, onChange: (e) => onMinChange(e.target.value), placeholder: minPlaceholder ?? (type === "date" ? "From" : "Min"), className: cn(inputClass, prefix ? "pl-5" : "") })] })] }), _jsx(Span, { size: "xs", className: "flex-shrink-0 pb-[0.375rem]", color: "faint", children: "-" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [maxLabel && (_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: maxLabel })), _jsxs(Div, { className: "relative", children: [prefix && (_jsx(Span, { size: "xs", className: "absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none", color: "faint", children: prefix })), _jsx(Input, { type: type, value: maxValue, onChange: (e) => onMaxChange(e.target.value), placeholder: maxPlaceholder ?? (type === "date" ? "To" : "Max"), className: cn(inputClass, prefix ? "pl-5" : "") })] })] })] })] }))] }));
|
|
30
30
|
}
|
|
@@ -14,5 +14,5 @@ export function SwitchFilter({ title, label, checked, onChange, defaultCollapsed
|
|
|
14
14
|
else
|
|
15
15
|
setInternalCollapsed((c) => !c);
|
|
16
16
|
};
|
|
17
|
-
return (_jsxs(Div, { role: "group", "aria-labelledby": `sf-${title}`, className: cn("p-[var(--appkit-space-4)] border-b border-[var(--appkit-color-border)] last:border-b-0", className), children: [_jsxs(Row, { gap: "sm", children: [_jsxs(Button, { type: "button", id: `sf-${title}`, onClick: handleToggle, variant: "ghost", size: "sm", className: "flex-1 justify-[space-between] text-[0.875rem] font-[600] text-[var(--appkit-color-text)] py-[0.25rem] hover:opacity-80 transition-opacity", "aria-expanded": !isCollapsed, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: title }), _jsx("svg", { className: cn("w-4 h-4 text-[var(--appkit-color-text-muted)] transition-transform duration-200", isCollapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), checked && onClear && (_jsx(Button, { type: "button", onClick: onClear, variant: "ghost", size: "sm", className: CLS_CLEAR_BTN, "aria-label": "Clear", children: _jsx("svg", { className: "w-3 h-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), !isCollapsed && (_jsxs(Row, { className: "mt-3", align: "center", justify: "between", gap: "3", children: [_jsx(Span, { size: "sm", color: "muted", children: label }), _jsx(Toggle, { checked: checked, onChange: onChange, size: "sm" })] }))] }));
|
|
17
|
+
return (_jsxs(Div, { role: "group", "aria-labelledby": `sf-${title}`, className: cn("p-[var(--appkit-space-4)] border-b border-[var(--appkit-color-border)] last:border-b-0", className), children: [_jsxs(Row, { gap: "sm", children: [_jsxs(Button, { type: "button", id: `sf-${title}`, onClick: handleToggle, variant: "ghost", size: "sm", className: "flex-1 justify-[space-between] text-[0.875rem] font-[600] text-[var(--appkit-color-text)] py-[0.25rem] hover:opacity-80 transition-opacity", "aria-expanded": !isCollapsed, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: title }), _jsx("svg", { className: cn("w-4 h-4 shrink-0 text-[var(--appkit-color-text-muted)] transition-transform duration-200", isCollapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), checked && onClear && (_jsx(Button, { type: "button", onClick: onClear, variant: "ghost", size: "sm", className: CLS_CLEAR_BTN, "aria-label": "Clear", children: _jsx("svg", { className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), !isCollapsed && (_jsxs(Row, { className: "mt-3", align: "center", justify: "between", gap: "3", children: [_jsx(Span, { size: "sm", color: "muted", children: label }), _jsx(Toggle, { checked: checked, onChange: onChange, size: "sm" })] }))] }));
|
|
18
18
|
}
|