feat: CNB exchange rates, multi-currency KPI stats, invoice PDF VAT in CZK
- ČNB exchange rate service with date-specific rates and caching - Invoice/received invoice stats convert foreign currencies to CZK - Dashboard revenue converts all currencies to CZK - Invoice PDF: VAT recap table always in CZK with CNB rate footer - Inline styles replaced with utility classes (step 4 cleanup) - Spinner animation exempt from prefers-reduced-motion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -180,9 +180,7 @@ function SortableItemRow({
|
||||
</button>
|
||||
</td>
|
||||
)}
|
||||
<td style={{ textAlign: "center", color: "var(--text-tertiary)" }}>
|
||||
{index + 1}
|
||||
</td>
|
||||
<td className="text-center text-tertiary">{index + 1}</td>
|
||||
<td style={{ verticalAlign: "top" }}>
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "column", gap: "0.25rem" }}
|
||||
@@ -191,10 +189,9 @@ function SortableItemRow({
|
||||
type="text"
|
||||
value={item.description}
|
||||
onChange={(e) => onUpdate("description", e.target.value)}
|
||||
className="admin-form-input"
|
||||
className="admin-form-input fw-500"
|
||||
placeholder="Název položky"
|
||||
readOnly={readOnly}
|
||||
style={{ fontWeight: 500 }}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
@@ -240,7 +237,7 @@ function SortableItemRow({
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
</td>
|
||||
<td style={{ textAlign: "center" }}>
|
||||
<td className="text-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={item.is_included_in_total}
|
||||
@@ -248,10 +245,7 @@ function SortableItemRow({
|
||||
disabled={readOnly}
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
className="admin-mono"
|
||||
style={{ textAlign: "right", fontWeight: 600 }}
|
||||
>
|
||||
<td className="admin-mono text-right fw-600">
|
||||
{formatCurrency(lineTotal, currency)}
|
||||
</td>
|
||||
{!readOnly && (
|
||||
@@ -874,11 +868,10 @@ export default function OfferDetail() {
|
||||
{isEdit ? `Nabídka ${form.quotation_number}` : "Nová nabídka"}
|
||||
{isInvalidated && (
|
||||
<span
|
||||
className="admin-badge admin-badge-danger"
|
||||
className="admin-badge admin-badge-danger text-xs"
|
||||
style={{
|
||||
marginLeft: "0.75rem",
|
||||
verticalAlign: "middle",
|
||||
fontSize: "0.75rem",
|
||||
}}
|
||||
>
|
||||
Zneplatněna
|
||||
@@ -1208,10 +1201,7 @@ export default function OfferDetail() {
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<label
|
||||
className="admin-form-checkbox"
|
||||
style={{ whiteSpace: "nowrap" }}
|
||||
>
|
||||
<label className="admin-form-checkbox whitespace-nowrap">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={form.apply_vat}
|
||||
@@ -1672,7 +1662,7 @@ export default function OfferDetail() {
|
||||
<FormField label="Příloha (PDF)">
|
||||
{orderAttachment ? (
|
||||
<div className="flex-row gap-2">
|
||||
<span style={{ fontSize: "0.875rem" }}>
|
||||
<span className="text-md">
|
||||
{orderAttachment.name}{" "}
|
||||
<span className="text-tertiary">
|
||||
({(orderAttachment.size / 1024).toFixed(0)} KB)
|
||||
|
||||
Reference in New Issue
Block a user