fix(wizard,quotes,dates): reliable error routing, single-project accept, Persian date/time input
Wizard: - failed saves now route to the exact step+field: a new data-field attribute on every required section (supplier_id, line_items, termsheets, project, quote_date, expiry_date, notes, proforma) plus resolveErrorTarget() that maps ApiError.field / message keywords (supplier, line item/material/unit/quantity, termsheet/schedule/milestone, project, dates, notes, file) to the offending step — the «رفع خطا» toast action scrolls/focuses the right control instead of guessing - material records are no longer created on the fly while typing (rememberMaterial no longer fires addMaterial per blur); materials/specs/units are written once during the final save, avoiding half-created catalog rows Accept flow: - quote_service no longer rewrites LineItemProjects on accept — those candidate-project rows stay 100% untouched and only item_decisions.project_id is written, matching the per-item target-project model and the UI payload (item_projects: []) Dates/time: - JalaliDatePicker: hour/minute are now editable text fields (Persian digits, clamping, 3-digit retype handling) with a controlled useEffect that only rewrites the inputs when the numeric value actually changes — no more wiping in-progress typing; date-only detection tightened to a full YYYY-MM-DD match so datetime strings are not misread Also: /api validation logging (RequestValidationError) now includes the path and error list for faster diagnosis. Verified: api + frontend image builds (python, tsc, vite) clean on top of origin/production (incl. the team's mobile layout merges).
This commit is contained in:
@@ -37,6 +37,7 @@ import {
|
||||
interface EditItemRow {
|
||||
key: string;
|
||||
origLi?: LineItem;
|
||||
projects?: { id: string; name: string }[];
|
||||
cat: string;
|
||||
brand: string;
|
||||
specs: string[];
|
||||
@@ -148,7 +149,17 @@ export function EditModal({ quoteId, onClose }: EditModalProps) {
|
||||
if (!q) return;
|
||||
const sup = supplierById(suppliers, q.supplier_id);
|
||||
setSupplierName(sup.name);
|
||||
setSelectedProjects(q.projects && q.projects.length ? [...q.projects] : q.project ? [q.project] : []);
|
||||
// حفظ تمام پروژههای کاندیدا: اتحاد پروژههای سطح پیشنهاد و پروژههای کاندیدای هر قلم
|
||||
const candidateSet = new Set<string>();
|
||||
(q.projects || []).forEach((p) => p && candidateSet.add(p));
|
||||
if (q.project) candidateSet.add(q.project);
|
||||
q.line_items.forEach((li) => {
|
||||
(li.projects || []).forEach((p) => {
|
||||
const n = typeof p === "string" ? p : p.name;
|
||||
if (n) candidateSet.add(n);
|
||||
});
|
||||
});
|
||||
setSelectedProjects([...candidateSet]);
|
||||
setNotes(q.notes || "");
|
||||
setItemRows(
|
||||
q.line_items.map((li) => {
|
||||
@@ -156,6 +167,7 @@ export function EditModal({ quoteId, onClose }: EditModalProps) {
|
||||
return {
|
||||
key: li.id,
|
||||
origLi: li,
|
||||
projects: li.projects && li.projects.length ? [...li.projects] : [],
|
||||
cat: m.category || "",
|
||||
brand: m.brand || "",
|
||||
specs: materialSpecs(m),
|
||||
@@ -349,8 +361,15 @@ export function EditModal({ quoteId, onClose }: EditModalProps) {
|
||||
acceptedTsId = null;
|
||||
}
|
||||
|
||||
const candProjs =
|
||||
row.projects && row.projects.length
|
||||
? row.projects
|
||||
: row.origLi?.projects && row.origLi.projects.length
|
||||
? row.origLi.projects
|
||||
: selectedProjects.map((pname) => ({ id: "", name: pname }));
|
||||
|
||||
newItems.push({
|
||||
id: "li-" + (newItems.length + 1),
|
||||
id: row.origLi ? row.origLi.id : row.key.startsWith("new-") ? row.key : "li-" + (newItems.length + 1),
|
||||
material_id: materialId,
|
||||
quantity: qty,
|
||||
unit,
|
||||
@@ -360,7 +379,7 @@ export function EditModal({ quoteId, onClose }: EditModalProps) {
|
||||
accepted_ts_id: acceptedTsId,
|
||||
rejected,
|
||||
arrived_date: row.origLi?.arrived_date ?? null,
|
||||
projects: row.origLi?.projects ?? [],
|
||||
projects: candProjs,
|
||||
target_project: status === "offered" ? null : (row.origLi?.target_project ?? null),
|
||||
target_project_id: status === "offered" ? null : (row.origLi?.target_project_id ?? null),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user