refactor: CSS utility tridy + slouceni badge souboru

- pridano 20 utility trid (flex-1, mb-2, text-right, fw-500, admin-spinner-sm, atd.)
- nahrazeno ~100 opakovanych inline stylu ve 39 JSX souborech
- slouceno leave.css, orders.css, projects.css do admin.css (status badges)
- bundle size: 228.91 -> 228.43 kB (-0.48 kB)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 11:27:15 +01:00
parent f7466f0667
commit 10fbb9ebc7
106 changed files with 431 additions and 475 deletions

View File

@@ -11,9 +11,6 @@ import './admin.css'
import './login.css'
import './dashboard.css'
import './attendance.css'
import './leave.css'
import './orders.css'
import './projects.css'
import './settings.css'
import './offers.css'
import './invoices.css'

View File

@@ -291,7 +291,29 @@ img {
text-decoration: underline;
}
/* Login styles moved to login.css */
/* Layout utilities */
.flex-1 { flex: 1; }
.flex-row { display: flex; align-items: center; }
.flex-row-gap { display: flex; align-items: center; gap: var(--space-3); }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
/* Spacing utilities */
.mb-2 { margin-bottom: var(--space-2); }
.mb-4 { margin-bottom: var(--space-4); }
.mb-6 { margin-bottom: var(--space-6); }
.mt-2 { margin-top: var(--space-2); }
.mt-6 { margin-top: var(--space-6); }
.gap-2 { gap: var(--space-2); }
.gap-4 { gap: var(--space-4); }
.gap-5 { gap: var(--space-5); }
/* Typography utilities */
.fw-500 { font-weight: 500; }
.text-right { text-align: right; }
.text-center { text-align: center; }
/* Spinner variant */
.admin-spinner-sm { width: 16px; height: 16px; border-width: 2px; }
/* ============================================================================
Forms
@@ -434,11 +456,10 @@ img {
/* Checkbox */
.admin-form-checkbox {
display: inline-flex;
align-items: center;
align-items: flex-start;
gap: 0;
cursor: pointer;
user-select: none;
white-space: nowrap;
}
.admin-form-checkbox input {
@@ -508,7 +529,7 @@ img {
align-items: center;
font-size: 13px;
color: var(--text-secondary);
line-height: 1;
line-height: 1.4;
}
/* Reorderable List */
@@ -1500,6 +1521,23 @@ img {
color: var(--danger);
}
/* Status Badges - Leave Requests */
.badge-pending { background: color-mix(in srgb, var(--warning) 15%, transparent); color: var(--warning); }
.badge-approved { background: color-mix(in srgb, var(--success) 15%, transparent); color: var(--success); }
.badge-rejected { background: color-mix(in srgb, var(--danger) 15%, transparent); color: var(--danger); }
.badge-cancelled { background: var(--muted-light); color: var(--muted); }
/* Status Badges - Orders */
.admin-badge-order-prijata { background: color-mix(in srgb, var(--info) 15%, transparent); color: var(--info); }
.admin-badge-order-realizace { background: color-mix(in srgb, var(--warning) 15%, transparent); color: var(--warning); }
.admin-badge-order-dokoncena { background: color-mix(in srgb, var(--success) 15%, transparent); color: var(--success); }
.admin-badge-order-stornovana { background: color-mix(in srgb, var(--danger) 15%, transparent); color: var(--danger); }
/* Status Badges - Projects */
.admin-badge-project-aktivni { background: color-mix(in srgb, var(--success) 15%, transparent); color: var(--success); }
.admin-badge-project-dokonceny { background: color-mix(in srgb, var(--info) 15%, transparent); color: var(--info); }
.admin-badge-project-zruseny { background: color-mix(in srgb, var(--danger) 15%, transparent); color: var(--danger); }
/* ============================================================================
Modals
============================================================================ */

View File

@@ -75,7 +75,7 @@ export default function AdminLayout() {
</svg>
</button>
<div style={{ flex: 1 }} />
<div className="flex-1" />
<button
onClick={toggleTheme}

View File

@@ -107,7 +107,7 @@ export default function ConfirmModal({
>
{loading ? (
<>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
Zpracování...
</>
) : (

View File

@@ -24,7 +24,7 @@ export default class ErrorBoundary extends Component {
<line x1="12" y1="17" x2="12.01" y2="17" />
</svg>
</div>
<p style={{ marginBottom: '0.5rem' }}>Něco se pokazilo při načítání stránky.</p>
<p className="mb-2">Něco se pokazilo při načítání stránky.</p>
{import.meta.env.DEV && this.state.error && (
<pre className="admin-error-stack">
{this.state.error.message}

View File

@@ -25,7 +25,7 @@ export default function OfferItemsSection({
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.2 }}
>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
<div className="flex-between mb-4">
<div>
<h3 className="admin-card-title" style={{ margin: 0 }}>Položky</h3>
{itemsError && <span className="admin-form-error">{itemsError}</span>}
@@ -49,7 +49,7 @@ export default function OfferItemsSection({
className="offers-template-menu-item"
onClick={() => addItemFromTemplate(t)}
>
<div style={{ fontWeight: 500 }}>{t.name}</div>
<div className="fw-500">{t.name}</div>
{t.default_price > 0 && (
<div style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)' }}>
{Number(t.default_price).toFixed(2)}

View File

@@ -14,7 +14,7 @@ export default function OfferScopeSection({
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.3 }}
>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
<div className="flex-between mb-4">
<h3 className="admin-card-title" style={{ margin: 0 }}>Rozsah projektu</h3>
{!readOnly && (
<div style={{ display: 'flex', gap: '0.5rem', position: 'relative' }}>

View File

@@ -31,7 +31,7 @@ function ProjectTimeStatus({ form, projectLogs }) {
function ProjectLogRow({ log, index, projectList, onUpdate, onRemove }) {
return (
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center', marginBottom: '0.5rem' }}>
<div className="flex-row gap-2 mb-2">
<select
value={log.project_id}
onChange={(e) => onUpdate(index, 'project_id', e.target.value)}

View File

@@ -44,7 +44,7 @@ export default function DashActivityFeed({ activities }) {
return (
<div className="admin-card dash-activity-card">
<div className="admin-card-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div className="admin-card-header flex-between">
<h2 className="admin-card-title">Audit log</h2>
<Link to="/audit-log" className="admin-btn admin-btn-primary admin-btn-sm">Detail &rarr;</Link>
</div>

View File

@@ -8,7 +8,7 @@ export default function DashAttendanceToday({ attendance }) {
return (
<div className="admin-card dash-attendance-card">
<div className="admin-card-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div className="admin-card-header flex-between">
<h2 className="admin-card-title">Docházka dnes</h2>
<Link to="/attendance/admin" className="admin-btn admin-btn-primary admin-btn-sm">Detail &rarr;</Link>
</div>

View File

@@ -81,7 +81,7 @@ export default function DashProfile({
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.25 }}
>
<div className="admin-card-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div className="admin-card-header flex-between">
<h2 className="admin-card-title">Váš účet</h2>
<button onClick={openEditModal} className="admin-btn admin-btn-secondary admin-btn-sm">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
@@ -113,8 +113,8 @@ export default function DashProfile({
{/* 2FA Section */}
<div style={{ borderTop: '1px solid var(--border-color)', marginTop: '1rem', paddingTop: '1rem' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<div className="flex-between">
<div className="flex-row-gap">
<div style={{
width: 36, height: 36, borderRadius: '50%',
display: 'flex', alignItems: 'center', justifyContent: 'center',
@@ -202,7 +202,7 @@ export default function DashProfile({
<div className="admin-modal-body">
{backupCodes ? (
<div>
<div className="admin-role-locked-notice" style={{ marginBottom: '1rem' }}>
<div className="admin-role-locked-notice mb-4">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
<line x1="12" y1="9" x2="12" y2="13" /><line x1="12" y1="17" x2="12.01" y2="17" />
@@ -241,7 +241,7 @@ export default function DashProfile({
</div>
)}
{totpSecret && (
<div style={{ marginBottom: '1rem' }}>
<div className="mb-4">
<label className="admin-form-label" style={{ fontSize: '0.75rem' }}>Nebo zadejte klíč ručně:</label>
<div style={{ padding: '0.5rem 0.75rem', background: 'var(--bg-secondary)', borderRadius: '0.375rem', fontFamily: 'monospace', fontSize: '0.875rem', wordBreak: 'break-all', color: 'var(--text-primary)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '0.5rem' }}>
<span>{totpSecret}</span>

View File

@@ -125,7 +125,7 @@ export default function DashSessions() {
{[0, 1, 2].map(i => (
<div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
</div>

View File

@@ -1,23 +0,0 @@
/* ============================================================================
Leave Request Status Badges
============================================================================ */
.badge-pending {
background: color-mix(in srgb, var(--warning) 15%, transparent);
color: var(--warning);
}
.badge-approved {
background: color-mix(in srgb, var(--success) 15%, transparent);
color: var(--success);
}
.badge-rejected {
background: color-mix(in srgb, var(--danger) 15%, transparent);
color: var(--danger);
}
.badge-cancelled {
background: var(--muted-light);
color: var(--muted);
}

View File

@@ -1,23 +0,0 @@
/* ============================================================================
Order Status Badges
============================================================================ */
.admin-badge-order-prijata {
background: color-mix(in srgb, var(--info) 15%, transparent);
color: var(--info);
}
.admin-badge-order-realizace {
background: color-mix(in srgb, var(--warning) 15%, transparent);
color: var(--warning);
}
.admin-badge-order-dokoncena {
background: color-mix(in srgb, var(--success) 15%, transparent);
color: var(--success);
}
.admin-badge-order-stornovana {
background: color-mix(in srgb, var(--danger) 15%, transparent);
color: var(--danger);
}

View File

@@ -509,7 +509,7 @@ export default function Attendance() {
className="admin-form-textarea"
rows={3}
/>
<div style={{ marginTop: '0.5rem' }}>
<div className="mt-2">
<button
onClick={handleSaveNotes}
className="admin-btn admin-btn-secondary admin-btn-sm"
@@ -543,7 +543,7 @@ export default function Attendance() {
{/* Completed Today */}
{completedToday.length > 0 && (
<div className="admin-card" style={{ marginTop: '1.5rem' }}>
<div className="admin-card mt-6">
<div className="admin-card-header">
<h2 className="admin-card-title">Dnešní dokončené směny</h2>
</div>

View File

@@ -147,8 +147,7 @@ export default function AttendanceAdmin() {
{/* Filters */}
<motion.div
className="admin-card"
style={{ marginBottom: '1.5rem' }}
className="admin-card mb-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.1 }}
@@ -181,8 +180,7 @@ export default function AttendanceAdmin() {
{/* User Totals */}
{Object.keys(data.user_totals).length > 0 && (
<motion.div
className="admin-grid admin-grid-3"
style={{ marginBottom: '1.5rem' }}
className="admin-grid admin-grid-3 mb-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.15 }}
@@ -190,7 +188,7 @@ export default function AttendanceAdmin() {
{Object.entries(data.user_totals).map(([uid, userData]) => (
<div key={uid} className="admin-card">
<div className="admin-card-body">
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.5rem' }}>
<div className="flex-row gap-2 mb-2">
<span style={{ fontWeight: 600 }}>{userData.name}</span>
<span className={`attendance-working-badge ${userData.working ? 'working' : 'finished'}`}>
{userData.working ? '✓' : '✗'}
@@ -213,7 +211,7 @@ export default function AttendanceAdmin() {
)}
</div>
{userData.fund !== null && (
<div style={{ marginTop: '0.5rem' }}>
<div className="mt-2">
<div className="text-secondary" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', fontSize: '0.8rem' }}>
<span>Fond: {userData.worked_hours}h / {userData.fund}h</span>
{userData.overtime > 0 && (

View File

@@ -297,7 +297,7 @@ export default function AttendanceBalances() {
const yf = getYearFundTotals(userId)
return (
<tr key={userId}>
<td style={{ fontWeight: 500 }}>{balance.name}</td>
<td className="fw-500">{balance.name}</td>
<td className="admin-mono">{balance.vacation_total}</td>
<td className="admin-mono">{balance.vacation_used.toFixed(1)}</td>
<td className="admin-mono">
@@ -355,9 +355,9 @@ export default function AttendanceBalances() {
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.2 }}
style={{ marginTop: '1.5rem' }}
className="mt-6"
>
<h2 className="admin-page-title" style={{ fontSize: '1.25rem', marginBottom: '1rem' }}>
<h2 className="admin-page-title mb-4" style={{ fontSize: '1.25rem' }}>
Měsíční přehled fondu {year}
</h2>
<div className="admin-grid admin-grid-3">
@@ -437,7 +437,7 @@ export default function AttendanceBalances() {
)}
{fundLoading && (
<div style={{ marginTop: '1.5rem' }}>
<div className="mt-6">
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
{[0, 1, 2].map(i => (
<div key={i} className="admin-skeleton-row">
@@ -456,9 +456,9 @@ export default function AttendanceBalances() {
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.3 }}
style={{ marginTop: '1.5rem' }}
className="mt-6"
>
<h2 className="admin-page-title" style={{ fontSize: '1.25rem', marginBottom: '1rem' }}>
<h2 className="admin-page-title mb-4" style={{ fontSize: '1.25rem' }}>
Měsíční přehled projektů {year}
</h2>
<div className="admin-grid admin-grid-3">
@@ -555,7 +555,7 @@ export default function AttendanceBalances() {
)}
{projectLoading && (
<div style={{ marginTop: '1.5rem' }}>
<div className="mt-6">
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
{[0, 1, 2].map(i => (
<div key={i} className="admin-skeleton-row">

View File

@@ -235,8 +235,7 @@ export default function AttendanceHistory() {
{/* Filters */}
<motion.div
className="admin-card"
style={{ marginBottom: '1.5rem' }}
className="admin-card mb-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.1 }}
@@ -256,8 +255,7 @@ export default function AttendanceHistory() {
{/* Monthly Fund Card */}
<motion.div
className="admin-card"
style={{ marginBottom: '1.5rem' }}
className="admin-card mb-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.15 }}
@@ -267,7 +265,7 @@ export default function AttendanceHistory() {
<div className="admin-skeleton" style={{ gap: '0.5rem' }}>
<div className="admin-skeleton-row" style={{ gap: '1rem' }}>
<div className="admin-skeleton-line" style={{ width: '48px', height: '48px', borderRadius: '12px', flexShrink: 0 }} />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-full" style={{ height: '6px', borderRadius: '3px' }} />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px', marginTop: '0.5rem' }} />

View File

@@ -254,8 +254,7 @@ export default function AttendanceLocation() {
href={`https://www.google.com/maps?q=${record.arrival_lat},${record.arrival_lng}`}
target="_blank"
rel="noopener noreferrer"
className="admin-btn admin-btn-secondary admin-btn-sm"
style={{ marginTop: '0.5rem' }}
className="admin-btn admin-btn-secondary admin-btn-sm mt-2"
>
Otevřít v Google Maps
</a>
@@ -287,8 +286,7 @@ export default function AttendanceLocation() {
href={`https://www.google.com/maps?q=${record.departure_lat},${record.departure_lng}`}
target="_blank"
rel="noopener noreferrer"
className="admin-btn admin-btn-secondary admin-btn-sm"
style={{ marginTop: '0.5rem' }}
className="admin-btn admin-btn-secondary admin-btn-sm mt-2"
>
Otevřít v Google Maps
</a>

View File

@@ -195,7 +195,7 @@ export default function AuditLog() {
<div className="admin-skeleton-line" style={{ width: '80px' }} />
<div className="admin-skeleton-line" style={{ width: '70px', borderRadius: '10px' }} />
<div className="admin-skeleton-line" style={{ width: '80px' }} />
<div className="admin-skeleton-line" style={{ flex: 1 }} />
<div className="admin-skeleton-line flex-1" />
<div className="admin-skeleton-line" style={{ width: '90px' }} />
</div>
))}
@@ -290,11 +290,10 @@ export default function AuditLog() {
)}
<motion.div
className="admin-card"
className="admin-card mb-4"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.1 }}
style={{ marginBottom: '1rem' }}
>
<div className="admin-card-body">
<div className="admin-form-row admin-form-row-5">
@@ -399,7 +398,7 @@ export default function AuditLog() {
{!loading && logs.map((log) => (
<tr key={log.id}>
<td className="admin-mono">{formatDatetime(log.created_at)}</td>
<td style={{ fontWeight: 500 }}>{log.username || '-'}</td>
<td className="fw-500">{log.username || '-'}</td>
<td>
<span className={`admin-badge ${ACTION_BADGE_CLASS[log.action] || 'admin-badge-secondary'}`}>
{ACTION_LABELS[log.action] || log.action}

View File

@@ -323,7 +323,7 @@ export default function CompanySettings() {
function renderBankButtonContent() {
if (bankSaving) {
return <><div className="admin-spinner" style={{ width: 14, height: 14, borderWidth: 2 }} />Ukládání...</>
return <><div className="admin-spinner admin-spinner-sm" />Ukládání...</>
}
if (editingBank !== null) return 'Uložit změny'
return (
@@ -351,7 +351,7 @@ export default function CompanySettings() {
<button onClick={handleSave} className="admin-btn admin-btn-primary" disabled={saving}>
{saving ? (
<>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
Ukládání...
</>
) : 'Uložit nastavení'}
@@ -569,7 +569,7 @@ export default function CompanySettings() {
<td className="admin-mono">{acc.iban}</td>
<td className="admin-mono">{acc.bic}</td>
<td>{acc.currency}</td>
<td style={{ textAlign: 'center' }}>
<td className="text-center">
{acc.is_default ? (
<span className="text-accent fw-600"></span>
) : ''}
@@ -780,7 +780,7 @@ export default function CompanySettings() {
<label className="admin-btn admin-btn-secondary" style={{ cursor: 'pointer' }}>
{uploadingLogo ? (
<>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
Nahrávání...
</>
) : (

View File

@@ -226,7 +226,7 @@ export default function Dashboard() {
style={{ border: '2px solid var(--danger)', background: 'var(--danger-light)' }}
>
<div className="admin-card-body" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '1rem', flexWrap: 'wrap' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<div className="flex-row-gap">
<div style={{
width: 40, height: 40, borderRadius: '50%',
display: 'flex', alignItems: 'center', justifyContent: 'center',
@@ -306,7 +306,7 @@ export default function Dashboard() {
<div className="dash-right-col">
{dashData?.projects && (
<div className="admin-card">
<div className="admin-card-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div className="admin-card-header flex-between">
<h2 className="admin-card-title">Aktivní projekty</h2>
<Link to="/projects" className="admin-btn admin-btn-primary admin-btn-sm">Vše &rarr;</Link>
</div>
@@ -326,7 +326,7 @@ export default function Dashboard() {
{dashData?.offers && (
<div className="admin-card">
<div className="admin-card-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div className="admin-card-header flex-between">
<h2 className="admin-card-title">Nabídky</h2>
<Link to="/offers" className="admin-btn admin-btn-primary admin-btn-sm">Zobrazit &rarr;</Link>
</div>

View File

@@ -420,7 +420,7 @@ export default function InvoiceCreate() {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<div className="flex-row gap-4">
<Link to="/invoices" className="admin-btn-icon" title="Zpět" aria-label="Zpět">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M19 12H5M12 19l-7-7 7-7" />
@@ -446,7 +446,7 @@ export default function InvoiceCreate() {
<button onClick={handleSubmit} className="admin-btn admin-btn-primary" disabled={saving}>
{saving ? (
<>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
Ukládání...
</>
) : 'Uložit'}
@@ -602,7 +602,7 @@ export default function InvoiceCreate() {
</select>
</FormField>
<FormField label="DPH">
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<div className="flex-row-gap">
<label className="admin-form-checkbox" style={{ whiteSpace: 'nowrap' }}>
<input
type="checkbox"
@@ -642,7 +642,7 @@ export default function InvoiceCreate() {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.2 }}
>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
<div className="flex-between mb-4">
<div>
<h3 className="admin-card-title" style={{ margin: 0 }}>Položky</h3>
{errors.items && <span className="admin-form-error">{errors.items}</span>}
@@ -684,15 +684,14 @@ export default function InvoiceCreate() {
<td style={{ width: '2rem' }}>
<DragHandle listeners={listeners} attributes={attributes} />
</td>
<td className="text-tertiary" style={{ textAlign: 'center', fontWeight: 500 }}>{index + 1}</td>
<td className="text-tertiary text-center fw-500">{index + 1}</td>
<td>
<input
type="text"
value={item.description}
onChange={(e) => updateItem(index, 'description', e.target.value)}
className="admin-form-input"
className="admin-form-input fw-500"
placeholder="Popis položky..."
style={{ fontWeight: 500 }}
/>
</td>
<td>

View File

@@ -254,11 +254,11 @@ export default function InvoiceDetail() {
return (
<div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}>
<div className="admin-skeleton-row" style={{ justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<div className="flex-row-gap">
<div className="admin-skeleton-line" style={{ width: '32px', height: '32px', borderRadius: '8px' }} />
<div className="admin-skeleton-line h-8" style={{ width: '200px' }} />
</div>
<div className="admin-skeleton-row" style={{ gap: '0.5rem' }}>
<div className="admin-skeleton-row gap-2">
<div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
<div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
</div>
@@ -277,9 +277,9 @@ export default function InvoiceDetail() {
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
{[0, 1, 2].map(i => (
<div key={i} className="admin-skeleton-row">
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-full" /></div>
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-3/4" /></div>
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-1/2" /></div>
<div className="flex-1"><div className="admin-skeleton-line w-full" /></div>
<div className="flex-1"><div className="admin-skeleton-line w-3/4" /></div>
<div className="flex-1"><div className="admin-skeleton-line w-1/2" /></div>
</div>
))}
</div>
@@ -302,14 +302,14 @@ export default function InvoiceDetail() {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<div className="flex-row gap-4">
<Link to="/invoices" className="admin-btn-icon" title="Zpět" aria-label="Zpět">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
</Link>
<div>
<h1 className="admin-page-title" style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<h1 className="admin-page-title flex-row-gap">
Faktura {invoice.invoice_number}
<span className={`admin-badge ${STATUS_CLASSES[invoice.status] || ''}`}>
{STATUS_LABELS[invoice.status] || invoice.status}
@@ -326,7 +326,7 @@ export default function InvoiceDetail() {
>
{pdfLoading ? (
<>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
PDF...
</>
) : (
@@ -376,9 +376,9 @@ export default function InvoiceDetail() {
>
<h3 className="admin-card-title">Informace</h3>
<div className="admin-form">
<div className="offers-form-row-3" style={{ marginBottom: '0.5rem' }}>
<div className="offers-form-row-3 mb-2">
<FormField label="Zákazník">
<div style={{ fontWeight: 500 }}>{invoice.customer_name || '—'}</div>
<div className="fw-500">{invoice.customer_name || '—'}</div>
{invoice.customer && (
<div className="text-tertiary" style={{ fontSize: '0.8rem', marginTop: '0.2rem' }}>
{invoice.customer.company_id && `IČ: ${invoice.customer.company_id}`}
@@ -399,7 +399,7 @@ export default function InvoiceDetail() {
<div>{invoice.currency}</div>
</FormField>
</div>
<div className="offers-form-row-3" style={{ marginBottom: '0.5rem' }}>
<div className="offers-form-row-3 mb-2">
<FormField label="Datum vystavení">
<div>{formatDate(invoice.issue_date)}</div>
</FormField>
@@ -424,7 +424,7 @@ export default function InvoiceDetail() {
</FormField>
</div>
{invoice.paid_date && (
<div className="admin-form-row" style={{ marginTop: '0.5rem' }}>
<div className="admin-form-row mt-2">
<FormField label="Datum úhrady">
<div style={{ color: 'var(--success)', fontWeight: 500 }}>{formatDate(invoice.paid_date)}</div>
</FormField>
@@ -440,11 +440,11 @@ export default function InvoiceDetail() {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.2 }}
>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
<div className="flex-between mb-4">
<h3 className="admin-card-title" style={{ margin: 0 }}>Položky</h3>
{isDraft && hasPermission('invoices.edit') && (
editingItems ? (
<div style={{ display: 'flex', gap: '0.5rem' }}>
<div className="flex-row gap-2">
<button type="button" onClick={addEditItem} className="admin-btn admin-btn-secondary admin-btn-sm">
+ Přidat položku
</button>
@@ -487,9 +487,8 @@ export default function InvoiceDetail() {
type="text"
value={item.description}
onChange={(e) => updateEditItem(index, 'description', e.target.value)}
className="admin-form-input"
className="admin-form-input fw-500"
placeholder="Popis položky..."
style={{ fontWeight: 500 }}
/>
</td>
<td>
@@ -578,10 +577,10 @@ export default function InvoiceDetail() {
return (
<tr key={item.id || index}>
<td className="text-tertiary" style={{ textAlign: 'center', fontWeight: 500 }}>{index + 1}</td>
<td style={{ fontWeight: 500 }}>{item.description || '—'}</td>
<td className="fw-500">{item.description || '—'}</td>
<td style={{ textAlign: 'center' }}>{item.quantity} {item.unit && <span className="text-tertiary">{item.unit}</span>}</td>
<td style={{ textAlign: 'center' }}>{item.unit || '—'}</td>
<td className="admin-mono" style={{ textAlign: 'right' }}>{formatCurrency(item.unit_price, invoice.currency)}</td>
<td className="admin-mono text-right">{formatCurrency(item.unit_price, invoice.currency)}</td>
<td style={{ textAlign: 'center' }}>{Number(invoice.apply_vat) ? Number(item.vat_rate) : 0}%</td>
<td className="admin-mono" style={{ textAlign: 'right', fontWeight: 600 }}>{formatCurrency(lineSubtotal + lineVat, invoice.currency)}</td>
</tr>
@@ -644,7 +643,7 @@ export default function InvoiceDetail() {
/>
</Suspense>
{hasPermission('invoices.edit') && (
<div style={{ marginTop: '0.5rem' }}>
<div className="mt-2">
<button
onClick={handleSaveNotes}
className="admin-btn admin-btn-secondary admin-btn-sm"

View File

@@ -313,7 +313,7 @@ export default function Invoices() {
</button>
</div>
<div className="offers-tabs" style={{ marginBottom: '1rem', justifyContent: 'center' }}>
<div className="offers-tabs mb-4" style={{ justifyContent: 'center' }}>
<button className={`offers-tab ${activeTab === 'issued' ? 'active' : ''}`} onClick={() => setActiveTab('issued')}>
Vydané
</button>
@@ -427,7 +427,7 @@ export default function Invoices() {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.2 }}
>
<div className="offers-tabs" style={{ marginBottom: '1.5rem' }}>
<div className="offers-tabs mb-6">
{STATUS_FILTERS.map(f => (
<button
key={f.value}
@@ -448,7 +448,7 @@ export default function Invoices() {
transition={{ duration: 0.4, delay: 0.25 }}
>
<div className="admin-card-body">
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}>
<div className="admin-search-bar mb-4">
<input
type="text"
value={search}
@@ -494,7 +494,7 @@ export default function Invoices() {
<th style={{ cursor: 'pointer' }} onClick={() => handleSort('due_date')}>
Splatnost <SortIcon column="due_date" sort={activeSort} order={order} />
</th>
<th style={{ textAlign: 'right' }}>Celkem</th>
<th className="text-right">Celkem</th>
<th>Akce</th>
</tr>
</thead>

View File

@@ -179,8 +179,8 @@ export default function LeaveApproval() {
{[0, 1, 2, 3, 4].map(i => (
<div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="flex-1">
<div className="admin-skeleton-line w-1/3 mb-2" />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
<div className="admin-skeleton-line w-1/4" />
@@ -217,7 +217,7 @@ export default function LeaveApproval() {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.1 }}
>
<div className="offers-tabs" style={{ marginBottom: '1.5rem' }}>
<div className="offers-tabs mb-6">
<button
className={`offers-tab ${activeTab === 'pending' ? 'active' : ''}`}
onClick={() => setActiveTab('pending')}
@@ -249,7 +249,7 @@ export default function LeaveApproval() {
<div className="admin-card">
<div className="admin-card-body">
<div className="admin-empty-state">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="text-muted" style={{ marginBottom: '1rem' }}>
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="text-muted mb-4">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
<polyline points="22 4 12 14.01 9 11.01" />
</svg>
@@ -263,8 +263,8 @@ export default function LeaveApproval() {
<div key={req.id} className="admin-card">
<div className="admin-card-body" style={{ padding: '1.25rem' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', flexWrap: 'wrap', gap: '1rem' }}>
<div style={{ flex: 1 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '0.5rem' }}>
<div className="flex-1">
<div className="flex-row-gap mb-2">
<strong style={{ fontSize: '1rem' }}>{req.employee_name}</strong>
<span className={`attendance-leave-badge ${leaveTypeClasses[req.leave_type] || ''}`}>
{leaveTypeLabels[req.leave_type] || req.leave_type}
@@ -413,7 +413,7 @@ export default function LeaveApproval() {
</div>
<div className="admin-modal-body">
{rejectModal.request && (
<p className="text-secondary" style={{ marginBottom: '1rem' }}>
<p className="text-secondary mb-4">
{rejectModal.request.employee_name} {leaveTypeLabels[rejectModal.request.leave_type]},{' '}
{formatDate(rejectModal.request.date_from)} {formatDate(rejectModal.request.date_to)} ({rejectModal.request.total_days} dnů)
</p>

View File

@@ -106,40 +106,40 @@ export default function LeaveRequests() {
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="flex-1">
<div className="admin-skeleton-line w-1/3 mb-2" />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
<div className="admin-skeleton-line w-1/4" />
</div>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
<div className="flex-1">
<div className="admin-skeleton-line w-1/2 mb-2" />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
</div>
<div className="admin-skeleton-line w-1/4" />
</div>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="admin-skeleton-line w-3/4" style={{ marginBottom: '0.5rem' }} />
<div className="flex-1">
<div className="admin-skeleton-line w-3/4 mb-2" />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
<div className="admin-skeleton-line w-1/4" />
</div>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
<div className="flex-1">
<div className="admin-skeleton-line w-1/2 mb-2" />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
</div>
<div className="admin-skeleton-line w-1/4" />
</div>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="flex-1">
<div className="admin-skeleton-line w-1/3 mb-2" />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
<div className="admin-skeleton-line w-1/4" />

View File

@@ -163,11 +163,11 @@ export default function OfferDetail() {
return (
<div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}>
<div className="admin-skeleton-row" style={{ justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<div className="flex-row-gap">
<div className="admin-skeleton-line" style={{ width: '32px', height: '32px', borderRadius: '8px' }} />
<div className="admin-skeleton-line h-8" style={{ width: '200px' }} />
</div>
<div className="admin-skeleton-row" style={{ gap: '0.5rem' }}>
<div className="admin-skeleton-row gap-2">
<div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
<div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
</div>
@@ -186,9 +186,9 @@ export default function OfferDetail() {
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
{[0, 1, 2].map(i => (
<div key={i} className="admin-skeleton-row">
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-full" /></div>
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-3/4" /></div>
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-1/2" /></div>
<div className="flex-1"><div className="admin-skeleton-line w-full" /></div>
<div className="flex-1"><div className="admin-skeleton-line w-3/4" /></div>
<div className="flex-1"><div className="admin-skeleton-line w-1/2" /></div>
</div>
))}
</div>
@@ -216,7 +216,7 @@ export default function OfferDetail() {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<div className="flex-row gap-4">
<Link to="/offers" className="admin-btn-icon" title="Zpět" aria-label="Zpět">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M19 12H5M12 19l-7-7 7-7" />
@@ -246,7 +246,7 @@ export default function OfferDetail() {
<button onClick={handlePdf} className="admin-btn admin-btn-secondary" disabled={pdfLoading}>
{pdfLoading ? (
<>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
PDF...
</>
) : (
@@ -293,7 +293,7 @@ export default function OfferDetail() {
<button onClick={handleSave} className="admin-btn admin-btn-primary" disabled={saving}>
{saving ? (
<>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
Ukládání...
</>
) : 'Uložit'}
@@ -410,14 +410,13 @@ export default function OfferDetail() {
<div className="offers-form-row-3">
<FormField label="Sazba DPH (%)">
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<div className="flex-row-gap">
<input
type="number"
value={form.vat_rate}
onChange={(e) => updateForm('vat_rate', parseFloat(e.target.value) || 0)}
className="admin-form-input"
className="admin-form-input flex-1"
step="0.1"
style={{ flex: 1 }}
readOnly={isInvalidated}
/>
<label className="admin-form-checkbox" style={{ whiteSpace: 'nowrap' }}>
@@ -514,7 +513,7 @@ export default function OfferDetail() {
</FormField>
<FormField label="Příloha (PDF)">
{orderAttachment ? (
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<div className="flex-row gap-2">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent-color)" strokeWidth="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" />

View File

@@ -211,7 +211,7 @@ export default function Offers() {
{[0, 1, 2, 3, 4].map(i => (
<div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
@@ -267,7 +267,7 @@ export default function Offers() {
transition={{ duration: 0.4, delay: 0.1 }}
>
<div className="admin-card-body">
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}>
<div className="admin-search-bar mb-4">
<input
type="text"
value={search}
@@ -315,7 +315,7 @@ export default function Offers() {
<th style={{ cursor: 'pointer' }} onClick={() => handleSort('currency')}>
Měna <SortIcon column="currency" sort={activeSort} order={order} />
</th>
<th style={{ textAlign: 'right' }}>Celkem</th>
<th className="text-right">Celkem</th>
<th>Akce</th>
</tr>
</thead>
@@ -395,7 +395,7 @@ export default function Offers() {
{q.currency}
</span>
</td>
<td className="admin-mono" style={{ textAlign: 'right', fontWeight: 500 }}>
<td className="admin-mono text-right fw-500">
{formatCurrency(q.total, q.currency)}
</td>
<td>
@@ -578,7 +578,7 @@ export default function Offers() {
</FormField>
<FormField label="Příloha (PDF)">
{orderAttachment ? (
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<div className="flex-row gap-2">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent-color)" strokeWidth="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" />

View File

@@ -244,7 +244,7 @@ export default function OffersCustomers() {
{[0, 1, 2, 3, 4].map(i => (
<div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
@@ -289,7 +289,7 @@ export default function OffersCustomers() {
transition={{ duration: 0.4, delay: 0.1 }}
>
<div className="admin-card-body">
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}>
<div className="admin-search-bar mb-4">
<input
type="text"
value={search}
@@ -606,7 +606,7 @@ export default function OffersCustomers() {
<button type="button" onClick={handleSubmit} className="admin-btn admin-btn-primary" disabled={saving}>
{saving && (
<>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
Ukládání...
</>
)}

View File

@@ -151,7 +151,7 @@ function ItemTemplatesTab() {
{[0, 1, 2, 3, 4].map(i => (
<div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
@@ -171,7 +171,7 @@ function ItemTemplatesTab() {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.1 }}
>
<div className="admin-card-header" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div className="admin-card-header flex-between">
<h3 className="admin-card-title">Šablony položek ({templates.length})</h3>
<button onClick={openCreate} className="admin-btn admin-btn-primary admin-btn-sm">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
@@ -199,7 +199,7 @@ function ItemTemplatesTab() {
<tbody>
{templates.map((t) => (
<tr key={t.id}>
<td style={{ fontWeight: 500 }}>{t.name}</td>
<td className="fw-500">{t.name}</td>
<td style={{ color: 'var(--text-secondary)' }}>{t.description || '—'}</td>
<td>{Number(t.default_price).toFixed(2)}</td>
<td style={{ color: 'var(--text-secondary)' }}>{t.category || '—'}</td>
@@ -258,7 +258,7 @@ function ItemTemplatesTab() {
<div className="admin-modal-footer">
<button type="button" onClick={() => setShowModal(false)} className="admin-btn admin-btn-secondary" disabled={saving}>Zrušit</button>
<button type="button" onClick={handleSubmit} className="admin-btn admin-btn-primary" disabled={saving}>
{saving && (<><div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />Ukládání...</>)}
{saving && (<><div className="admin-spinner admin-spinner-sm" />Ukládání...</>)}
{!saving && (editingTemplate ? 'Uložit' : 'Vytvořit')}
</button>
</div>
@@ -424,7 +424,7 @@ function ScopeTemplatesTab() {
{[0, 1, 2, 3, 4].map(i => (
<div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
@@ -444,7 +444,7 @@ function ScopeTemplatesTab() {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.1 }}
>
<div className="admin-card-header" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div className="admin-card-header flex-between">
<h3 className="admin-card-title">Šablony rozsahu ({templates.length})</h3>
<button onClick={openCreate} className="admin-btn admin-btn-primary admin-btn-sm">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
@@ -469,7 +469,7 @@ function ScopeTemplatesTab() {
<tbody>
{templates.map((t) => (
<tr key={t.id}>
<td style={{ fontWeight: 500 }}>{t.name}</td>
<td className="fw-500">{t.name}</td>
<td>
<div className="admin-table-actions">
<button onClick={() => openEdit(t)} className="admin-btn-icon" title="Upravit" aria-label="Upravit">
@@ -511,7 +511,7 @@ function ScopeTemplatesTab() {
</FormField>
<div className="admin-form-group">
<label className="admin-form-label" style={{ marginBottom: '0.5rem' }}>Sekce</label>
<label className="admin-form-label mb-2">Sekce</label>
<div className="offers-scope-list">
{form.sections.map((section, index) => (
<div key={section._key} className="offers-scope-section">
@@ -564,7 +564,7 @@ function ScopeTemplatesTab() {
<div className="admin-modal-footer">
<button type="button" onClick={() => setShowModal(false)} className="admin-btn admin-btn-secondary" disabled={saving}>Zrušit</button>
<button type="button" onClick={handleSubmit} className="admin-btn admin-btn-primary" disabled={saving}>
{saving && (<><div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />Ukládání...</>)}
{saving && (<><div className="admin-spinner admin-spinner-sm" />Ukládání...</>)}
{!saving && (editingTemplate ? 'Uložit' : 'Vytvořit')}
</button>
</div>

View File

@@ -217,11 +217,11 @@ export default function OrderDetail() {
return (
<div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}>
<div className="admin-skeleton-row" style={{ justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<div className="flex-row-gap">
<div className="admin-skeleton-line" style={{ width: '32px', height: '32px', borderRadius: '8px' }} />
<div className="admin-skeleton-line h-8" style={{ width: '200px' }} />
</div>
<div className="admin-skeleton-row" style={{ gap: '0.5rem' }}>
<div className="admin-skeleton-row gap-2">
<div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
<div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
</div>
@@ -240,9 +240,9 @@ export default function OrderDetail() {
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
{[0, 1, 2].map(i => (
<div key={i} className="admin-skeleton-row">
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-full" /></div>
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-3/4" /></div>
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-1/2" /></div>
<div className="flex-1"><div className="admin-skeleton-line w-full" /></div>
<div className="flex-1"><div className="admin-skeleton-line w-3/4" /></div>
<div className="flex-1"><div className="admin-skeleton-line w-1/2" /></div>
</div>
))}
</div>
@@ -262,14 +262,14 @@ export default function OrderDetail() {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<div className="flex-row gap-4">
<Link to="/orders" className="admin-btn-icon" title="Zpět" aria-label="Zpět">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
</Link>
<div>
<h1 className="admin-page-title" style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<h1 className="admin-page-title flex-row-gap">
{editingNumber ? (
<span style={{ display: 'inline-flex', alignItems: 'center', gap: '0.5rem' }}>
Objednávka
@@ -345,7 +345,7 @@ export default function OrderDetail() {
disabled={statusChanging === status}
>
{statusChanging === status ? (
<div className="admin-spinner" style={{ width: 14, height: 14, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
) : (
TRANSITION_LABELS[status] || status
)}
@@ -372,7 +372,7 @@ export default function OrderDetail() {
>
<div className="admin-card-body">
<h3 className="admin-card-title">Informace</h3>
<div className="admin-form-row" style={{ marginBottom: '0.5rem' }}>
<div className="admin-form-row mb-2">
<FormField label="Nabídka">
<div>
<Link to={`/offers/${order.quotation_id}`} className="link-accent">
@@ -393,9 +393,9 @@ export default function OrderDetail() {
</div>
</FormField>
</div>
<div className="admin-form-row admin-form-row-3" style={{ marginBottom: '0.5rem' }}>
<div className="admin-form-row admin-form-row-3 mb-2">
<FormField label="Zákazník">
<div style={{ fontWeight: 500 }}>{order.customer_name || '—'}</div>
<div className="fw-500">{order.customer_name || '—'}</div>
</FormField>
<FormField label="Číslo obj. zákazníka">
<div>{order.customer_order_number || '—'}</div>
@@ -404,7 +404,7 @@ export default function OrderDetail() {
<div>{order.currency}</div>
</FormField>
</div>
<div className="admin-form-row admin-form-row-3" style={{ marginBottom: '0.5rem' }}>
<div className="admin-form-row admin-form-row-3 mb-2">
<FormField label="Datum vytvoření">
<div>{formatDate(order.created_at)}</div>
</FormField>
@@ -418,7 +418,7 @@ export default function OrderDetail() {
disabled={attachmentLoading}
>
{attachmentLoading ? (
<div className="admin-spinner" style={{ width: 14, height: 14, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
) : (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
@@ -464,7 +464,7 @@ export default function OrderDetail() {
<tr key={item.id || index}>
<td style={{ color: 'var(--text-tertiary)', textAlign: 'center', fontWeight: 500 }}>{index + 1}</td>
<td>
<div style={{ fontWeight: 500 }}>{item.description || '—'}</div>
<div className="fw-500">{item.description || '—'}</div>
{item.item_description && (
<div style={{ fontSize: '0.8rem', color: 'var(--text-tertiary)', marginTop: '0.25rem' }}>{item.item_description}</div>
)}
@@ -561,7 +561,7 @@ export default function OrderDetail() {
/>
</FormField>
{hasPermission('orders.edit') && (
<div style={{ marginTop: '0.5rem' }}>
<div className="mt-2">
<button
onClick={handleSaveNotes}
className="admin-btn admin-btn-secondary admin-btn-sm"

View File

@@ -83,7 +83,7 @@ export default function Orders() {
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
@@ -91,7 +91,7 @@ export default function Orders() {
</div>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
</div>
@@ -99,7 +99,7 @@ export default function Orders() {
</div>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-3/4" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
@@ -107,7 +107,7 @@ export default function Orders() {
</div>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
</div>
@@ -115,7 +115,7 @@ export default function Orders() {
</div>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
@@ -151,7 +151,7 @@ export default function Orders() {
transition={{ duration: 0.4, delay: 0.1 }}
>
<div className="admin-card-body">
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}>
<div className="admin-search-bar mb-4">
<input
type="text"
value={search}
@@ -191,7 +191,7 @@ export default function Orders() {
<th style={{ cursor: 'pointer' }} onClick={() => handleSort('created_at')}>
Datum <SortIcon column="created_at" sort={activeSort} order={order} />
</th>
<th style={{ textAlign: 'right' }}>Celkem</th>
<th className="text-right">Celkem</th>
<th>Akce</th>
</tr>
</thead>
@@ -217,7 +217,7 @@ export default function Orders() {
<td className="admin-mono">
{formatDate(o.created_at)}
</td>
<td className="admin-mono" style={{ textAlign: 'right', fontWeight: 500 }}>
<td className="admin-mono text-right fw-500">
{formatCurrency(o.total, o.currency)}
</td>
<td>

View File

@@ -158,7 +158,7 @@ export default function ProjectCreate() {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<div className="flex-row gap-4">
<Link to="/projects" className="admin-btn-icon" title="Zpět" aria-label="Zpět">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M19 12H5M12 19l-7-7 7-7" />

View File

@@ -210,7 +210,7 @@ export default function ProjectDetail() {
return (
<div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}>
<div className="admin-skeleton-row" style={{ justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<div className="flex-row-gap">
<div className="admin-skeleton-line" style={{ width: '32px', height: '32px', borderRadius: '8px' }} />
<div className="admin-skeleton-line h-8" style={{ width: '200px' }} />
</div>
@@ -273,7 +273,7 @@ export default function ProjectDetail() {
<button onClick={handleSave} className="admin-btn admin-btn-primary" disabled={saving}>
{saving ? (
<>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
Ukládání...
</>
) : 'Uložit'}
@@ -380,7 +380,7 @@ export default function ProjectDetail() {
<h3 className="admin-card-title">Poznámky</h3>
{/* Add note */}
<div style={{ marginBottom: '1rem' }}>
<div className="mb-4">
<textarea
value={newNote}
onChange={(e) => setNewNote(e.target.value)}
@@ -394,14 +394,14 @@ export default function ProjectDetail() {
}
}}
/>
<div style={{ marginTop: '0.5rem' }}>
<div className="mt-2">
<button
onClick={handleAddNote}
className="admin-btn admin-btn-secondary admin-btn-sm"
disabled={addingNote || !newNote.trim()}
>
{addingNote ? (
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
) : (
'Přidat poznámku'
)}
@@ -452,7 +452,7 @@ export default function ProjectDetail() {
}}
>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: '0.5rem' }}>
<div style={{ flex: 1 }}>
<div className="flex-1">
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.25rem' }}>
<span style={{ fontWeight: 600, fontSize: '0.85rem' }}>
{note.user_name}

View File

@@ -78,7 +78,7 @@ export default function Projects() {
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
@@ -86,7 +86,7 @@ export default function Projects() {
</div>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
</div>
@@ -94,7 +94,7 @@ export default function Projects() {
</div>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-3/4" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
@@ -102,7 +102,7 @@ export default function Projects() {
</div>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
</div>
@@ -110,7 +110,7 @@ export default function Projects() {
</div>
<div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="flex-1">
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
@@ -155,7 +155,7 @@ export default function Projects() {
transition={{ duration: 0.4, delay: 0.1 }}
>
<div className="admin-card-body">
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}>
<div className="admin-search-bar mb-4">
<input
type="text"
value={search}
@@ -210,7 +210,7 @@ export default function Projects() {
{p.project_number}
</Link>
</td>
<td style={{ fontWeight: 500 }}>{p.name || '—'}</td>
<td className="fw-500">{p.name || '—'}</td>
<td>{p.customer_name || '—'}</td>
<td>
<span className={`admin-badge ${STATUS_CLASSES[p.status] || ''}`}>
@@ -242,7 +242,7 @@ export default function Projects() {
disabled={deletingId === p.id}
>
{deletingId === p.id ? (
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
<div className="admin-spinner admin-spinner-sm" />
) : (
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<polyline points="3 6 5 6 21 6" />

View File

@@ -369,7 +369,7 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
const renderKpi = () => {
if (!hasLoadedOnce.current && statsLoading) {
return (
<div className="dash-kpi-grid dash-kpi-4" style={{ marginBottom: '1.5rem' }}>
<div className="dash-kpi-grid dash-kpi-4 mb-6">
{[0, 1, 2, 3].map(i => (
<div key={i} className="admin-stat-card">
<div className="admin-skeleton-line" style={{ width: '60%', height: '11px', marginBottom: '0.5rem' }} />
@@ -456,7 +456,7 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
transition={{ duration: 0.4, delay: 0.15 }}
>
<div className="admin-card-body">
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}>
<div className="admin-search-bar mb-4">
<input
type="text"
value={search}
@@ -549,7 +549,7 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
</td>
<td className="admin-mono">{formatDate(inv.issue_date)}</td>
<td className="admin-mono">{formatDate(inv.due_date)}</td>
<td className="admin-mono" style={{ textAlign: 'right', fontWeight: 500 }}>
<td className="admin-mono text-right fw-500">
{formatCurrency(inv.amount, inv.currency)}
</td>
<td>
@@ -615,7 +615,7 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
<h2 className="admin-modal-title">Nahrát přijaté faktury</h2>
</div>
<div className="admin-modal-body">
<div style={{ marginBottom: '1rem' }}>
<div className="mb-4">
<input
ref={fileInputRef}
type="file"

View File

@@ -276,8 +276,8 @@ export default function Settings() {
{[0, 1, 2, 3, 4].map(i => (
<div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="flex-1">
<div className="admin-skeleton-line w-1/3 mb-2" />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
<div className="admin-skeleton-line w-1/4" />
@@ -306,7 +306,7 @@ export default function Settings() {
function renderRoleButtonContent() {
if (saving) {
return <><div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />Ukládání...</>
return <><div className="admin-spinner admin-spinner-sm" />Ukládání...</>
}
return editingRole ? 'Uložit změny' : 'Vytvořit roli'
}
@@ -347,7 +347,7 @@ export default function Settings() {
</div>
<div className="admin-card-body">
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '1rem' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<div className="flex-row-gap">
<div style={{
width: 36, height: 36, borderRadius: '50%',
display: 'flex', alignItems: 'center', justifyContent: 'center',

View File

@@ -333,13 +333,12 @@ export default function Trips() {
{/* Recent Trips */}
<motion.div
className="admin-card"
style={{ marginTop: '1.5rem' }}
className="admin-card mt-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.2 }}
>
<div className="admin-card-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div className="admin-card-header flex-between">
<h2 className="admin-card-title">Poslední jízdy</h2>
<Link to="/trips/history" className="admin-btn admin-btn-secondary admin-btn-sm">
Zobrazit historii

View File

@@ -350,8 +350,7 @@ export default function TripsAdmin() {
</motion.div>
<motion.div
className="admin-grid admin-grid-3"
style={{ marginTop: '1.5rem' }}
className="admin-grid admin-grid-3 mt-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.15 }}
@@ -399,8 +398,7 @@ export default function TripsAdmin() {
{/* Trips Table */}
<motion.div
className="admin-card"
style={{ marginTop: '1.5rem' }}
className="admin-card mt-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.2 }}

View File

@@ -109,8 +109,7 @@ export default function TripsHistory() {
</motion.div>
<motion.div
className="admin-grid admin-grid-3"
style={{ marginTop: '1.5rem' }}
className="admin-grid admin-grid-3 mt-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.15 }}
@@ -158,15 +157,14 @@ export default function TripsHistory() {
{/* Trips Table */}
<motion.div
className="admin-card"
style={{ marginTop: '1.5rem' }}
className="admin-card mt-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.2 }}
>
<div className="admin-card-body">
{loading && (
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
<div className="admin-skeleton gap-5">
{[0, 1, 2, 3, 4].map(i => (
<div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line w-1/4" />

View File

@@ -214,8 +214,8 @@ export default function Users() {
{[0, 1, 2, 3, 4].map(i => (
<div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="flex-1">
<div className="admin-skeleton-line w-1/3 mb-2" />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
<div className="admin-skeleton-line w-1/4" />

View File

@@ -183,8 +183,8 @@ export default function Vehicles() {
{[0, 1, 2, 3, 4].map(i => (
<div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line circle" />
<div style={{ flex: 1 }}>
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
<div className="flex-1">
<div className="admin-skeleton-line w-1/3 mb-2" />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div>
<div className="admin-skeleton-line w-1/4" />
@@ -259,7 +259,7 @@ export default function Vehicles() {
<tbody>
{vehicles.map((vehicle) => (
<tr key={vehicle.id} className={!vehicle.is_active ? 'admin-table-row-inactive' : ''}>
<td className="admin-mono" style={{ fontWeight: 500 }}>{vehicle.spz}</td>
<td className="admin-mono fw-500">{vehicle.spz}</td>
<td>{vehicle.name}</td>
<td>
{vehicle.brand || vehicle.model
@@ -267,7 +267,7 @@ export default function Vehicles() {
: '—'}
</td>
<td className="admin-mono">{formatKm(vehicle.initial_km)} km</td>
<td className="admin-mono" style={{ fontWeight: 500 }}>{formatKm(vehicle.current_km)} km</td>
<td className="admin-mono fw-500">{formatKm(vehicle.current_km)} km</td>
<td className="admin-mono">{vehicle.trip_count}</td>
<td>
<button

View File

@@ -1,18 +0,0 @@
/* ============================================================================
Project Status Badges
============================================================================ */
.admin-badge-project-aktivni {
background: color-mix(in srgb, var(--success) 15%, transparent);
color: var(--success);
}
.admin-badge-project-dokonceny {
background: color-mix(in srgb, var(--info) 15%, transparent);
color: var(--info);
}
.admin-badge-project-zruseny {
background: color-mix(in srgb, var(--danger) 15%, transparent);
color: var(--danger);
}