@mohasinac/appkit 4.14.0 → 4.16.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/server/features/auctions/actions.js +2 -2
- package/dist/_internal/server/features/cart/service.d.ts +3 -3
- package/dist/_internal/server/features/checkout/actions.js +19 -0
- package/dist/_internal/server/features/checkout/source-context.d.ts +5 -0
- package/dist/_internal/server/features/checkout/source-context.js +157 -0
- package/dist/_internal/server/features/offers/adapters.d.ts +103 -0
- package/dist/_internal/server/features/offers/adapters.js +88 -0
- package/dist/_internal/server/features/offers/index.d.ts +2 -0
- package/dist/_internal/server/features/offers/index.js +1 -0
- package/dist/_internal/server/features/orders/adapters.js +24 -0
- package/dist/_internal/server/jobs/core/offerExpiry.js +10 -10
- package/dist/_internal/shared/actions/action-registry.d.ts +9 -2
- package/dist/_internal/shared/actions/action-registry.js +458 -7
- package/dist/_internal/shared/features/auctions/schema.d.ts +14 -2
- package/dist/_internal/shared/features/auctions/schema.js +13 -1
- package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
- package/dist/_internal/shared/features/cart/schema.d.ts +25 -20
- package/dist/_internal/shared/features/cart/schema.js +15 -0
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +22 -22
- package/dist/_internal/shared/features/orders/schema.d.ts +4 -4
- package/dist/_internal/shared/features/products/schema.d.ts +27 -27
- package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
- package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
- package/dist/_internal/shared/history/history.d.ts +22 -0
- package/dist/_internal/shared/history/history.js +41 -1
- package/dist/client.d.ts +13 -2
- package/dist/client.js +20 -2
- package/dist/constants/field-names.d.ts +8 -6
- package/dist/constants/field-names.js +48 -6
- package/dist/contracts/extend.d.ts +29 -0
- package/dist/contracts/extend.js +20 -0
- package/dist/contracts/index.d.ts +2 -1
- package/dist/contracts/index.js +2 -0
- package/dist/features/account/components/UserBidsView.js +12 -4
- package/dist/features/account/components/UserReturnsView.d.ts +3 -0
- package/dist/features/account/components/UserReturnsView.js +17 -1
- package/dist/features/admin/components/AdminAuditLogView.js +1 -0
- package/dist/features/admin/components/AdminBidsView.js +15 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +65 -33
- package/dist/features/admin/components/AdminFeatureEditorView.js +35 -6
- package/dist/features/admin/components/AdminNavEditorView.js +15 -5
- package/dist/features/admin/components/AdminNewsletterView.js +18 -2
- package/dist/features/admin/components/AdminOffersView.js +62 -18
- package/dist/features/admin/components/AdminPaymentMethodsView.js +24 -4
- package/dist/features/admin/components/AdminScammerEditorView.d.ts +2 -1
- package/dist/features/admin/components/AdminScammerEditorView.js +3 -13
- package/dist/features/admin/components/AdminScammersView.js +3 -8
- package/dist/features/admin/components/AdminSectionsView.js +32 -23
- package/dist/features/admin/components/AdminSessionsView.js +17 -2
- package/dist/features/admin/components/AdminViewCards.js +24 -17
- package/dist/features/admin/components/DataListingView.js +15 -1
- package/dist/features/admin/components/DataTable.js +7 -17
- package/dist/features/admin/components/ViewAuditLogEntryModal.js +1 -0
- package/dist/features/admin/components/sections/adminSectionsTypes.d.ts +4 -2
- package/dist/features/admin/components/sections/adminSectionsTypes.js +45 -27
- package/dist/features/admin/constants/filter-tabs.d.ts +3 -0
- package/dist/features/admin/constants/filter-tabs.js +1 -0
- package/dist/features/admin/schemas/homepage-section-form.d.ts +22 -0
- package/dist/features/admin/schemas/homepage-section-form.js +68 -0
- package/dist/features/admin/schemas/nav-item-form.d.ts +74 -0
- package/dist/features/admin/schemas/nav-item-form.js +72 -0
- package/dist/features/auctions/components/AuctionBidsTable.d.ts +10 -1
- package/dist/features/auctions/components/AuctionBidsTable.js +22 -4
- package/dist/features/auctions/components/bid-detail-fields.d.ts +29 -0
- package/dist/features/auctions/components/bid-detail-fields.js +103 -0
- package/dist/features/auctions/schemas/index.d.ts +14 -14
- package/dist/features/audit-log/schemas/firestore.d.ts +14 -0
- package/dist/features/audit-log/schemas/firestore.js +7 -0
- package/dist/features/auth/schemas/index.d.ts +2 -2
- package/dist/features/blog/actions/blog-actions.d.ts +7 -7
- package/dist/features/blog/schemas/blog-form.d.ts +80 -0
- package/dist/features/blog/schemas/blog-form.js +100 -0
- package/dist/features/blog/schemas/index.d.ts +10 -10
- package/dist/features/cart/schemas/index.d.ts +3 -3
- package/dist/features/catalogue/schemas/validation.d.ts +12 -12
- package/dist/features/categories/schemas/index.d.ts +16 -16
- package/dist/features/collections/schemas/index.d.ts +8 -8
- package/dist/features/events/components/AdminEventEditorView.js +32 -54
- package/dist/features/events/components/AdminEventEntriesView.js +22 -3
- package/dist/features/events/schemas/event-form.d.ts +113 -0
- package/dist/features/events/schemas/event-form.js +82 -0
- package/dist/features/events/schemas/index.d.ts +2 -2
- package/dist/features/faq/actions/faq-actions.d.ts +6 -6
- package/dist/features/faq/schemas/index.d.ts +6 -6
- package/dist/features/grouped/schemas/grouped-listing-form.d.ts +101 -0
- package/dist/features/grouped/schemas/grouped-listing-form.js +89 -0
- package/dist/features/grouped/schemas/index.d.ts +4 -4
- package/dist/features/homepage/actions/homepage-section-actions.d.ts +11 -3
- package/dist/features/homepage/actions/homepage-section-actions.js +33 -1
- package/dist/features/homepage/schemas/index.d.ts +71 -40
- package/dist/features/homepage/schemas/index.js +24 -0
- package/dist/features/messages/schemas/index.d.ts +2 -2
- package/dist/features/orders/components/OrderStatusTimeline.d.ts +5 -1
- package/dist/features/orders/components/OrderStatusTimeline.js +39 -20
- package/dist/features/orders/repository/orders.repository.js +5 -0
- package/dist/features/orders/schemas/firestore.d.ts +36 -1
- package/dist/features/orders/schemas/index.d.ts +24 -24
- package/dist/features/orders/types/index.d.ts +35 -7
- package/dist/features/pre-orders/schemas/index.d.ts +4 -4
- package/dist/features/products/components/GroupSettingsPanel.js +5 -1
- package/dist/features/products/schemas/index.d.ts +38 -38
- package/dist/features/products/schemas/product-features.validators.d.ts +75 -2
- package/dist/features/products/schemas/product-features.validators.js +63 -0
- package/dist/features/promotions/schemas/index.d.ts +14 -14
- package/dist/features/reviews/schemas/index.d.ts +14 -14
- package/dist/features/scams/constants/scammer-status-display.d.ts +28 -0
- package/dist/features/scams/constants/scammer-status-display.js +79 -0
- package/dist/features/scams/schemas/index.d.ts +2 -2
- package/dist/features/search/schemas/index.d.ts +2 -2
- package/dist/features/seller/actions/offer-actions.js +32 -2
- package/dist/features/seller/components/OfferPhaseTimeline.d.ts +59 -0
- package/dist/features/seller/components/OfferPhaseTimeline.js +107 -0
- package/dist/features/seller/components/SellerBidsView.js +11 -4
- package/dist/features/seller/components/SellerCouponEditorView.js +17 -17
- package/dist/features/seller/components/SellerOffersView.js +2 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +38 -14
- package/dist/features/seller/repository/offer.repository.d.ts +69 -8
- package/dist/features/seller/repository/offer.repository.js +127 -23
- package/dist/features/seller/schemas/coupon-form.d.ts +72 -0
- package/dist/features/seller/schemas/coupon-form.js +116 -0
- package/dist/features/seller/schemas/firestore.d.ts +56 -2
- package/dist/features/seller/schemas/firestore.js +27 -0
- package/dist/features/seller/schemas/index.d.ts +2 -2
- package/dist/features/seller/schemas/offer-forms.d.ts +21 -0
- package/dist/features/seller/schemas/offer-forms.js +21 -0
- package/dist/features/shell/build-sections.d.ts +63 -0
- package/dist/features/shell/build-sections.js +207 -0
- package/dist/features/shell/field-ui-meta.d.ts +115 -0
- package/dist/features/shell/field-ui-meta.js +324 -0
- package/dist/features/shell/index.d.ts +4 -0
- package/dist/features/shell/index.js +2 -0
- package/dist/features/shipments/schemas/validation.d.ts +22 -22
- package/dist/features/status-history/components/StatusTimeline.d.ts +74 -0
- package/dist/features/status-history/components/StatusTimeline.js +46 -0
- package/dist/features/status-history/index.d.ts +11 -0
- package/dist/features/status-history/index.js +10 -0
- package/dist/features/store-extensions/components/ReviewDecisionModal.d.ts +27 -0
- package/dist/features/store-extensions/components/ReviewDecisionModal.js +82 -0
- package/dist/features/store-extensions/index.d.ts +12 -0
- package/dist/features/store-extensions/index.js +12 -0
- package/dist/features/store-extensions/schemas/admin-notification-form.d.ts +36 -0
- package/dist/features/store-extensions/schemas/admin-notification-form.js +114 -0
- package/dist/features/store-extensions/schemas/analytics-forms.d.ts +57 -0
- package/dist/features/store-extensions/schemas/analytics-forms.js +158 -0
- package/dist/features/store-extensions/schemas/custom-role-form.d.ts +85 -0
- package/dist/features/store-extensions/schemas/custom-role-form.js +118 -0
- package/dist/features/store-extensions/schemas/firestore.d.ts +21 -1
- package/dist/features/store-extensions/schemas/firestore.js +60 -0
- package/dist/features/store-extensions/schemas/google-config-form.d.ts +53 -0
- package/dist/features/store-extensions/schemas/google-config-form.js +105 -0
- package/dist/features/store-extensions/schemas/item-request-create-form.d.ts +28 -0
- package/dist/features/store-extensions/schemas/item-request-create-form.js +80 -0
- package/dist/features/store-extensions/schemas/listing-template-form.d.ts +78 -0
- package/dist/features/store-extensions/schemas/listing-template-form.js +87 -0
- package/dist/features/store-extensions/schemas/moderation-review-form.d.ts +49 -0
- package/dist/features/store-extensions/schemas/moderation-review-form.js +101 -0
- package/dist/features/store-extensions/schemas/payout-method-form.d.ts +153 -0
- package/dist/features/store-extensions/schemas/payout-method-form.js +155 -0
- package/dist/features/store-extensions/schemas/report-create-form.d.ts +25 -0
- package/dist/features/store-extensions/schemas/report-create-form.js +79 -0
- package/dist/features/store-extensions/schemas/report-review-form.d.ts +47 -0
- package/dist/features/store-extensions/schemas/report-review-form.js +111 -0
- package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +148 -0
- package/dist/features/store-extensions/schemas/shipping-config-form.js +104 -0
- package/dist/features/store-extensions/schemas/store-category-form.d.ts +54 -0
- package/dist/features/store-extensions/schemas/store-category-form.js +73 -0
- package/dist/features/stores/schemas/index.d.ts +4 -4
- package/dist/features/support/schemas/index.d.ts +4 -4
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +350 -0
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.js +12 -2
- package/dist/next/routing/route-map.d.ts +16 -2
- package/dist/next/routing/route-map.js +9 -2
- package/dist/schemas/firestore-value.d.ts +8 -0
- package/dist/schemas/firestore-value.js +44 -0
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/index.js +1 -1
- package/dist/schemas/registry.d.ts +734 -101
- package/dist/schemas/registry.js +64 -1
- package/dist/seed/bids-seed-data.js +30 -0
- package/dist/seed/offers-seed-data.js +212 -7
- package/dist/seed/orders-seed-data.js +336 -1
- package/dist/seed/products-auctions-seed-data.js +41 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +5 -0
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/DataTable.js +4 -2
- package/dist/ui/columns/build-columns.d.ts +6 -1
- package/dist/ui/columns/build-columns.js +53 -0
- package/dist/ui/columns/cell-renderers.d.ts +68 -0
- package/dist/ui/columns/cell-renderers.js +111 -0
- package/dist/ui/columns/column-renderers.d.ts +6 -1
- package/dist/ui/columns/column-renderers.js +6 -1
- package/dist/ui/columns/index.d.ts +2 -1
- package/dist/ui/columns/index.js +5 -1
- package/dist/ui/components/Button.js +27 -2
- package/dist/ui/components/RecordDetailModal.d.ts +10 -1
- package/dist/ui/components/RecordDetailModal.js +2 -2
- package/dist/ui/icons/icon-registry.d.ts +65 -0
- package/dist/ui/icons/icon-registry.js +115 -0
- package/package.json +2 -1
- package/scripts/node-esm-loader.mjs +65 -0
- package/scripts/seed-cli-loader.mjs +6 -49
- package/scripts/seed-cli.mjs +1 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/*
|
|
4
|
+
* WHY: Four surfaces asked a moderator to justify a decision, and not one of
|
|
5
|
+
* them checked that they had. `admin/moderation/page.tsx` and
|
|
6
|
+
* `ModerationDetailActions` each rendered their own reject-reason modal;
|
|
7
|
+
* `admin/reports/page.tsx` and `ReportDetailActions` each rendered their
|
|
8
|
+
* own resolution-note modal. All four submitted an empty string happily,
|
|
9
|
+
* so media could be blocked and a report closed with no record of why.
|
|
10
|
+
*
|
|
11
|
+
* WHAT: One modal for both decisions.
|
|
12
|
+
*
|
|
13
|
+
* ## Why one component and not two
|
|
14
|
+
*
|
|
15
|
+
* The two are the same observable thing — *a decision plus a note that is
|
|
16
|
+
* required before it can be made* — differing only in vocabulary. Per the
|
|
17
|
+
* Duplication Decision Framework this is a consolidate, on two counts: the
|
|
18
|
+
* copies have the same prop surface, and a single validation fix would
|
|
19
|
+
* otherwise have needed four commits. The differences are all DATA (labels,
|
|
20
|
+
* which field carries the note, which schema validates it), not branching, so
|
|
21
|
+
* there are no conditional props to trade away.
|
|
22
|
+
*
|
|
23
|
+
* ## The note field is named by the caller
|
|
24
|
+
*
|
|
25
|
+
* Moderation calls it `reason`, reports call it `resolution`. Naming it
|
|
26
|
+
* generically here would mean translating on the way out, and a schema issue
|
|
27
|
+
* on `resolution` would then have no field to land on — the error would fall
|
|
28
|
+
* back to a banner, which is the thing this replaces.
|
|
29
|
+
*
|
|
30
|
+
* EXPORTS: ReviewDecisionModal, type ReviewDecisionModalProps
|
|
31
|
+
*
|
|
32
|
+
* @tag domain:store-extensions,moderation,reports
|
|
33
|
+
* @tag layer:component
|
|
34
|
+
* @tag pattern:form
|
|
35
|
+
* @tag access:client
|
|
36
|
+
* @tag consumers:admin/moderation,admin/reports
|
|
37
|
+
* @tag sideEffects:none
|
|
38
|
+
*/
|
|
39
|
+
import { useState } from "react";
|
|
40
|
+
import { Modal } from "../../../ui/components/Modal";
|
|
41
|
+
import { Button } from "../../../ui/components/Button";
|
|
42
|
+
import { Row } from "../../../ui/components/Layout";
|
|
43
|
+
import { Stack } from "../../../ui/components/Layout";
|
|
44
|
+
import { Form } from "../../../ui/components/Form";
|
|
45
|
+
import { FieldTextarea } from "../../../ui/forms/FieldTextarea";
|
|
46
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
47
|
+
import { applyZodIssues } from "../../../ui/forms/FormShell";
|
|
48
|
+
import { useToast } from "../../../ui/components/Toast";
|
|
49
|
+
import { normalizeError } from "../../../errors/normalize";
|
|
50
|
+
export function ReviewDecisionModal({ isOpen, onClose, title, schema, status, noteField, noteLabel, noteHelp, notePlaceholder, confirmLabel, confirmVariant = "primary", onConfirm, }) {
|
|
51
|
+
const { showToast } = useToast();
|
|
52
|
+
const [note, setNote] = useState("");
|
|
53
|
+
const [submitting, setSubmitting] = useState(false);
|
|
54
|
+
const close = () => {
|
|
55
|
+
setNote("");
|
|
56
|
+
onClose();
|
|
57
|
+
};
|
|
58
|
+
return (_jsx(Modal, { isOpen: isOpen, onClose: close, title: title, size: "sm", children: _jsx(Form, { schema: schema, onSubmit: (e) => e.preventDefault(), children: ({ setFieldError, clearErrors }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FormErrorSummary, {}), _jsx(FieldTextarea, { name: noteField, label: noteLabel, hint: noteHelp, rows: 4, required: true, value: note, onChange: setNote, placeholder: notePlaceholder }), _jsxs(Row, { justify: "end", gap: "sm", children: [_jsx(Button, { variant: "ghost", type: "button", onClick: close, disabled: submitting, children: "Cancel" }), _jsx(Button, { variant: confirmVariant, type: "submit", disabled: submitting, isLoading: submitting, onClick: async () => {
|
|
59
|
+
clearErrors();
|
|
60
|
+
const payload = { status, [noteField]: note };
|
|
61
|
+
const parsed = schema.safeParse(payload);
|
|
62
|
+
if (!parsed.success) {
|
|
63
|
+
applyZodIssues(parsed.error.issues, setFieldError);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
setSubmitting(true);
|
|
67
|
+
try {
|
|
68
|
+
await onConfirm(parsed.data);
|
|
69
|
+
close();
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
// Deliberately stays OPEN on failure. Closing would discard
|
|
73
|
+
// the note the reviewer just typed, and a decision that did
|
|
74
|
+
// not save must not look like one that did.
|
|
75
|
+
void normalizeError(err);
|
|
76
|
+
showToast("Couldn't save that decision. Try again.", "error");
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
setSubmitting(false);
|
|
80
|
+
}
|
|
81
|
+
}, children: confirmLabel })] })] })) }) }));
|
|
82
|
+
}
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
export * from "./schemas/firestore";
|
|
2
|
+
export * from "./schemas/store-category-form";
|
|
3
|
+
export * from "./schemas/listing-template-form";
|
|
4
|
+
export * from "./schemas/payout-method-form";
|
|
5
|
+
export * from "./schemas/shipping-config-form";
|
|
6
|
+
export * from "./schemas/custom-role-form";
|
|
7
|
+
export * from "./schemas/moderation-review-form";
|
|
8
|
+
export * from "./schemas/report-review-form";
|
|
9
|
+
export * from "./schemas/report-create-form";
|
|
10
|
+
export * from "./schemas/item-request-create-form";
|
|
11
|
+
export * from "./schemas/analytics-forms";
|
|
12
|
+
export * from "./schemas/google-config-form";
|
|
13
|
+
export * from "./schemas/admin-notification-form";
|
|
2
14
|
export * from "./schemas/rbac";
|
|
3
15
|
export * from "./repository/store-extensions.repositories";
|
|
4
16
|
export * from "./repository/rbac.repositories";
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
export * from "./schemas/firestore";
|
|
2
|
+
export * from "./schemas/store-category-form";
|
|
3
|
+
export * from "./schemas/listing-template-form";
|
|
4
|
+
export * from "./schemas/payout-method-form";
|
|
5
|
+
export * from "./schemas/shipping-config-form";
|
|
6
|
+
export * from "./schemas/custom-role-form";
|
|
7
|
+
export * from "./schemas/moderation-review-form";
|
|
8
|
+
export * from "./schemas/report-review-form";
|
|
9
|
+
export * from "./schemas/report-create-form";
|
|
10
|
+
export * from "./schemas/item-request-create-form";
|
|
11
|
+
export * from "./schemas/analytics-forms";
|
|
12
|
+
export * from "./schemas/google-config-form";
|
|
13
|
+
export * from "./schemas/admin-notification-form";
|
|
2
14
|
export * from "./schemas/rbac";
|
|
3
15
|
export * from "./repository/store-extensions.repositories";
|
|
4
16
|
export * from "./repository/rbac.repositories";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { AdminNotificationCategory, AdminNotificationDocument } from "./rbac";
|
|
3
|
+
export declare const ADMIN_NOTIFICATION_CATEGORIES: [AdminNotificationCategory, ...AdminNotificationCategory[]];
|
|
4
|
+
type Severity = AdminNotificationDocument["severity"];
|
|
5
|
+
export declare const ADMIN_NOTIFICATION_SEVERITIES: [Severity, ...Severity[]];
|
|
6
|
+
/**
|
|
7
|
+
* 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
|
|
8
|
+
* by schema instance and every zod wrapper returns a new one.
|
|
9
|
+
*/
|
|
10
|
+
export declare const adminNotificationCreateSchema: z.ZodObject<{
|
|
11
|
+
title: z.ZodString;
|
|
12
|
+
body: z.ZodString;
|
|
13
|
+
category: z.ZodEnum<[AdminNotificationCategory, ...AdminNotificationCategory[]]>;
|
|
14
|
+
severity: z.ZodEnum<["warning" | "error" | "info", ...("warning" | "error" | "info")[]]>;
|
|
15
|
+
audienceUserIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
16
|
+
entityType: z.ZodOptional<z.ZodString>;
|
|
17
|
+
entityId: z.ZodOptional<z.ZodString>;
|
|
18
|
+
}, "strict", z.ZodTypeAny, {
|
|
19
|
+
title: string;
|
|
20
|
+
body: string;
|
|
21
|
+
category: AdminNotificationCategory;
|
|
22
|
+
severity: "warning" | "error" | "info";
|
|
23
|
+
entityId?: string | undefined;
|
|
24
|
+
entityType?: string | undefined;
|
|
25
|
+
audienceUserIds?: string[] | undefined;
|
|
26
|
+
}, {
|
|
27
|
+
title: string;
|
|
28
|
+
body: string;
|
|
29
|
+
category: AdminNotificationCategory;
|
|
30
|
+
severity: "warning" | "error" | "info";
|
|
31
|
+
entityId?: string | undefined;
|
|
32
|
+
entityType?: string | undefined;
|
|
33
|
+
audienceUserIds?: string[] | undefined;
|
|
34
|
+
}>;
|
|
35
|
+
export type AdminNotificationCreateValues = z.infer<typeof adminNotificationCreateSchema>;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: `POST /api/admin/admin-notifications` spread the raw request body into
|
|
3
|
+
* Firestore with only `isRead: false` pinned. The last unvalidated write
|
|
4
|
+
* path in `src/app/api`.
|
|
5
|
+
*
|
|
6
|
+
* `category` and `severity` are both closed unions that the admin
|
|
7
|
+
* notification bell filters and colours by, and neither was checked — so
|
|
8
|
+
* a notification could be stored with a category no filter chip matches
|
|
9
|
+
* and a severity no badge renders, which is the shape that produces a row
|
|
10
|
+
* visible in the raw collection and invisible in the UI (Recurrent Root
|
|
11
|
+
* Cause #33).
|
|
12
|
+
*
|
|
13
|
+
* `audienceUserIds` decides WHO SEES IT. Unvalidated and unbounded, it
|
|
14
|
+
* could arrive as a string rather than an array — silently narrowing or
|
|
15
|
+
* widening the audience of an admin notification.
|
|
16
|
+
*
|
|
17
|
+
* WHAT: The create contract.
|
|
18
|
+
*
|
|
19
|
+
* ## `isRead` / `readAt` are absent on purpose
|
|
20
|
+
*
|
|
21
|
+
* Both are per-admin read state, owned by the read/dismiss path. A creator
|
|
22
|
+
* marking its own notification as already read would file it straight into the
|
|
23
|
+
* history nobody looks at.
|
|
24
|
+
*
|
|
25
|
+
* EXPORTS:
|
|
26
|
+
* adminNotificationCreateSchema, ADMIN_NOTIFICATION_CATEGORIES,
|
|
27
|
+
* ADMIN_NOTIFICATION_SEVERITIES, type AdminNotificationCreateValues
|
|
28
|
+
*
|
|
29
|
+
* @tag domain:store-extensions,admin-notifications
|
|
30
|
+
* @tag layer:schema
|
|
31
|
+
* @tag pattern:none
|
|
32
|
+
* @tag access:isomorphic
|
|
33
|
+
* @tag consumers:/api/admin/admin-notifications
|
|
34
|
+
* @tag sideEffects:none
|
|
35
|
+
*/
|
|
36
|
+
import { z } from "zod";
|
|
37
|
+
import { annotate } from "../../shell/field-ui-meta";
|
|
38
|
+
/*
|
|
39
|
+
* Keyed `Record<Union, true>` so BOTH mistakes are compile errors — a member
|
|
40
|
+
* added to the union and forgotten here, and a value here that is not real.
|
|
41
|
+
*/
|
|
42
|
+
const CATEGORY_MAP = {
|
|
43
|
+
system: true,
|
|
44
|
+
security: true,
|
|
45
|
+
moderation: true,
|
|
46
|
+
payouts: true,
|
|
47
|
+
fraud: true,
|
|
48
|
+
growth: true,
|
|
49
|
+
};
|
|
50
|
+
export const ADMIN_NOTIFICATION_CATEGORIES = Object.keys(CATEGORY_MAP);
|
|
51
|
+
const SEVERITY_MAP = {
|
|
52
|
+
info: true,
|
|
53
|
+
warning: true,
|
|
54
|
+
error: true,
|
|
55
|
+
};
|
|
56
|
+
export const ADMIN_NOTIFICATION_SEVERITIES = Object.keys(SEVERITY_MAP);
|
|
57
|
+
/**
|
|
58
|
+
* 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
|
|
59
|
+
* by schema instance and every zod wrapper returns a new one.
|
|
60
|
+
*/
|
|
61
|
+
export const adminNotificationCreateSchema = z
|
|
62
|
+
.object({
|
|
63
|
+
title: annotate(z.string().min(1, "Give the notification a title.").max(200), {
|
|
64
|
+
section: "message",
|
|
65
|
+
sectionLabel: "Message",
|
|
66
|
+
sectionRequired: true,
|
|
67
|
+
quick: true,
|
|
68
|
+
order: 1,
|
|
69
|
+
row: "full",
|
|
70
|
+
}),
|
|
71
|
+
body: annotate(z.string().min(1, "Say what happened.").max(2000), {
|
|
72
|
+
section: "message",
|
|
73
|
+
quick: true,
|
|
74
|
+
order: 2,
|
|
75
|
+
row: "full",
|
|
76
|
+
kind: "textarea",
|
|
77
|
+
}),
|
|
78
|
+
category: annotate(z.enum(ADMIN_NOTIFICATION_CATEGORIES), {
|
|
79
|
+
section: "routing",
|
|
80
|
+
sectionLabel: "Routing",
|
|
81
|
+
sectionRequired: true,
|
|
82
|
+
quick: true,
|
|
83
|
+
order: 1,
|
|
84
|
+
row: "pair",
|
|
85
|
+
}),
|
|
86
|
+
severity: annotate(z.enum(ADMIN_NOTIFICATION_SEVERITIES), {
|
|
87
|
+
section: "routing",
|
|
88
|
+
quick: true,
|
|
89
|
+
order: 2,
|
|
90
|
+
row: "pair",
|
|
91
|
+
}),
|
|
92
|
+
// Empty (or omitted) means every admin — that is the documented meaning of
|
|
93
|
+
// the field, so an empty array must stay expressible and is NOT `.min(1)`.
|
|
94
|
+
// Capped because this is the audience of a broadcast.
|
|
95
|
+
audienceUserIds: annotate(z.array(z.string().min(1)).max(200).optional(), {
|
|
96
|
+
section: "routing",
|
|
97
|
+
order: 3,
|
|
98
|
+
row: "full",
|
|
99
|
+
kind: "list",
|
|
100
|
+
help: "Leave empty to notify every admin.",
|
|
101
|
+
}),
|
|
102
|
+
entityType: annotate(z.string().max(80).optional(), {
|
|
103
|
+
section: "link",
|
|
104
|
+
sectionLabel: "Linked record",
|
|
105
|
+
order: 1,
|
|
106
|
+
row: "pair",
|
|
107
|
+
}),
|
|
108
|
+
entityId: annotate(z.string().max(200).optional(), {
|
|
109
|
+
section: "link",
|
|
110
|
+
order: 2,
|
|
111
|
+
row: "pair",
|
|
112
|
+
}),
|
|
113
|
+
})
|
|
114
|
+
.strict();
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { AlertOperator, AnalyticsCardType } from "./firestore";
|
|
3
|
+
export declare const ANALYTICS_ALERT_OPERATORS: [AlertOperator, ...AlertOperator[]];
|
|
4
|
+
export declare const ANALYTICS_CARD_TYPES: [AnalyticsCardType, ...AnalyticsCardType[]];
|
|
5
|
+
/**
|
|
6
|
+
* 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
|
|
7
|
+
* by schema instance and every zod wrapper returns a new one.
|
|
8
|
+
*/
|
|
9
|
+
export declare const analyticsAlertCreateSchema: z.ZodObject<{
|
|
10
|
+
label: z.ZodString;
|
|
11
|
+
metric: z.ZodString;
|
|
12
|
+
operator: z.ZodEnum<[AlertOperator, ...AlertOperator[]]>;
|
|
13
|
+
threshold: z.ZodNumber;
|
|
14
|
+
windowHours: z.ZodNumber;
|
|
15
|
+
isActive: z.ZodOptional<z.ZodBoolean>;
|
|
16
|
+
notifyChannels: z.ZodOptional<z.ZodArray<z.ZodEnum<["in-app", "email", "whatsapp"]>, "many">>;
|
|
17
|
+
}, "strict", z.ZodTypeAny, {
|
|
18
|
+
label: string;
|
|
19
|
+
operator: AlertOperator;
|
|
20
|
+
threshold: number;
|
|
21
|
+
metric: string;
|
|
22
|
+
windowHours: number;
|
|
23
|
+
isActive?: boolean | undefined;
|
|
24
|
+
notifyChannels?: ("email" | "whatsapp" | "in-app")[] | undefined;
|
|
25
|
+
}, {
|
|
26
|
+
label: string;
|
|
27
|
+
operator: AlertOperator;
|
|
28
|
+
threshold: number;
|
|
29
|
+
metric: string;
|
|
30
|
+
windowHours: number;
|
|
31
|
+
isActive?: boolean | undefined;
|
|
32
|
+
notifyChannels?: ("email" | "whatsapp" | "in-app")[] | undefined;
|
|
33
|
+
}>;
|
|
34
|
+
export type AnalyticsAlertCreateValues = z.infer<typeof analyticsAlertCreateSchema>;
|
|
35
|
+
export declare const analyticsCardCreateSchema: z.ZodObject<{
|
|
36
|
+
title: z.ZodString;
|
|
37
|
+
type: z.ZodEnum<[AnalyticsCardType, ...AnalyticsCardType[]]>;
|
|
38
|
+
metric: z.ZodString;
|
|
39
|
+
filters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<import("../../..").FirestoreValue, z.ZodTypeDef, import("../../..").FirestoreValue>>>;
|
|
40
|
+
position: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
isVisible: z.ZodOptional<z.ZodBoolean>;
|
|
42
|
+
}, "strict", z.ZodTypeAny, {
|
|
43
|
+
type: AnalyticsCardType;
|
|
44
|
+
title: string;
|
|
45
|
+
metric: string;
|
|
46
|
+
position?: number | undefined;
|
|
47
|
+
filters?: Record<string, import("../../..").FirestoreValue> | undefined;
|
|
48
|
+
isVisible?: boolean | undefined;
|
|
49
|
+
}, {
|
|
50
|
+
type: AnalyticsCardType;
|
|
51
|
+
title: string;
|
|
52
|
+
metric: string;
|
|
53
|
+
position?: number | undefined;
|
|
54
|
+
filters?: Record<string, import("../../..").FirestoreValue> | undefined;
|
|
55
|
+
isVisible?: boolean | undefined;
|
|
56
|
+
}>;
|
|
57
|
+
export type AnalyticsCardCreateValues = z.infer<typeof analyticsCardCreateSchema>;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: `POST /api/store/analytics/alerts` and `POST /api/store/analytics/cards`
|
|
3
|
+
* both spread the raw request body into Firestore.
|
|
4
|
+
*
|
|
5
|
+
* The alert one matters most. An analytics alert is a rule that FIRES —
|
|
6
|
+
* `metric`, `operator`, `threshold` and `windowHours` are what a scheduled
|
|
7
|
+
* job evaluates, and none of the four was checked. A `threshold` arriving
|
|
8
|
+
* as the string "50" from a number input was written as a string, so the
|
|
9
|
+
* comparison the alert exists to perform would run against a string; an
|
|
10
|
+
* `operator` outside the real six matched nothing; and `windowHours` was
|
|
11
|
+
* unbounded, so a rule could ask a job to look back a hundred years.
|
|
12
|
+
*
|
|
13
|
+
* Neither failure surfaces as an error. The alert is created, listed, and
|
|
14
|
+
* looks configured — it simply never fires, or fires wrongly.
|
|
15
|
+
*
|
|
16
|
+
* WHAT: The two create contracts.
|
|
17
|
+
*
|
|
18
|
+
* ## `scope` / `ownerId` / `isBuiltIn` are absent on purpose
|
|
19
|
+
*
|
|
20
|
+
* The routes pin all three. `scope` in particular decides whether a row is a
|
|
21
|
+
* seller's or the platform's, so accepting it from the body would let a seller
|
|
22
|
+
* file an admin-scoped card.
|
|
23
|
+
*
|
|
24
|
+
* EXPORTS:
|
|
25
|
+
* analyticsAlertCreateSchema, analyticsCardCreateSchema,
|
|
26
|
+
* ANALYTICS_ALERT_OPERATORS, ANALYTICS_CARD_TYPES,
|
|
27
|
+
* type AnalyticsAlertCreateValues, type AnalyticsCardCreateValues
|
|
28
|
+
*
|
|
29
|
+
* @tag domain:store-extensions,analytics
|
|
30
|
+
* @tag layer:schema
|
|
31
|
+
* @tag pattern:none
|
|
32
|
+
* @tag access:isomorphic
|
|
33
|
+
* @tag consumers:/api/store/analytics/alerts,/api/store/analytics/cards
|
|
34
|
+
* @tag sideEffects:none
|
|
35
|
+
*/
|
|
36
|
+
import { z } from "zod";
|
|
37
|
+
import { annotate } from "../../shell/field-ui-meta";
|
|
38
|
+
import { firestoreValueSchema } from "../../../schemas/firestore-value";
|
|
39
|
+
/*
|
|
40
|
+
* Keyed `Record<Union, true>` so BOTH mistakes are compile errors — a value
|
|
41
|
+
* added to the union and forgotten here, and a value here that is not real.
|
|
42
|
+
*/
|
|
43
|
+
const ALERT_OPERATOR_MAP = {
|
|
44
|
+
">": true,
|
|
45
|
+
"<": true,
|
|
46
|
+
">=": true,
|
|
47
|
+
"<=": true,
|
|
48
|
+
"==": true,
|
|
49
|
+
"!=": true,
|
|
50
|
+
};
|
|
51
|
+
export const ANALYTICS_ALERT_OPERATORS = Object.keys(ALERT_OPERATOR_MAP);
|
|
52
|
+
const CARD_TYPE_MAP = {
|
|
53
|
+
metric: true,
|
|
54
|
+
line: true,
|
|
55
|
+
bar: true,
|
|
56
|
+
pie: true,
|
|
57
|
+
table: true,
|
|
58
|
+
custom: true,
|
|
59
|
+
};
|
|
60
|
+
export const ANALYTICS_CARD_TYPES = Object.keys(CARD_TYPE_MAP);
|
|
61
|
+
/** One year. A window beyond this is a typo, not a reporting period. */
|
|
62
|
+
const MAX_WINDOW_HOURS = 24 * 365;
|
|
63
|
+
/**
|
|
64
|
+
* 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
|
|
65
|
+
* by schema instance and every zod wrapper returns a new one.
|
|
66
|
+
*/
|
|
67
|
+
export const analyticsAlertCreateSchema = z
|
|
68
|
+
.object({
|
|
69
|
+
label: annotate(z.string().min(1, "Give the alert a name.").max(120), {
|
|
70
|
+
section: "alert",
|
|
71
|
+
sectionLabel: "Alert",
|
|
72
|
+
sectionRequired: true,
|
|
73
|
+
quick: true,
|
|
74
|
+
order: 1,
|
|
75
|
+
row: "full",
|
|
76
|
+
}),
|
|
77
|
+
metric: annotate(z.string().min(1, "Choose a metric to watch."), {
|
|
78
|
+
section: "rule",
|
|
79
|
+
sectionLabel: "When to fire",
|
|
80
|
+
sectionRequired: true,
|
|
81
|
+
quick: true,
|
|
82
|
+
order: 1,
|
|
83
|
+
row: "pair",
|
|
84
|
+
}),
|
|
85
|
+
operator: annotate(z.enum(ANALYTICS_ALERT_OPERATORS), {
|
|
86
|
+
section: "rule",
|
|
87
|
+
quick: true,
|
|
88
|
+
order: 2,
|
|
89
|
+
row: "pair",
|
|
90
|
+
}),
|
|
91
|
+
// Coerced. A number input sends a string, and an uncoerced threshold is
|
|
92
|
+
// compared as a string by whatever evaluates the rule — "9" > "10".
|
|
93
|
+
threshold: annotate(z.coerce.number({ invalid_type_error: "Set a numeric threshold." }), {
|
|
94
|
+
section: "rule",
|
|
95
|
+
quick: true,
|
|
96
|
+
order: 3,
|
|
97
|
+
row: "pair",
|
|
98
|
+
kind: "number",
|
|
99
|
+
}),
|
|
100
|
+
windowHours: annotate(z.coerce
|
|
101
|
+
.number()
|
|
102
|
+
.int("The window must be a whole number of hours.")
|
|
103
|
+
.min(1, "The window must be at least an hour.")
|
|
104
|
+
.max(MAX_WINDOW_HOURS, "That window is longer than a year."), { section: "rule", quick: true, order: 4, row: "pair", kind: "number" }),
|
|
105
|
+
isActive: annotate(z.boolean().optional(), {
|
|
106
|
+
section: "delivery",
|
|
107
|
+
sectionLabel: "Delivery",
|
|
108
|
+
order: 1,
|
|
109
|
+
row: "quarter",
|
|
110
|
+
}),
|
|
111
|
+
notifyChannels: annotate(z.array(z.enum(["in-app", "email", "whatsapp"])).min(1).optional(), { section: "delivery", order: 2, row: "full", kind: "list" }),
|
|
112
|
+
})
|
|
113
|
+
.strict();
|
|
114
|
+
export const analyticsCardCreateSchema = z
|
|
115
|
+
.object({
|
|
116
|
+
title: annotate(z.string().min(1, "Give the card a title.").max(120), {
|
|
117
|
+
section: "card",
|
|
118
|
+
sectionLabel: "Card",
|
|
119
|
+
sectionRequired: true,
|
|
120
|
+
quick: true,
|
|
121
|
+
order: 1,
|
|
122
|
+
row: "full",
|
|
123
|
+
}),
|
|
124
|
+
type: annotate(z.enum(ANALYTICS_CARD_TYPES), {
|
|
125
|
+
section: "card",
|
|
126
|
+
quick: true,
|
|
127
|
+
order: 2,
|
|
128
|
+
row: "pair",
|
|
129
|
+
}),
|
|
130
|
+
metric: annotate(z.string().min(1, "Choose a metric."), {
|
|
131
|
+
section: "card",
|
|
132
|
+
quick: true,
|
|
133
|
+
order: 3,
|
|
134
|
+
row: "pair",
|
|
135
|
+
}),
|
|
136
|
+
// Deliberately open — filter shape varies by card type, and a closed schema
|
|
137
|
+
// would silently strip whatever the builder produced for a type it did not
|
|
138
|
+
// enumerate.
|
|
139
|
+
filters: annotate(z.record(firestoreValueSchema).optional(), {
|
|
140
|
+
section: "card",
|
|
141
|
+
order: 4,
|
|
142
|
+
row: "full",
|
|
143
|
+
kind: "list",
|
|
144
|
+
}),
|
|
145
|
+
position: annotate(z.coerce.number().int().min(0).optional(), {
|
|
146
|
+
section: "layout",
|
|
147
|
+
sectionLabel: "Layout",
|
|
148
|
+
order: 1,
|
|
149
|
+
row: "pair",
|
|
150
|
+
kind: "number",
|
|
151
|
+
}),
|
|
152
|
+
isVisible: annotate(z.boolean().optional(), {
|
|
153
|
+
section: "layout",
|
|
154
|
+
order: 2,
|
|
155
|
+
row: "quarter",
|
|
156
|
+
}),
|
|
157
|
+
})
|
|
158
|
+
.strict();
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Whether a string is a permission this system can actually grant. */
|
|
3
|
+
export declare function isKnownPermission(value: string): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
|
|
6
|
+
* by schema instance and every zod wrapper returns a new one.
|
|
7
|
+
*/
|
|
8
|
+
export declare const customRoleFormSchema: z.ZodObject<{
|
|
9
|
+
name: z.ZodString;
|
|
10
|
+
slug: z.ZodString;
|
|
11
|
+
description: z.ZodOptional<z.ZodString>;
|
|
12
|
+
scope: z.ZodEnum<["global", "store"]>;
|
|
13
|
+
permissions: z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">;
|
|
14
|
+
inheritsFrom: z.ZodOptional<z.ZodString>;
|
|
15
|
+
isActive: z.ZodBoolean;
|
|
16
|
+
}, "strip", z.ZodTypeAny, {
|
|
17
|
+
scope: "store" | "global";
|
|
18
|
+
name: string;
|
|
19
|
+
slug: string;
|
|
20
|
+
isActive: boolean;
|
|
21
|
+
permissions: string[];
|
|
22
|
+
description?: string | undefined;
|
|
23
|
+
inheritsFrom?: string | undefined;
|
|
24
|
+
}, {
|
|
25
|
+
scope: "store" | "global";
|
|
26
|
+
name: string;
|
|
27
|
+
slug: string;
|
|
28
|
+
isActive: boolean;
|
|
29
|
+
permissions: string[];
|
|
30
|
+
description?: string | undefined;
|
|
31
|
+
inheritsFrom?: string | undefined;
|
|
32
|
+
}>;
|
|
33
|
+
export type CustomRoleFormValues = z.infer<typeof customRoleFormSchema>;
|
|
34
|
+
/** Create contract. `createdBy` is absent — the route sets it from the session. */
|
|
35
|
+
export declare const customRoleCreateSchema: z.ZodObject<{
|
|
36
|
+
name: z.ZodString;
|
|
37
|
+
slug: z.ZodString;
|
|
38
|
+
description: z.ZodOptional<z.ZodString>;
|
|
39
|
+
scope: z.ZodEnum<["global", "store"]>;
|
|
40
|
+
permissions: z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">;
|
|
41
|
+
inheritsFrom: z.ZodOptional<z.ZodString>;
|
|
42
|
+
isActive: z.ZodBoolean;
|
|
43
|
+
}, "strip", z.ZodTypeAny, {
|
|
44
|
+
scope: "store" | "global";
|
|
45
|
+
name: string;
|
|
46
|
+
slug: string;
|
|
47
|
+
isActive: boolean;
|
|
48
|
+
permissions: string[];
|
|
49
|
+
description?: string | undefined;
|
|
50
|
+
inheritsFrom?: string | undefined;
|
|
51
|
+
}, {
|
|
52
|
+
scope: "store" | "global";
|
|
53
|
+
name: string;
|
|
54
|
+
slug: string;
|
|
55
|
+
isActive: boolean;
|
|
56
|
+
permissions: string[];
|
|
57
|
+
description?: string | undefined;
|
|
58
|
+
inheritsFrom?: string | undefined;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Update contract — partial and `.strict()`, so an unknown key is a 400 rather
|
|
62
|
+
* than a silent write into a document the permission system reads.
|
|
63
|
+
*/
|
|
64
|
+
export declare const customRoleUpdateSchema: z.ZodObject<{
|
|
65
|
+
name: z.ZodOptional<z.ZodString>;
|
|
66
|
+
description: z.ZodOptional<z.ZodString>;
|
|
67
|
+
scope: z.ZodOptional<z.ZodEnum<["global", "store"]>>;
|
|
68
|
+
permissions: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
|
|
69
|
+
inheritsFrom: z.ZodOptional<z.ZodString>;
|
|
70
|
+
isActive: z.ZodOptional<z.ZodBoolean>;
|
|
71
|
+
}, "strict", z.ZodTypeAny, {
|
|
72
|
+
scope?: "store" | "global" | undefined;
|
|
73
|
+
name?: string | undefined;
|
|
74
|
+
description?: string | undefined;
|
|
75
|
+
isActive?: boolean | undefined;
|
|
76
|
+
permissions?: string[] | undefined;
|
|
77
|
+
inheritsFrom?: string | undefined;
|
|
78
|
+
}, {
|
|
79
|
+
scope?: "store" | "global" | undefined;
|
|
80
|
+
name?: string | undefined;
|
|
81
|
+
description?: string | undefined;
|
|
82
|
+
isActive?: boolean | undefined;
|
|
83
|
+
permissions?: string[] | undefined;
|
|
84
|
+
inheritsFrom?: string | undefined;
|
|
85
|
+
}>;
|