@mohasinac/appkit 4.63.1 ā 4.63.3
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/.build-stamp.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"stampVersion": 1,
|
|
3
|
-
"version": "4.63.
|
|
4
|
-
"head": "
|
|
3
|
+
"version": "4.63.3",
|
|
4
|
+
"head": "aea6affd6821cc7b43256c25614e42422df7177c",
|
|
5
5
|
"branch": "main",
|
|
6
6
|
"dirty": false,
|
|
7
|
-
"builtAt": "2026-10-
|
|
7
|
+
"builtAt": "2026-10-23T10:12:11.248Z"
|
|
8
8
|
}
|
|
@@ -10,7 +10,11 @@ import { normalizeError } from "../../../errors/normalize.js";
|
|
|
10
10
|
import { DataListingView } from "../../admin/components/DataListingView.js";
|
|
11
11
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry.js";
|
|
12
12
|
import { DISPUTE_KIND, } from "../../../_internal/shared/features/orders/dispute-reasons.js";
|
|
13
|
-
import { DISPUTE_REASON_LABEL_MAX, } from "../schemas/firestore.js";
|
|
13
|
+
import { DISPUTE_REASON_FIELDS, DISPUTE_REASON_LABEL_MAX, } from "../schemas/firestore.js";
|
|
14
|
+
/* š The DEFINING module, never `@mohasinac/appkit/client` ā in a view that is
|
|
15
|
+
* fine, but these are the canonical sort helpers and `audit-sieve-constants-views`
|
|
16
|
+
* is what blocks the raw `"label"` / `"-label"` literals below. */
|
|
17
|
+
import { sortBy } from "../../../constants/sort.js";
|
|
14
18
|
/**
|
|
15
19
|
* What each kind MEANS, in one place.
|
|
16
20
|
*
|
|
@@ -104,7 +108,7 @@ export function AdminDisputeReasonsView({ endpoint, onSetActive, onCreate, }) {
|
|
|
104
108
|
subtitle: "What buyers can pick when reporting a problem. A reason's kind decides which gate it inherits ā the wording is yours.",
|
|
105
109
|
endpoint: endpoint,
|
|
106
110
|
queryKey: ["admin", "dispute-reasons"],
|
|
107
|
-
defaultSort: "
|
|
111
|
+
defaultSort: sortBy(DISPUTE_REASON_FIELDS.LABEL, "ASC"),
|
|
108
112
|
/*
|
|
109
113
|
* š REQUIRED on `ListingViewConfig`, and the default must be one of
|
|
110
114
|
* these or the dropdown opens with nothing selected (Root Cause #63).
|
|
@@ -112,9 +116,9 @@ export function AdminDisputeReasonsView({ endpoint, onSetActive, onCreate, }) {
|
|
|
112
116
|
* wording and which kind a reason belongs to.
|
|
113
117
|
*/
|
|
114
118
|
sortOptions: [
|
|
115
|
-
{ value: "
|
|
116
|
-
{ value: "
|
|
117
|
-
{ value: "
|
|
119
|
+
{ value: sortBy(DISPUTE_REASON_FIELDS.LABEL, "ASC"), label: "Wording (AāZ)" },
|
|
120
|
+
{ value: sortBy(DISPUTE_REASON_FIELDS.LABEL, "DESC"), label: "Wording (ZāA)" },
|
|
121
|
+
{ value: sortBy(DISPUTE_REASON_FIELDS.KIND, "ASC"), label: "Kind" },
|
|
118
122
|
],
|
|
119
123
|
filterKeys: ["scope"],
|
|
120
124
|
filterDefaults: { scope: "active" },
|
|
@@ -209,6 +209,13 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
|
|
|
209
209
|
// validator on every render and re-fire the effect below in a loop.
|
|
210
210
|
const onValidationChangeRef = useRef(onValidationChange);
|
|
211
211
|
onValidationChangeRef.current = onValidationChange;
|
|
212
|
+
/**
|
|
213
|
+
* The last error map actually HANDED TO the caller.
|
|
214
|
+
*
|
|
215
|
+
* š This is what stops an infinite render loop, and the loop froze the whole
|
|
216
|
+
* app ā see `emitValidation` below.
|
|
217
|
+
*/
|
|
218
|
+
const lastEmittedRef = useRef(null);
|
|
212
219
|
/**
|
|
213
220
|
* Are two field-error maps the same set of messages?
|
|
214
221
|
*
|
|
@@ -232,6 +239,44 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
|
|
|
232
239
|
.filter((s) => s.fields?.some((f) => keys.some((k) => k === f || k.startsWith(`${f}.`))))
|
|
233
240
|
.map((s) => s.id);
|
|
234
241
|
}, [ordered]);
|
|
242
|
+
/**
|
|
243
|
+
* Hand the result to the caller, but ONLY when it actually changed.
|
|
244
|
+
*
|
|
245
|
+
* ## š The loop this closes froze every admin editor drawer
|
|
246
|
+
*
|
|
247
|
+
* `runValidation` runs on EVERY render by design: `values` is a fresh object
|
|
248
|
+
* each time, so `ordered` ā `erroringSectionIds` ā `runValidation` are all
|
|
249
|
+
* new identities and the effect below re-fires. The comment there argues
|
|
250
|
+
* that is safe, and for this component's OWN state it is ā `sameErrors` and
|
|
251
|
+
* the `forceOpenIds` length check both return the previous object, so React
|
|
252
|
+
* bails out.
|
|
253
|
+
*
|
|
254
|
+
* It was NOT safe for the caller. `onValidationChange` was invoked
|
|
255
|
+
* unconditionally on every run, and the real call site is
|
|
256
|
+
* `onValidationChange={() => form.validate(draft)}` ā which setStates. So:
|
|
257
|
+
* render ā effect ā emit ā caller setState ā render ā ⦠forever. React
|
|
258
|
+
* reported "Maximum update depth exceeded" ~4x/second.
|
|
259
|
+
*
|
|
260
|
+
* The visible symptom was not a validation bug at all. A React tree stuck in
|
|
261
|
+
* an update loop never commits a transition, and App Router navigation IS a
|
|
262
|
+
* transition ā so with the drawer open, Cancel, the X, the sort dropdown and
|
|
263
|
+
* even plain nav links all did nothing. Measured on production: `router.replace`
|
|
264
|
+
* produced ZERO `history.replaceState` calls, no error, no failed request.
|
|
265
|
+
* The only escape was a full page reload.
|
|
266
|
+
*
|
|
267
|
+
* Emitting only on a real change is also what the prop NAME promises; firing
|
|
268
|
+
* it with an identical map on every render was never meaningful to a caller.
|
|
269
|
+
*/
|
|
270
|
+
const emitValidation = useCallback((next, index) => {
|
|
271
|
+
const prev = lastEmittedRef.current;
|
|
272
|
+
if (prev && sameErrors(prev, next))
|
|
273
|
+
return;
|
|
274
|
+
lastEmittedRef.current = next;
|
|
275
|
+
onValidationChangeRef.current?.(next, index);
|
|
276
|
+
},
|
|
277
|
+
// `sameErrors` is a pure local helper; `lastEmittedRef`/`onValidationChangeRef`
|
|
278
|
+
// are refs. Nothing here needs to re-create this callback.
|
|
279
|
+
[]);
|
|
235
280
|
const runValidation = useCallback((nextValues) => {
|
|
236
281
|
const activeSchema = schema ?? inheritedSchema;
|
|
237
282
|
if (!activeSchema)
|
|
@@ -243,7 +288,7 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
|
|
|
243
288
|
if (parsed.success) {
|
|
244
289
|
setFieldErrors((prev) => (Object.keys(prev).length === 0 ? prev : {}));
|
|
245
290
|
setForceOpenIds((prev) => (prev.length === 0 ? prev : []));
|
|
246
|
-
|
|
291
|
+
emitValidation({}, fieldToSectionIndex);
|
|
247
292
|
return;
|
|
248
293
|
}
|
|
249
294
|
const next = {};
|
|
@@ -266,7 +311,7 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
|
|
|
266
311
|
const kept = prev.filter((id) => stillBad.has(id));
|
|
267
312
|
return kept.length === prev.length ? prev : kept;
|
|
268
313
|
});
|
|
269
|
-
|
|
314
|
+
emitValidation(next, fieldToSectionIndex);
|
|
270
315
|
}, [schema, inheritedSchema, fieldToSectionIndex, erroringSectionIds]);
|
|
271
316
|
/*
|
|
272
317
|
* š `values` MUST be a dependency, and leaving it out cost the entire admin
|