fix(offers,invoices): per-button save loading state (only the clicked button spins)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -558,6 +558,10 @@ export default function InvoiceDetail() {
|
|||||||
]);
|
]);
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
// Which save action is in flight ("draft" | LIVE_STATUS | "save"), so only the
|
||||||
|
// clicked button shows its spinner — `saving` still disables all of them to
|
||||||
|
// prevent a concurrent double-submit.
|
||||||
|
const [savingAction, setSavingAction] = useState<string | null>(null);
|
||||||
const [dataReady, setDataReady] = useState(false);
|
const [dataReady, setDataReady] = useState(false);
|
||||||
const [invoiceNumber, setInvoiceNumber] = useState("");
|
const [invoiceNumber, setInvoiceNumber] = useState("");
|
||||||
const initialSnapshotRef = useRef<string | null>(null);
|
const initialSnapshotRef = useRef<string | null>(null);
|
||||||
@@ -976,6 +980,7 @@ export default function InvoiceDetail() {
|
|||||||
if (Object.keys(newErrors).length > 0) return;
|
if (Object.keys(newErrors).length > 0) return;
|
||||||
|
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
|
setSavingAction(targetStatus ?? "save");
|
||||||
try {
|
try {
|
||||||
const payload: Record<string, unknown> = {
|
const payload: Record<string, unknown> = {
|
||||||
...form,
|
...form,
|
||||||
@@ -1016,6 +1021,7 @@ export default function InvoiceDetail() {
|
|||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
|
setSavingAction(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1710,7 +1716,7 @@ export default function InvoiceDetail() {
|
|||||||
onClick={() => handleCreateSubmit("draft")}
|
onClick={() => handleCreateSubmit("draft")}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
>
|
>
|
||||||
{saving ? (
|
{savingAction === "draft" ? (
|
||||||
<CircularProgress size={16} color="inherit" />
|
<CircularProgress size={16} color="inherit" />
|
||||||
) : (
|
) : (
|
||||||
"Uložit koncept"
|
"Uložit koncept"
|
||||||
@@ -1720,7 +1726,7 @@ export default function InvoiceDetail() {
|
|||||||
onClick={() => handleCreateSubmit(LIVE_STATUS)}
|
onClick={() => handleCreateSubmit(LIVE_STATUS)}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
>
|
>
|
||||||
{saving ? (
|
{savingAction === LIVE_STATUS ? (
|
||||||
<>
|
<>
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
size={16}
|
size={16}
|
||||||
@@ -1745,7 +1751,7 @@ export default function InvoiceDetail() {
|
|||||||
onClick={() => handleCreateSubmit("draft")}
|
onClick={() => handleCreateSubmit("draft")}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
>
|
>
|
||||||
{saving ? (
|
{savingAction === "draft" ? (
|
||||||
<CircularProgress size={16} color="inherit" />
|
<CircularProgress size={16} color="inherit" />
|
||||||
) : (
|
) : (
|
||||||
"Uložit koncept"
|
"Uložit koncept"
|
||||||
@@ -1756,7 +1762,7 @@ export default function InvoiceDetail() {
|
|||||||
onClick={() => handleCreateSubmit(LIVE_STATUS)}
|
onClick={() => handleCreateSubmit(LIVE_STATUS)}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
>
|
>
|
||||||
{saving ? (
|
{savingAction === LIVE_STATUS ? (
|
||||||
<>
|
<>
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
size={16}
|
size={16}
|
||||||
@@ -1787,7 +1793,7 @@ export default function InvoiceDetail() {
|
|||||||
{isEdit && invoice && invoice.status !== "draft" && (
|
{isEdit && invoice && invoice.status !== "draft" && (
|
||||||
<>
|
<>
|
||||||
<Button onClick={() => handleCreateSubmit()} disabled={saving}>
|
<Button onClick={() => handleCreateSubmit()} disabled={saving}>
|
||||||
{saving ? (
|
{savingAction === "save" ? (
|
||||||
<>
|
<>
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
size={16}
|
size={16}
|
||||||
|
|||||||
@@ -554,6 +554,10 @@ export default function OfferDetail() {
|
|||||||
|
|
||||||
const loading = isEdit && offerQuery.isLoading;
|
const loading = isEdit && offerQuery.isLoading;
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
// Which save action is in flight ("draft" | LIVE_STATUS | "save"), so only the
|
||||||
|
// clicked button shows its spinner — `saving` still disables all of them to
|
||||||
|
// prevent a concurrent double-submit.
|
||||||
|
const [savingAction, setSavingAction] = useState<string | null>(null);
|
||||||
const [errors, setErrors] = useState<Record<string, string | undefined>>({});
|
const [errors, setErrors] = useState<Record<string, string | undefined>>({});
|
||||||
const [form, setForm] = useState<OfferForm>(emptyForm);
|
const [form, setForm] = useState<OfferForm>(emptyForm);
|
||||||
const [items, setItems] = useState<OfferItem[]>(() => [emptyItem()]);
|
const [items, setItems] = useState<OfferItem[]>(() => [emptyItem()]);
|
||||||
@@ -807,6 +811,7 @@ export default function OfferDetail() {
|
|||||||
if (Object.keys(newErrors).length > 0) return;
|
if (Object.keys(newErrors).length > 0) return;
|
||||||
|
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
|
setSavingAction(targetStatus ?? "save");
|
||||||
try {
|
try {
|
||||||
const url = isEdit ? `${API_BASE}/offers/${id}` : `${API_BASE}/offers`;
|
const url = isEdit ? `${API_BASE}/offers/${id}` : `${API_BASE}/offers`;
|
||||||
const payload: any = {
|
const payload: any = {
|
||||||
@@ -883,6 +888,7 @@ export default function OfferDetail() {
|
|||||||
alert.error("Chyba připojení");
|
alert.error("Chyba připojení");
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
|
setSavingAction(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1136,7 +1142,7 @@ export default function OfferDetail() {
|
|||||||
onClick={() => handleSave("draft")}
|
onClick={() => handleSave("draft")}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
>
|
>
|
||||||
{saving ? (
|
{savingAction === "draft" ? (
|
||||||
<CircularProgress size={16} color="inherit" />
|
<CircularProgress size={16} color="inherit" />
|
||||||
) : (
|
) : (
|
||||||
"Uložit koncept"
|
"Uložit koncept"
|
||||||
@@ -1146,7 +1152,7 @@ export default function OfferDetail() {
|
|||||||
onClick={() => handleSave(LIVE_STATUS)}
|
onClick={() => handleSave(LIVE_STATUS)}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
>
|
>
|
||||||
{saving ? (
|
{savingAction === LIVE_STATUS ? (
|
||||||
<>
|
<>
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
size={16}
|
size={16}
|
||||||
@@ -1171,7 +1177,7 @@ export default function OfferDetail() {
|
|||||||
onClick={() => handleSave("draft")}
|
onClick={() => handleSave("draft")}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
>
|
>
|
||||||
{saving ? (
|
{savingAction === "draft" ? (
|
||||||
<CircularProgress size={16} color="inherit" />
|
<CircularProgress size={16} color="inherit" />
|
||||||
) : (
|
) : (
|
||||||
"Uložit koncept"
|
"Uložit koncept"
|
||||||
@@ -1182,7 +1188,7 @@ export default function OfferDetail() {
|
|||||||
onClick={() => handleSave(LIVE_STATUS)}
|
onClick={() => handleSave(LIVE_STATUS)}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
>
|
>
|
||||||
{saving ? (
|
{savingAction === LIVE_STATUS ? (
|
||||||
<>
|
<>
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
size={16}
|
size={16}
|
||||||
@@ -1202,7 +1208,7 @@ export default function OfferDetail() {
|
|||||||
{/* ── Edit mode, live (non-draft) offer — unchanged plain save ── */}
|
{/* ── Edit mode, live (non-draft) offer — unchanged plain save ── */}
|
||||||
{isEdit && !readOnly && offerStatus !== "draft" && (
|
{isEdit && !readOnly && offerStatus !== "draft" && (
|
||||||
<Button onClick={() => handleSave()} disabled={saving}>
|
<Button onClick={() => handleSave()} disabled={saving}>
|
||||||
{saving ? (
|
{savingAction === "save" ? (
|
||||||
<>
|
<>
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
size={16}
|
size={16}
|
||||||
|
|||||||
Reference in New Issue
Block a user