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:
BOHA
2026-03-27 13:44:53 +01:00
parent a3ef37d0d2
commit 8cdf057ab3
13 changed files with 173 additions and 117 deletions

View File

@@ -639,17 +639,16 @@ export default function Settings() {
</div>
<div>
<div
className="fw-500 text-md"
style={{
fontWeight: 500,
color: "var(--text-primary)",
fontSize: "0.875rem",
}}
>
Povinné dvoufaktorové ověření (2FA)
</div>
<div
className="text-xs"
style={{
fontSize: "0.75rem",
color: "var(--text-secondary)",
}}
>
@@ -781,16 +780,16 @@ export default function Settings() {
<tr key={role.id}>
<td>
<div
className="fw-500"
style={{
fontWeight: 500,
color: "var(--text-primary)",
}}
>
{role.display_name}
</div>
<div
className="text-xs"
style={{
fontSize: "0.75rem",
color: "var(--text-tertiary)",
}}
>
@@ -1077,9 +1076,9 @@ export default function Settings() {
/>
</FormField>
<small
className="text-xs"
style={{
color: "var(--text-tertiary)",
fontSize: "0.75rem",
}}
>
Změna se projeví po restartu serveru
@@ -1179,10 +1178,9 @@ export default function Settings() {
/>
)}
<div
className="fw-600 text-md"
style={{
fontWeight: 600,
marginBottom: "0.5rem",
fontSize: "0.875rem",
}}
>
{cfg.label}
@@ -1355,8 +1353,8 @@ export default function Settings() {
<div className="admin-card-body">
{systemInfo ? (
<table
className="w-full"
style={{
width: "100%",
fontSize: "0.85rem",
borderCollapse: "collapse",
}}
@@ -1374,16 +1372,16 @@ export default function Settings() {
).map(([label, val]) => (
<tr key={label}>
<td
className="whitespace-nowrap"
style={{
padding: "6px 12px 6px 0",
color: "var(--text-secondary)",
whiteSpace: "nowrap",
width: 160,
}}
>
{label}
</td>
<td style={{ padding: "6px 0", fontWeight: 500 }}>
<td className="fw-500" style={{ padding: "6px 0" }}>
{val}
</td>
</tr>
@@ -1516,9 +1514,9 @@ export default function Settings() {
</span>
{info?.configured && (
<span
className="text-xs"
style={{
marginLeft: 8,
fontSize: "0.75rem",
color: "var(--text-tertiary)",
}}
>
@@ -1548,8 +1546,7 @@ export default function Settings() {
<button
onClick={handleSaveSystemSettings}
disabled={sysSettingsSaving}
className="admin-btn admin-btn-primary"
style={{ width: "100%" }}
className="admin-btn admin-btn-primary w-full"
>
{sysSettingsSaving ? (
<>
@@ -1637,9 +1634,9 @@ export default function Settings() {
/>
{!editingRole && (
<small
className="text-xs"
style={{
color: "var(--text-tertiary)",
fontSize: "0.75rem",
}}
>
Pouze malá písmena, čísla a pomlčky. Nelze později