diff --git a/package.json b/package.json index 819dd6e..28ab37a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "app-ts", - "version": "1.5.9", + "version": "1.6.0", "description": "", "main": "dist/server.js", "scripts": { diff --git a/src/admin/offers.css b/src/admin/offers.css index f409554..563175d 100644 --- a/src/admin/offers.css +++ b/src/admin/offers.css @@ -491,10 +491,69 @@ display: none; } +@media (max-width: 768px) { + .offers-items-table .admin-table td .admin-form-input { + font-size: 16px; + min-height: 44px; + padding: 9px 10px; + } + + /* Give the description column more room by shrinking numeric columns */ + .offers-col-qty { + width: 4rem !important; + } + .offers-col-unit { + width: 4rem !important; + } + .offers-col-price { + width: 5.5rem !important; + } + .offers-col-included { + width: 3.5rem !important; + } + .offers-col-total { + width: 5.5rem !important; + } + .offers-col-del { + width: 2.5rem !important; + } +} + @media (max-width: 640px) { .offers-items-table { margin: 0 -1rem; width: calc(100% + 2rem); + border-radius: 0; + border-left: none; + border-right: none; + } + + .offers-items-table .admin-table { + min-width: 700px; + } + + .offers-items-table .admin-table td { + padding: 6px; + font-size: 12px; + } + + .offers-items-table .admin-table th { + font-size: 10px; + padding: 6px; + } + + /* Further reduce numeric columns on very small screens */ + .offers-col-qty { + width: 3.5rem !important; + } + .offers-col-unit { + width: 3.5rem !important; + } + .offers-col-price { + width: 5rem !important; + } + .offers-col-total { + width: 5rem !important; } } diff --git a/src/admin/pages/OfferDetail.tsx b/src/admin/pages/OfferDetail.tsx index f1122c0..ca9fe5f 100644 --- a/src/admin/pages/OfferDetail.tsx +++ b/src/admin/pages/OfferDetail.tsx @@ -274,9 +274,10 @@ function loadOfferDraft(): { sections?: unknown[]; } | null { try { - const raw = localStorage.getItem("boha_offer_draft"); + const raw = localStorage.getItem(DRAFT_KEY); return raw ? JSON.parse(raw) : null; - } catch { + } catch (e) { + console.error("Failed to load offer draft:", e); return null; } } @@ -353,17 +354,28 @@ export default function OfferDetail() { const draft = loadOfferDraft(); if (draft?.form) { return { - ...emptyForm, + quotation_number: + (draft.form.quotation_number as string) || emptyForm.quotation_number, project_code: (draft.form.project_code as string) || emptyForm.project_code, + customer_id: + (draft.form.customer_id as number | null) ?? emptyForm.customer_id, customer_name: (draft.form.customer_name as string) || emptyForm.customer_name, created_at: (draft.form.created_at as string) || emptyForm.created_at, valid_until: (draft.form.valid_until as string) || emptyForm.valid_until, currency: (draft.form.currency as string) || emptyForm.currency, - customer_id: - (draft.form.customer_id as number | null) ?? emptyForm.customer_id, + language: (draft.form.language as string) || emptyForm.language, + vat_rate: (draft.form.vat_rate as number) ?? emptyForm.vat_rate, + apply_vat: (draft.form.apply_vat as boolean) ?? emptyForm.apply_vat, + exchange_rate: + (draft.form.exchange_rate as string) || emptyForm.exchange_rate, + scope_title: + (draft.form.scope_title as string) || emptyForm.scope_title, + scope_description: + (draft.form.scope_description as string) || + emptyForm.scope_description, }; } return emptyForm; @@ -590,22 +602,27 @@ export default function OfferDetail() { useEffect(() => { if (isEdit) return; try { + const data = JSON.parse(debouncedDraft); const draft = { form: { - project_code: form.project_code, - customer_id: form.customer_id, - customer_name: form.customer_name, - created_at: form.created_at, - valid_until: form.valid_until, - currency: form.currency, + project_code: data.form.project_code ?? "", + customer_id: data.form.customer_id ?? null, + customer_name: data.form.customer_name ?? "", + created_at: data.form.created_at ?? "", + valid_until: data.form.valid_until ?? "", + currency: data.form.currency ?? "CZK", + language: data.form.language ?? "EN", + vat_rate: data.form.vat_rate ?? 21, + apply_vat: data.form.apply_vat ?? false, + exchange_rate: data.form.exchange_rate ?? "", }, - items, - sections, + items: data.items, + sections: data.sections, savedAt: new Date().toISOString(), }; localStorage.setItem(DRAFT_KEY, JSON.stringify(draft)); - } catch { - /* localStorage full or unavailable */ + } catch (e) { + console.error("Failed to save offer draft:", e); } }, [debouncedDraft]); // eslint-disable-line react-hooks/exhaustive-deps @@ -697,8 +714,8 @@ export default function OfferDetail() { if (!isEdit) { try { localStorage.removeItem(DRAFT_KEY); - } catch { - /* ignore */ + } catch (e) { + console.error("Failed to remove offer draft:", e); } } if (!isEdit && result.data?.id) { @@ -1283,7 +1300,7 @@ export default function OfferDetail() {

)} -
+
# - Popis - Množství - Jednotka - Cena/ks - + Popis + + Množství + + + Jednotka + + + Cena/ks + + V ceně - + Celkem {!isInvalidated && !isLockedByOther && ( - + )}