@mohasinac/appkit 4.21.0 → 4.23.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/checkout/locked-lines.js +12 -2
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +5 -0
- package/dist/_internal/server/jobs/core/auctionSettlement.js +10 -4
- package/dist/_internal/server/jobs/core/offerExpiry.js +10 -2
- package/dist/_internal/server/jobs/core/payoutBatch.js +15 -3
- package/dist/_internal/shared/features/products/schema.d.ts +8 -8
- package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
- package/dist/_internal/shared/history/index.d.ts +1 -1
- package/dist/_internal/shared/history/types.d.ts +16 -0
- package/dist/client.d.ts +7 -0
- package/dist/client.js +4 -0
- package/dist/constants/field-names.d.ts +12 -0
- package/dist/constants/field-names.js +12 -0
- package/dist/features/account/components/AddressForm.js +45 -29
- package/dist/features/admin/actions/admin-actions.js +13 -3
- package/dist/features/admin/components/AdminBidsView.js +7 -1
- package/dist/features/admin/components/AdminBundleEditorView.js +3 -0
- package/dist/features/admin/components/AdminScammerEditorView.d.ts +5 -1
- package/dist/features/admin/components/AdminScammerEditorView.js +3 -2
- package/dist/features/admin/components/AdminSupportTicketDetailView.d.ts +5 -1
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +3 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +3 -1
- package/dist/features/auctions/repository/bid.repository.d.ts +21 -8
- package/dist/features/auctions/repository/bid.repository.js +57 -33
- package/dist/features/auctions/schemas/firestore.d.ts +27 -0
- package/dist/features/auctions/schemas/firestore.js +21 -0
- package/dist/features/auth/schemas/index.d.ts +2 -2
- package/dist/features/catalogue/actions/approve-catalogue-listing.js +7 -6
- package/dist/features/catalogue/actions/list-from-catalogue.js +2 -2
- package/dist/features/catalogue/actions/submit-for-approval.js +1 -4
- package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +5 -1
- package/dist/features/catalogue/components/CatalogueItemEditorView.js +3 -1
- package/dist/features/catalogue/repository/catalogue.repository.d.ts +23 -0
- package/dist/features/catalogue/repository/catalogue.repository.js +28 -1
- package/dist/features/catalogue/schemas/firestore.d.ts +19 -0
- package/dist/features/catalogue/schemas/firestore.js +12 -0
- package/dist/features/categories/schemas/index.d.ts +2 -2
- package/dist/features/layout/BottomActions.js +18 -1
- package/dist/features/layout/BottomActionsContext.d.ts +19 -0
- package/dist/features/layout/BottomActionsContext.js +37 -0
- package/dist/features/layout/hooks/useBottomActions.d.ts +10 -0
- package/dist/features/layout/hooks/useBottomActions.js +48 -11
- package/dist/features/layout/hooks/useFormBottomActions.d.ts +57 -0
- package/dist/features/layout/hooks/useFormBottomActions.js +89 -0
- package/dist/features/layout/index.d.ts +2 -0
- package/dist/features/layout/index.js +1 -0
- package/dist/features/messages/schemas/index.d.ts +6 -6
- package/dist/features/orders/schemas/index.d.ts +12 -12
- package/dist/features/payments/repository/payout.repository.d.ts +18 -5
- package/dist/features/payments/repository/payout.repository.js +45 -19
- package/dist/features/payments/schemas/firestore.d.ts +33 -1
- package/dist/features/payments/schemas/firestore.js +39 -0
- package/dist/features/payments/schemas/index.d.ts +12 -12
- package/dist/features/products/components/MakeOfferButton.js +5 -3
- package/dist/features/products/schemas/index.d.ts +2 -2
- package/dist/features/promotions/schemas/index.d.ts +12 -12
- package/dist/features/scams/repository/scammer.repository.d.ts +18 -1
- package/dist/features/scams/repository/scammer.repository.js +24 -2
- package/dist/features/scams/schemas/firestore.d.ts +24 -0
- package/dist/features/scams/schemas/firestore.js +30 -0
- package/dist/features/shell/QuickFormDrawer.js +15 -1
- package/dist/features/shell/SectionForm.d.ts +16 -2
- package/dist/features/shell/SectionForm.js +28 -2
- package/dist/features/shipments/components/AdminShipmentEditorView.js +5 -2
- package/dist/features/shipments/repository/shipments.repository.d.ts +18 -0
- package/dist/features/shipments/repository/shipments.repository.js +23 -1
- package/dist/features/shipments/schemas/firestore.d.ts +14 -0
- package/dist/features/shipments/schemas/firestore.js +19 -0
- package/dist/features/status-history/components/RecordStatusTimeline.d.ts +12 -0
- package/dist/features/status-history/components/RecordStatusTimeline.js +90 -0
- package/dist/features/status-history/index.d.ts +2 -0
- package/dist/features/status-history/index.js +1 -0
- package/dist/features/stores/repository/store.repository.d.ts +30 -2
- package/dist/features/stores/repository/store.repository.js +43 -4
- package/dist/features/stores/schemas/firestore.d.ts +29 -0
- package/dist/features/stores/schemas/firestore.js +31 -0
- package/dist/features/support/repository/support.repository.d.ts +47 -3
- package/dist/features/support/repository/support.repository.js +72 -12
- package/dist/features/support/schemas/firestore.d.ts +38 -1
- package/dist/features/support/schemas/firestore.js +34 -0
- package/dist/features/support/schemas/index.d.ts +2 -2
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +90 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +16 -1
- package/dist/schemas/registry.d.ts +32 -32
- package/dist/seed/bids-seed-data.js +8 -0
- package/dist/ui/components/Form.d.ts +22 -1
- package/dist/ui/components/Form.js +30 -2
- package/dist/ui/components/Modal.js +4 -3
- package/dist/ui/components/SideDrawer.js +5 -4
- package/dist/ui/components/overlay-context.d.ts +21 -0
- package/dist/ui/components/overlay-context.js +25 -0
- package/dist/ui/forms/FormErrorList.d.ts +29 -0
- package/dist/ui/forms/FormErrorList.js +36 -0
- package/dist/ui/forms/FormErrorSummary.d.ts +8 -0
- package/dist/ui/forms/FormErrorSummary.js +15 -8
- package/dist/ui/forms/FormShell.d.ts +25 -1
- package/dist/ui/forms/FormShell.js +39 -4
- package/dist/ui/forms/index.d.ts +2 -0
- package/dist/ui/forms/index.js +1 -0
- package/package.json +1 -1
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* Handles seller payout requests and admin payout processing.
|
|
6
6
|
*/
|
|
7
7
|
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
8
|
-
import { PAYOUT_COLLECTION, PAYOUT_FIELDS, createPayoutId } from "../schemas";
|
|
8
|
+
import { PAYOUT_COLLECTION, PAYOUT_FIELDS, PAYOUT_TRACKED_FIELDS, createPayoutId } from "../schemas";
|
|
9
|
+
import { withHistory } from "../../../_internal/shared/history/index";
|
|
9
10
|
import { encryptPiiFields, decryptPiiFields, addPiiIndices, PAYOUT_PII_FIELDS, PAYOUT_PII_INDEX_MAP, encryptPayoutBankAccount, decryptPayoutBankAccount, } from "../../../security";
|
|
10
11
|
export class PayoutRepository extends BaseRepository {
|
|
11
12
|
constructor() {
|
|
@@ -120,10 +121,14 @@ export class PayoutRepository extends BaseRepository {
|
|
|
120
121
|
}));
|
|
121
122
|
}
|
|
122
123
|
/**
|
|
123
|
-
* Update payout status (admin action)
|
|
124
|
+
* Update payout status (admin action).
|
|
125
|
+
*
|
|
126
|
+
* `prior` keeps the timeline entry read-free — the admin route already
|
|
127
|
+
* fetches the payout to authorise the change (Rule #6).
|
|
124
128
|
*/
|
|
125
|
-
async updateStatus(payoutId, status, extra) {
|
|
126
|
-
|
|
129
|
+
async updateStatus(payoutId, status, extra, ctx, prior) {
|
|
130
|
+
const current = prior !== undefined ? prior : await this.findById(payoutId);
|
|
131
|
+
return this.update(payoutId, this.withPayoutHistory(current, {
|
|
127
132
|
status,
|
|
128
133
|
...extra,
|
|
129
134
|
...(status === PAYOUT_FIELDS.STATUS_VALUES.PAID ||
|
|
@@ -131,29 +136,50 @@ export class PayoutRepository extends BaseRepository {
|
|
|
131
136
|
? { processedAt: new Date() }
|
|
132
137
|
: {}),
|
|
133
138
|
updatedAt: new Date(),
|
|
134
|
-
});
|
|
139
|
+
}, ctx, "adminUpdatePayout"));
|
|
135
140
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
/*
|
|
142
|
+
* ── Batch dispatch primitives ──────────────────────────────────────────
|
|
143
|
+
*
|
|
144
|
+
* These take a `DocumentReference` rather than an id because the payouts
|
|
145
|
+
* batch already holds one from its query snapshot. `prior` comes from the
|
|
146
|
+
* SAME snapshot, so history costs no read here either — but it must be the
|
|
147
|
+
* caller's running copy, not the original: the sequence is
|
|
148
|
+
* pending → processing → failed, and diffing the final write against the
|
|
149
|
+
* ORIGINAL snapshot would record "pending → failed" and lose the dispatch
|
|
150
|
+
* attempt entirely.
|
|
151
|
+
*/
|
|
152
|
+
markProcessing(ref, ctx, prior) {
|
|
153
|
+
return ref.update(this.withPayoutHistory(prior, { status: "processing", processedAt: new Date(), updatedAt: new Date() }, ctx, "payoutBatch:dispatch"));
|
|
142
154
|
}
|
|
143
|
-
recordSuccess(ref, razorpayPayoutId, razorpayStatus) {
|
|
144
|
-
return ref.update({
|
|
145
|
-
razorpayPayoutId,
|
|
146
|
-
razorpayStatus,
|
|
147
|
-
updatedAt: new Date(),
|
|
148
|
-
});
|
|
155
|
+
recordSuccess(ref, razorpayPayoutId, razorpayStatus, ctx, prior) {
|
|
156
|
+
return ref.update(this.withPayoutHistory(prior, { razorpayPayoutId, razorpayStatus, updatedAt: new Date() }, ctx, "payoutBatch:accepted"));
|
|
149
157
|
}
|
|
150
|
-
recordFailure(ref, failureCount, reason, isFinal) {
|
|
151
|
-
return ref.update({
|
|
158
|
+
recordFailure(ref, failureCount, reason, isFinal, ctx, prior) {
|
|
159
|
+
return ref.update(this.withPayoutHistory(prior, {
|
|
152
160
|
status: isFinal ? "failed" : "pending",
|
|
153
161
|
failureCount,
|
|
154
162
|
lastFailureReason: reason,
|
|
155
163
|
updatedAt: new Date(),
|
|
164
|
+
},
|
|
165
|
+
// The reason is the point of the entry — `lastFailureReason` is
|
|
166
|
+
// overwritten by the next retry, so history is where the earlier
|
|
167
|
+
// attempts survive.
|
|
168
|
+
{ ...ctx, reason }, isFinal ? "payoutBatch:failedFinal" : "payoutBatch:failedRetrying"));
|
|
169
|
+
}
|
|
170
|
+
/** Append a timeline entry when the patch touches a tracked field. */
|
|
171
|
+
withPayoutHistory(current, patch, ctx, defaultTrigger) {
|
|
172
|
+
const withEntry = withHistory(current, patch, {
|
|
173
|
+
tracked: PAYOUT_TRACKED_FIELDS,
|
|
174
|
+
actor: ctx?.actor ?? { role: "system" },
|
|
175
|
+
trigger: ctx?.trigger ?? defaultTrigger,
|
|
176
|
+
reason: ctx?.reason,
|
|
177
|
+
note: ctx?.note,
|
|
178
|
+
// `sellerEmail` / `upiId` — `encryptPiiFields` never descends into
|
|
179
|
+
// arrays, so either reaching `changes` would persist in PLAINTEXT.
|
|
180
|
+
piiFields: PAYOUT_PII_FIELDS,
|
|
156
181
|
});
|
|
182
|
+
return withEntry ?? patch;
|
|
157
183
|
}
|
|
158
184
|
/**
|
|
159
185
|
* Get all order IDs that have already been paid out for a seller.
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { type GeneratePayoutIdInput } from "../../../utils/id-generators";
|
|
5
5
|
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
6
|
+
import type { StatusChangeEntry } from "../../../_internal/shared/history/types";
|
|
6
7
|
export interface PayoutBankAccount {
|
|
7
8
|
accountHolderName: string;
|
|
8
9
|
accountNumberMasked: string;
|
|
@@ -70,7 +71,38 @@ export interface PayoutDocument extends BaseDocument {
|
|
|
70
71
|
transactionId?: string;
|
|
71
72
|
/** Seller-set personal follow-up flag — surfaced only to the owning seller, never mutated by admin. */
|
|
72
73
|
sellerReminderFlag?: boolean;
|
|
74
|
+
/** Razorpay's payout id, once the API accepted the dispatch. */
|
|
75
|
+
razorpayPayoutId?: string;
|
|
76
|
+
/** Razorpay's own status string — NOT this document's `status`. */
|
|
77
|
+
razorpayStatus?: string;
|
|
78
|
+
/** Consecutive dispatch failures. At MAX_FAILURES the payout goes `failed`. */
|
|
79
|
+
failureCount?: number;
|
|
80
|
+
/**
|
|
81
|
+
* Why the LAST attempt failed. Overwritten by every retry, which is why the
|
|
82
|
+
* timeline tracks it: `statusHistory` is the only place the earlier reasons
|
|
83
|
+
* survive.
|
|
84
|
+
*/
|
|
85
|
+
lastFailureReason?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Who changed what, when, and why. See § "Status History" in CLAUDE.md.
|
|
88
|
+
*
|
|
89
|
+
* Payouts are the entity where this matters most to a seller: "it says
|
|
90
|
+
* failed, what happened" was previously answerable only from
|
|
91
|
+
* `lastFailureReason`, which the NEXT retry overwrites.
|
|
92
|
+
*/
|
|
93
|
+
statusHistory?: StatusChangeEntry[];
|
|
94
|
+
statusHistoryTruncated?: number;
|
|
73
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* The fields whose changes earn a timeline entry.
|
|
98
|
+
*
|
|
99
|
+
* `netAmount`/`refundDeductions` are excluded — a refund deduction is money
|
|
100
|
+
* churn before dispatch, and § "Status History" keeps money off the timeline;
|
|
101
|
+
* `refundDeductions[]` on the document is already its own record.
|
|
102
|
+
* `failureCount` is excluded because `lastFailureReason` changing is the
|
|
103
|
+
* event, and tracking both would double every retry entry.
|
|
104
|
+
*/
|
|
105
|
+
export declare const PAYOUT_TRACKED_FIELDS: readonly ["status", "processedAt", "transactionId", "razorpayPayoutId", "razorpayStatus", "lastFailureReason"];
|
|
74
106
|
export declare const PAYOUT_COLLECTION: "payouts";
|
|
75
107
|
export declare const PAYOUT_INDEXED_FIELDS: readonly ["storeId", "status", "requestedAt", "createdAt"];
|
|
76
108
|
export declare const DEFAULT_PLATFORM_FEE_RATE: 0.05;
|
|
@@ -91,7 +123,7 @@ export declare const PAYOUT_FIELDS: {
|
|
|
91
123
|
};
|
|
92
124
|
};
|
|
93
125
|
export declare const PAYOUT_PUBLIC_FIELDS: readonly ["id", "storeId", "amount", "currency", "status", "paymentMethod", "requestedAt", "processedAt", "createdAt"];
|
|
94
|
-
export declare const PAYOUT_ADMIN_UPDATEABLE_FIELDS: readonly ["status", "adminNote", "processedAt"];
|
|
126
|
+
export declare const PAYOUT_ADMIN_UPDATEABLE_FIELDS: readonly ["status", "adminNote", "processedAt", "transactionId"];
|
|
95
127
|
/** The only field a seller may write on their own payout doc. */
|
|
96
128
|
export declare const PAYOUT_SELLER_UPDATEABLE_FIELDS: readonly ["sellerReminderFlag"];
|
|
97
129
|
export type PayoutCreateInput = Omit<PayoutDocument, "id" | "createdAt" | "updatedAt" | "status" | "requestedAt">;
|
|
@@ -8,6 +8,23 @@ export const PayoutStatusValues = {
|
|
|
8
8
|
PAID: "paid",
|
|
9
9
|
FAILED: "failed",
|
|
10
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* The fields whose changes earn a timeline entry.
|
|
13
|
+
*
|
|
14
|
+
* `netAmount`/`refundDeductions` are excluded — a refund deduction is money
|
|
15
|
+
* churn before dispatch, and § "Status History" keeps money off the timeline;
|
|
16
|
+
* `refundDeductions[]` on the document is already its own record.
|
|
17
|
+
* `failureCount` is excluded because `lastFailureReason` changing is the
|
|
18
|
+
* event, and tracking both would double every retry entry.
|
|
19
|
+
*/
|
|
20
|
+
export const PAYOUT_TRACKED_FIELDS = [
|
|
21
|
+
"status",
|
|
22
|
+
"processedAt",
|
|
23
|
+
"transactionId",
|
|
24
|
+
"razorpayPayoutId",
|
|
25
|
+
"razorpayStatus",
|
|
26
|
+
"lastFailureReason",
|
|
27
|
+
];
|
|
11
28
|
export const PAYOUT_COLLECTION = "payouts";
|
|
12
29
|
export const PAYOUT_INDEXED_FIELDS = [
|
|
13
30
|
"storeId",
|
|
@@ -47,10 +64,32 @@ export const PAYOUT_PUBLIC_FIELDS = [
|
|
|
47
64
|
"processedAt",
|
|
48
65
|
"createdAt",
|
|
49
66
|
];
|
|
67
|
+
/*
|
|
68
|
+
* 🛑 `transactionId` was MISSING here until 2026-08-26, and the whole
|
|
69
|
+
* mark-paid flow was built on top of the gap:
|
|
70
|
+
*
|
|
71
|
+
* · `AdminPayoutMarkPaidModal` collects a UTR and makes it REQUIRED
|
|
72
|
+
* · `PATCH /api/admin/payouts/[id]` declares it in its Zod schema and its
|
|
73
|
+
* own docstring advertises "+ transaction reference"
|
|
74
|
+
* · that route's test asserts it reaches `adminUpdatePayout`
|
|
75
|
+
* · `adminUpdatePayout` picks only the fields in THIS list, so it was
|
|
76
|
+
* dropped — and the route replies `{ id, ...body }`, echoing the
|
|
77
|
+
* discarded UTR back as though it had been stored
|
|
78
|
+
*
|
|
79
|
+
* Every payout marked paid before this therefore has no payment reference,
|
|
80
|
+
* which is the one field a seller disputing "you say you paid me" needs.
|
|
81
|
+
* Root Cause #40's shape: a 200 that confirms a write which never happened.
|
|
82
|
+
*
|
|
83
|
+
* `processedAt` stays accepted for back-compat but is server-stamped by
|
|
84
|
+
* `updateStatus` on the transition into paid/failed — a client-sent
|
|
85
|
+
* timestamp is how the reports collection ended up with two shapes in one
|
|
86
|
+
* field.
|
|
87
|
+
*/
|
|
50
88
|
export const PAYOUT_ADMIN_UPDATEABLE_FIELDS = [
|
|
51
89
|
"status",
|
|
52
90
|
"adminNote",
|
|
53
91
|
"processedAt",
|
|
92
|
+
"transactionId",
|
|
54
93
|
];
|
|
55
94
|
/** The only field a seller may write on their own payout doc. */
|
|
56
95
|
export const PAYOUT_SELLER_UPDATEABLE_FIELDS = ["sellerReminderFlag"];
|
|
@@ -33,20 +33,20 @@ export declare const payoutRefundDeductionSchema: z.ZodObject<{
|
|
|
33
33
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
34
34
|
}, z.ZodTypeAny, "passthrough">>]>;
|
|
35
35
|
}, "strip", z.ZodTypeAny, {
|
|
36
|
+
reason: string;
|
|
36
37
|
orderId: string;
|
|
37
38
|
refundId: string;
|
|
38
39
|
refundedAmount: number;
|
|
39
40
|
deductedAmount: number;
|
|
40
|
-
reason: string;
|
|
41
41
|
appliedAt: string | Date | z.objectOutputType<{
|
|
42
42
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
43
43
|
}, z.ZodTypeAny, "passthrough">;
|
|
44
44
|
}, {
|
|
45
|
+
reason: string;
|
|
45
46
|
orderId: string;
|
|
46
47
|
refundId: string;
|
|
47
48
|
refundedAmount: number;
|
|
48
49
|
deductedAmount: number;
|
|
49
|
-
reason: string;
|
|
50
50
|
appliedAt: string | Date | z.objectInputType<{
|
|
51
51
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
52
52
|
}, z.ZodTypeAny, "passthrough">;
|
|
@@ -116,20 +116,20 @@ export declare const payoutFirestoreSchema: z.ZodObject<{
|
|
|
116
116
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
117
117
|
}, z.ZodTypeAny, "passthrough">>]>;
|
|
118
118
|
}, "strip", z.ZodTypeAny, {
|
|
119
|
+
reason: string;
|
|
119
120
|
orderId: string;
|
|
120
121
|
refundId: string;
|
|
121
122
|
refundedAmount: number;
|
|
122
123
|
deductedAmount: number;
|
|
123
|
-
reason: string;
|
|
124
124
|
appliedAt: string | Date | z.objectOutputType<{
|
|
125
125
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
126
126
|
}, z.ZodTypeAny, "passthrough">;
|
|
127
127
|
}, {
|
|
128
|
+
reason: string;
|
|
128
129
|
orderId: string;
|
|
129
130
|
refundId: string;
|
|
130
131
|
refundedAmount: number;
|
|
131
132
|
deductedAmount: number;
|
|
132
|
-
reason: string;
|
|
133
133
|
appliedAt: string | Date | z.objectInputType<{
|
|
134
134
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
135
135
|
}, z.ZodTypeAny, "passthrough">;
|
|
@@ -171,6 +171,9 @@ export declare const payoutFirestoreSchema: z.ZodObject<{
|
|
|
171
171
|
platformFeeRate: number;
|
|
172
172
|
paymentMethod: "bank_transfer" | "upi";
|
|
173
173
|
orderIds: string[];
|
|
174
|
+
processedAt?: string | Date | z.objectOutputType<{
|
|
175
|
+
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
176
|
+
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
174
177
|
bankAccount?: {
|
|
175
178
|
accountHolderName: string;
|
|
176
179
|
accountNumberMasked: string;
|
|
@@ -186,19 +189,16 @@ export declare const payoutFirestoreSchema: z.ZodObject<{
|
|
|
186
189
|
gstRate?: number | undefined;
|
|
187
190
|
isAutomatic?: boolean | undefined;
|
|
188
191
|
refundDeductions?: {
|
|
192
|
+
reason: string;
|
|
189
193
|
orderId: string;
|
|
190
194
|
refundId: string;
|
|
191
195
|
refundedAmount: number;
|
|
192
196
|
deductedAmount: number;
|
|
193
|
-
reason: string;
|
|
194
197
|
appliedAt: string | Date | z.objectOutputType<{
|
|
195
198
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
196
199
|
}, z.ZodTypeAny, "passthrough">;
|
|
197
200
|
}[] | undefined;
|
|
198
201
|
netAmount?: number | undefined;
|
|
199
|
-
processedAt?: string | Date | z.objectOutputType<{
|
|
200
|
-
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
201
|
-
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
202
202
|
}, {
|
|
203
203
|
currency: string;
|
|
204
204
|
status: "paid" | "failed" | "pending" | "processing";
|
|
@@ -221,6 +221,9 @@ export declare const payoutFirestoreSchema: z.ZodObject<{
|
|
|
221
221
|
platformFeeRate: number;
|
|
222
222
|
paymentMethod: "bank_transfer" | "upi";
|
|
223
223
|
orderIds: string[];
|
|
224
|
+
processedAt?: string | Date | z.objectInputType<{
|
|
225
|
+
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
226
|
+
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
224
227
|
bankAccount?: {
|
|
225
228
|
accountHolderName: string;
|
|
226
229
|
accountNumberMasked: string;
|
|
@@ -236,19 +239,16 @@ export declare const payoutFirestoreSchema: z.ZodObject<{
|
|
|
236
239
|
gstRate?: number | undefined;
|
|
237
240
|
isAutomatic?: boolean | undefined;
|
|
238
241
|
refundDeductions?: {
|
|
242
|
+
reason: string;
|
|
239
243
|
orderId: string;
|
|
240
244
|
refundId: string;
|
|
241
245
|
refundedAmount: number;
|
|
242
246
|
deductedAmount: number;
|
|
243
|
-
reason: string;
|
|
244
247
|
appliedAt: string | Date | z.objectInputType<{
|
|
245
248
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
246
249
|
}, z.ZodTypeAny, "passthrough">;
|
|
247
250
|
}[] | undefined;
|
|
248
251
|
netAmount?: number | undefined;
|
|
249
|
-
processedAt?: string | Date | z.objectInputType<{
|
|
250
|
-
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
251
|
-
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
252
252
|
}>;
|
|
253
253
|
export type PayoutFromSchema = z.infer<typeof payoutFirestoreSchema>;
|
|
254
254
|
/** Zod schema for payout status — use instead of inline `z.enum(["pending",...])`. */
|
|
@@ -38,7 +38,9 @@ export function MakeOfferButton({ productId, listedPrice, currency, minOfferPerc
|
|
|
38
38
|
const schema = makeOfferFormSchema({ listedPrice, minOffer, formatAmount: fmt });
|
|
39
39
|
const [offerAmount, setOfferAmount] = useState(String(Math.max(minOfferAmount(listedPrice, 90), minOffer)));
|
|
40
40
|
const [buyerNote, setBuyerNote] = useState("");
|
|
41
|
-
function submit(setFieldError, clearErrors) {
|
|
41
|
+
function submit(setFieldError, clearErrors, markSubmitAttempted) {
|
|
42
|
+
// type="button" — no native submit, so unhide the summary here.
|
|
43
|
+
markSubmitAttempted();
|
|
42
44
|
clearErrors();
|
|
43
45
|
const parsed = schema.safeParse({ offerAmount, buyerNote: buyerNote || undefined });
|
|
44
46
|
if (!parsed.success) {
|
|
@@ -75,8 +77,8 @@ export function MakeOfferButton({ productId, listedPrice, currency, minOfferPerc
|
|
|
75
77
|
if (state === "pending") {
|
|
76
78
|
return (_jsxs(Div, { className: `${CLS_PENDING_BOX} ${className}`, children: [_jsx(Span, { size: "lg", children: "\u23F3" }), _jsx(Text, { className: CLS_PENDING_TITLE, children: "Offer Pending" }), _jsx(Text, { className: CLS_PENDING_BODY, children: "You already have an offer on this item. Check My Offers for updates." })] }));
|
|
77
79
|
}
|
|
78
|
-
return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to make an offer. Please log in or create an account to continue." }), _jsx(Button, { variant: "ghost", size: "md", border: "strong", className: `w-full ${className}`, onClick: () => setState("open"), children: "Make Offer" }), _jsx(Modal, { isOpen: state === "open", onClose: () => setState("idle"), size: "md", title: "Make an offer", children: _jsx(Form, { schema: schema, onSubmit: (e) => e.preventDefault(), children: ({ setFieldError, clearErrors }) => (_jsxs(Stack, { gap: "md", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Listed at ", fmt(listedPrice), " \u00B7 Minimum offer: ", fmt(minOffer)] }), _jsx(FieldInput, { name: "offerAmount", type: "number", label: "Your offer amount", required: true, value: offerAmount, onChange: (value) => {
|
|
80
|
+
return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to make an offer. Please log in or create an account to continue." }), _jsx(Button, { variant: "ghost", size: "md", border: "strong", className: `w-full ${className}`, onClick: () => setState("open"), children: "Make Offer" }), _jsx(Modal, { isOpen: state === "open", onClose: () => setState("idle"), size: "md", title: "Make an offer", children: _jsx(Form, { schema: schema, onSubmit: (e) => e.preventDefault(), children: ({ setFieldError, clearErrors, markSubmitAttempted }) => (_jsxs(Stack, { gap: "md", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Listed at ", fmt(listedPrice), " \u00B7 Minimum offer: ", fmt(minOffer)] }), _jsx(FieldInput, { name: "offerAmount", type: "number", label: "Your offer amount", required: true, value: offerAmount, onChange: (value) => {
|
|
79
81
|
setOfferAmount(value);
|
|
80
82
|
clearErrors();
|
|
81
|
-
}, min: minOffer, max: listedPrice - 1, step: 1, "aria-label": "Offer amount" }), _jsxs(Text, { size: "xs", color: "faint", children: ["Must be between ", fmt(minOffer), " and ", fmt(listedPrice - 1)] }), _jsx(FieldInput, { name: "buyerNote", type: "text", label: "Note to seller (optional)", value: buyerNote, onChange: setBuyerNote, placeholder: "E.g. Bundle deal, long-time fan\u2026", maxLength: 300, "aria-label": "Note to seller" }), _jsx(Text, { size: "xs", color: "muted", children: "The seller will accept, decline, or suggest a counter price." }), _jsx(FormErrorSummary, {}), _jsxs(Stack, { gap: "xs", children: [_jsx(Button, { action: ACTIONS.PRODUCT["make-offer"], variant: "primary", size: "sm", type: "button", isLoading: isPending, disabled: isPending, onClick: () => submit(setFieldError, clearErrors), children: isPending ? "Sending…" : `Send offer of ${fmt(Number(offerAmount) || 0)}` }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => setState("idle"), disabled: isPending, children: "Cancel" })] })] })) }) })] }));
|
|
83
|
+
}, min: minOffer, max: listedPrice - 1, step: 1, "aria-label": "Offer amount" }), _jsxs(Text, { size: "xs", color: "faint", children: ["Must be between ", fmt(minOffer), " and ", fmt(listedPrice - 1)] }), _jsx(FieldInput, { name: "buyerNote", type: "text", label: "Note to seller (optional)", value: buyerNote, onChange: setBuyerNote, placeholder: "E.g. Bundle deal, long-time fan\u2026", maxLength: 300, "aria-label": "Note to seller" }), _jsx(Text, { size: "xs", color: "muted", children: "The seller will accept, decline, or suggest a counter price." }), _jsx(FormErrorSummary, {}), _jsxs(Stack, { gap: "xs", children: [_jsx(Button, { action: ACTIONS.PRODUCT["make-offer"], variant: "primary", size: "sm", type: "button", isLoading: isPending, disabled: isPending, onClick: () => submit(setFieldError, clearErrors, markSubmitAttempted), children: isPending ? "Sending…" : `Send offer of ${fmt(Number(offerAmount) || 0)}` }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => setState("idle"), disabled: isPending, children: "Cancel" })] })] })) }) })] }));
|
|
82
84
|
}
|
|
@@ -373,7 +373,7 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
373
373
|
condition?: "new" | "used" | "refurbished" | "like_new" | "good" | "fair" | "poor" | "broken" | "graded" | undefined;
|
|
374
374
|
insurance?: boolean | undefined;
|
|
375
375
|
insuranceCost?: number | undefined;
|
|
376
|
-
shippingPaidBy?: "
|
|
376
|
+
shippingPaidBy?: "buyer" | "seller" | undefined;
|
|
377
377
|
auctionEndDate?: string | Date | undefined;
|
|
378
378
|
startingBid?: number | undefined;
|
|
379
379
|
currentBid?: number | undefined;
|
|
@@ -512,7 +512,7 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
512
512
|
condition?: "new" | "used" | "refurbished" | "like_new" | "good" | "fair" | "poor" | "broken" | "graded" | undefined;
|
|
513
513
|
insurance?: boolean | undefined;
|
|
514
514
|
insuranceCost?: number | undefined;
|
|
515
|
-
shippingPaidBy?: "
|
|
515
|
+
shippingPaidBy?: "buyer" | "seller" | undefined;
|
|
516
516
|
auctionEndDate?: string | Date | undefined;
|
|
517
517
|
startingBid?: number | undefined;
|
|
518
518
|
currentBid?: number | undefined;
|
|
@@ -269,7 +269,7 @@ export declare const couponFirestoreSchema: z.ZodObject<{
|
|
|
269
269
|
code: string;
|
|
270
270
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
271
271
|
id: string;
|
|
272
|
-
scope: "
|
|
272
|
+
scope: "seller" | "admin";
|
|
273
273
|
name: string;
|
|
274
274
|
createdAt: string | Date | z.objectOutputType<{
|
|
275
275
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
@@ -327,7 +327,7 @@ export declare const couponFirestoreSchema: z.ZodObject<{
|
|
|
327
327
|
code: string;
|
|
328
328
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
329
329
|
id: string;
|
|
330
|
-
scope: "
|
|
330
|
+
scope: "seller" | "admin";
|
|
331
331
|
name: string;
|
|
332
332
|
createdAt: string | Date | z.objectInputType<{
|
|
333
333
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
@@ -459,7 +459,7 @@ export declare const claimedCouponSnapshotSchema: z.ZodObject<{
|
|
|
459
459
|
}>;
|
|
460
460
|
}, "strip", z.ZodTypeAny, {
|
|
461
461
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
462
|
-
scope: "
|
|
462
|
+
scope: "seller" | "admin";
|
|
463
463
|
name: string;
|
|
464
464
|
discount: {
|
|
465
465
|
value: number;
|
|
@@ -478,7 +478,7 @@ export declare const claimedCouponSnapshotSchema: z.ZodObject<{
|
|
|
478
478
|
description?: string | undefined;
|
|
479
479
|
}, {
|
|
480
480
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
481
|
-
scope: "
|
|
481
|
+
scope: "seller" | "admin";
|
|
482
482
|
name: string;
|
|
483
483
|
discount: {
|
|
484
484
|
value: number;
|
|
@@ -545,7 +545,7 @@ export declare const claimedCouponFirestoreSchema: z.ZodObject<{
|
|
|
545
545
|
}>;
|
|
546
546
|
}, "strip", z.ZodTypeAny, {
|
|
547
547
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
548
|
-
scope: "
|
|
548
|
+
scope: "seller" | "admin";
|
|
549
549
|
name: string;
|
|
550
550
|
discount: {
|
|
551
551
|
value: number;
|
|
@@ -564,7 +564,7 @@ export declare const claimedCouponFirestoreSchema: z.ZodObject<{
|
|
|
564
564
|
description?: string | undefined;
|
|
565
565
|
}, {
|
|
566
566
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
567
|
-
scope: "
|
|
567
|
+
scope: "seller" | "admin";
|
|
568
568
|
name: string;
|
|
569
569
|
discount: {
|
|
570
570
|
value: number;
|
|
@@ -624,7 +624,7 @@ export declare const claimedCouponFirestoreSchema: z.ZodObject<{
|
|
|
624
624
|
couponId: string;
|
|
625
625
|
couponSnapshot: {
|
|
626
626
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
627
|
-
scope: "
|
|
627
|
+
scope: "seller" | "admin";
|
|
628
628
|
name: string;
|
|
629
629
|
discount: {
|
|
630
630
|
value: number;
|
|
@@ -664,7 +664,7 @@ export declare const claimedCouponFirestoreSchema: z.ZodObject<{
|
|
|
664
664
|
couponId: string;
|
|
665
665
|
couponSnapshot: {
|
|
666
666
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
667
|
-
scope: "
|
|
667
|
+
scope: "seller" | "admin";
|
|
668
668
|
name: string;
|
|
669
669
|
discount: {
|
|
670
670
|
value: number;
|
|
@@ -738,7 +738,7 @@ export declare const couponItemSchema: z.ZodObject<{
|
|
|
738
738
|
code: string;
|
|
739
739
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
740
740
|
id: string;
|
|
741
|
-
scope: "
|
|
741
|
+
scope: "seller" | "admin";
|
|
742
742
|
name: string;
|
|
743
743
|
createdAt: string;
|
|
744
744
|
updatedAt: string;
|
|
@@ -762,7 +762,7 @@ export declare const couponItemSchema: z.ZodObject<{
|
|
|
762
762
|
code: string;
|
|
763
763
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
764
764
|
id: string;
|
|
765
|
-
scope: "
|
|
765
|
+
scope: "seller" | "admin";
|
|
766
766
|
name: string;
|
|
767
767
|
createdAt: string;
|
|
768
768
|
updatedAt: string;
|
|
@@ -794,7 +794,7 @@ export declare const promotionsListParamsSchema: z.ZodObject<{
|
|
|
794
794
|
}, "strip", z.ZodTypeAny, {
|
|
795
795
|
sort?: string | undefined;
|
|
796
796
|
page?: number | undefined;
|
|
797
|
-
scope?: "
|
|
797
|
+
scope?: "seller" | "admin" | undefined;
|
|
798
798
|
pageSize?: number | undefined;
|
|
799
799
|
storeId?: string | undefined;
|
|
800
800
|
isActive?: boolean | undefined;
|
|
@@ -802,7 +802,7 @@ export declare const promotionsListParamsSchema: z.ZodObject<{
|
|
|
802
802
|
}, {
|
|
803
803
|
sort?: string | undefined;
|
|
804
804
|
page?: number | undefined;
|
|
805
|
-
scope?: "
|
|
805
|
+
scope?: "seller" | "admin" | undefined;
|
|
806
806
|
pageSize?: number | undefined;
|
|
807
807
|
storeId?: string | undefined;
|
|
808
808
|
isActive?: boolean | undefined;
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { BaseRepository, parseSieveDateValue, type FirebaseSieveResult, type SieveModel } from "../../../providers/db-firebase";
|
|
2
|
+
import { type HistoryActor } from "../../../_internal/shared/history/index";
|
|
3
|
+
/** Who/why for a scammer write that lands in `statusHistory`. */
|
|
4
|
+
export interface ScammerWriteContext {
|
|
5
|
+
actor?: HistoryActor;
|
|
6
|
+
trigger?: string;
|
|
7
|
+
reason?: string;
|
|
8
|
+
note?: string;
|
|
9
|
+
}
|
|
2
10
|
import { type ScammerDocument, type ScammerIncidentDocument, type ScammerCommentDocument, type ScammerCreateInput, type ScammerAdminUpdateInput } from "../schemas/firestore";
|
|
3
11
|
declare class ScammerRepository extends BaseRepository<ScammerDocument> {
|
|
4
12
|
constructor();
|
|
@@ -53,7 +61,16 @@ declare class ScammerRepository extends BaseRepository<ScammerDocument> {
|
|
|
53
61
|
listAll(model: SieveModel): Promise<FirebaseSieveResult<ScammerDocument>>;
|
|
54
62
|
findBySeoSlug(seoSlug: string): Promise<ScammerDocument | null>;
|
|
55
63
|
create(input: ScammerCreateInput): Promise<ScammerDocument>;
|
|
56
|
-
|
|
64
|
+
/**
|
|
65
|
+
* The single admin write path for a scammer profile.
|
|
66
|
+
*
|
|
67
|
+
* The PATCH route used to call `update()` directly, bypassing this method
|
|
68
|
+
* entirely — so a decision on a public accusation against a named person
|
|
69
|
+
* left no record beyond `verifiedBy`/`verifiedAt`, which the NEXT decision
|
|
70
|
+
* overwrites. `prior` comes from the route's own 404 lookup, so the
|
|
71
|
+
* timeline entry costs no second read (Rule #6).
|
|
72
|
+
*/
|
|
73
|
+
adminUpdate(id: string, input: ScammerAdminUpdateInput, ctx?: ScammerWriteContext, prior?: ScammerDocument | null): Promise<void>;
|
|
57
74
|
incrementViews(id: string): Promise<void>;
|
|
58
75
|
/**
|
|
59
76
|
* Search for profiles whose phones, upiIds, emails, or displayNames arrays
|
|
@@ -3,6 +3,8 @@ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../..
|
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
4
|
import { serverLogger } from "../../../monitoring/server-logger";
|
|
5
5
|
import { increment } from "../../../contracts/field-ops";
|
|
6
|
+
import { withHistory } from "../../../_internal/shared/history/index";
|
|
7
|
+
import { SCAMMER_TRACKED_FIELDS, SCAMMER_HISTORY_PII_FIELDS } from "../schemas/firestore";
|
|
6
8
|
import { SCAMMER_COLLECTION, SCAMMER_FIELDS, SCAMMER_INCIDENTS_SUBCOLLECTION, SCAMMER_COMMENTS_SUBCOLLECTION, DEFAULT_SCAMMER_DATA, } from "../schemas/firestore";
|
|
7
9
|
class ScammerRepository extends BaseRepository {
|
|
8
10
|
constructor() {
|
|
@@ -68,9 +70,29 @@ class ScammerRepository extends BaseRepository {
|
|
|
68
70
|
throw new DatabaseError(`Failed to create scammer profile: ${error instanceof Error ? error.message : String(error)}`);
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
|
-
|
|
73
|
+
/**
|
|
74
|
+
* The single admin write path for a scammer profile.
|
|
75
|
+
*
|
|
76
|
+
* The PATCH route used to call `update()` directly, bypassing this method
|
|
77
|
+
* entirely — so a decision on a public accusation against a named person
|
|
78
|
+
* left no record beyond `verifiedBy`/`verifiedAt`, which the NEXT decision
|
|
79
|
+
* overwrites. `prior` comes from the route's own 404 lookup, so the
|
|
80
|
+
* timeline entry costs no second read (Rule #6).
|
|
81
|
+
*/
|
|
82
|
+
async adminUpdate(id, input, ctx, prior) {
|
|
72
83
|
try {
|
|
73
|
-
const
|
|
84
|
+
const current = prior !== undefined ? prior : await this.findById(id);
|
|
85
|
+
const withEntry = withHistory(current, { ...input, updatedAt: new Date() }, {
|
|
86
|
+
tracked: SCAMMER_TRACKED_FIELDS,
|
|
87
|
+
actor: ctx?.actor ?? { role: "system" },
|
|
88
|
+
trigger: ctx?.trigger ?? "adminUpdateScammer",
|
|
89
|
+
reason: ctx?.reason,
|
|
90
|
+
note: ctx?.note,
|
|
91
|
+
// A scammer profile is built entirely from identifying details, and
|
|
92
|
+
// `encryptPiiFields` never descends into arrays.
|
|
93
|
+
piiFields: SCAMMER_HISTORY_PII_FIELDS,
|
|
94
|
+
});
|
|
95
|
+
const data = prepareForFirestore(withEntry ?? { ...input, updatedAt: new Date() });
|
|
74
96
|
await this.getCollection().doc(id).update(data);
|
|
75
97
|
}
|
|
76
98
|
catch (error) {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* - If matches found → suggest linking to existing profile (new incident) instead of new profile
|
|
25
25
|
* - SCAMMER_MATCH_FIELDS lists the Firestore array-contains fields used for this query
|
|
26
26
|
*/
|
|
27
|
+
import type { StatusChangeEntry } from "../../../_internal/shared/history/types";
|
|
27
28
|
import type { ScamType } from "../constants/scam-types";
|
|
28
29
|
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
29
30
|
export declare const ScammerStatusValues: {
|
|
@@ -159,6 +160,16 @@ export interface ScammerDocument extends BaseDocument {
|
|
|
159
160
|
* Reset to false when all contests are reviewed.
|
|
160
161
|
*/
|
|
161
162
|
isContested?: boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Who changed what, when, and why. See § "Status History" in CLAUDE.md.
|
|
165
|
+
*
|
|
166
|
+
* A scammer profile is a public accusation against a named person, so "who
|
|
167
|
+
* verified this and on what note" is the record that matters most on it.
|
|
168
|
+
* `verifiedBy`/`verifiedAt` only ever hold the LAST decision — a profile
|
|
169
|
+
* verified, then removed, then re-verified keeps no trace of the middle.
|
|
170
|
+
*/
|
|
171
|
+
statusHistory?: StatusChangeEntry[];
|
|
172
|
+
statusHistoryTruncated?: number;
|
|
162
173
|
}
|
|
163
174
|
/**
|
|
164
175
|
* One victim's report linked to an existing scammer profile.
|
|
@@ -358,3 +369,16 @@ export declare const SCAMMER_FIELDS: {
|
|
|
358
369
|
export declare const SCAM_PLATFORM_LABELS: Record<ScamPlatform, string>;
|
|
359
370
|
export declare const SOCIAL_PLATFORM_LABELS: Record<SocialPlatform, string>;
|
|
360
371
|
export declare const SCAMMER_STATUS_LABELS: Record<ScammerStatus, string>;
|
|
372
|
+
/**
|
|
373
|
+
* The fields whose changes earn a timeline entry.
|
|
374
|
+
*
|
|
375
|
+
* `verificationNote` is included because it is the REASON attached to a
|
|
376
|
+
* decision, and it is overwritten by the next one.
|
|
377
|
+
*/
|
|
378
|
+
export declare const SCAMMER_TRACKED_FIELDS: readonly ["status", "verifiedBy", "verifiedAt", "verificationNote", "isPubliclyVisible"];
|
|
379
|
+
/**
|
|
380
|
+
* PII on this document, for `withHistory`'s scrub. A scammer profile is built
|
|
381
|
+
* ENTIRELY out of identifying details, so this list is long on purpose —
|
|
382
|
+
* `encryptPiiFields` never descends into arrays, and history is an array.
|
|
383
|
+
*/
|
|
384
|
+
export declare const SCAMMER_HISTORY_PII_FIELDS: readonly ["name", "displayName", "email", "phone", "phoneNumber", "upiId", "bankAccount", "accountNumber", "reporterEmail", "reporterName"];
|
|
@@ -231,3 +231,33 @@ export const SCAMMER_STATUS_LABELS = {
|
|
|
231
231
|
rejected: "Rejected",
|
|
232
232
|
removed: "Removed",
|
|
233
233
|
};
|
|
234
|
+
/**
|
|
235
|
+
* The fields whose changes earn a timeline entry.
|
|
236
|
+
*
|
|
237
|
+
* `verificationNote` is included because it is the REASON attached to a
|
|
238
|
+
* decision, and it is overwritten by the next one.
|
|
239
|
+
*/
|
|
240
|
+
export const SCAMMER_TRACKED_FIELDS = [
|
|
241
|
+
"status",
|
|
242
|
+
"verifiedBy",
|
|
243
|
+
"verifiedAt",
|
|
244
|
+
"verificationNote",
|
|
245
|
+
"isPubliclyVisible",
|
|
246
|
+
];
|
|
247
|
+
/**
|
|
248
|
+
* PII on this document, for `withHistory`'s scrub. A scammer profile is built
|
|
249
|
+
* ENTIRELY out of identifying details, so this list is long on purpose —
|
|
250
|
+
* `encryptPiiFields` never descends into arrays, and history is an array.
|
|
251
|
+
*/
|
|
252
|
+
export const SCAMMER_HISTORY_PII_FIELDS = [
|
|
253
|
+
"name",
|
|
254
|
+
"displayName",
|
|
255
|
+
"email",
|
|
256
|
+
"phone",
|
|
257
|
+
"phoneNumber",
|
|
258
|
+
"upiId",
|
|
259
|
+
"bankAccount",
|
|
260
|
+
"accountNumber",
|
|
261
|
+
"reporterEmail",
|
|
262
|
+
"reporterName",
|
|
263
|
+
];
|