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

@@ -1 +1 @@
{"window_start":1773395802,"count":2} {"window_start":1773397556,"count":8}

View File

@@ -1 +1 @@
{"window_start":1773395237,"count":1} {"window_start":1773396919,"count":1}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/AuditLog-BQhFrceK.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/assets/InvoiceCreate-CTHJkHkW.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/assets/InvoiceDetail-PAg_BB2y.js vendored Normal file

File diff suppressed because one or more lines are too long

2
dist/assets/Invoices-Cn75FAfB.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/LeaveApproval-kG7MLpMl.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/LeaveRequests-Bd0og4aD.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/assets/OfferDetail-Bh2jG-Bc.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/Offers-DSsqWWe8.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/OrderDetail-B5bb2z01.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/Orders-BmuD7hMO.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/ProjectCreate-DBrXwzbT.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/assets/ProjectDetail-DCOqYzFf.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/Projects-DimOmyc4.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/Settings-2mPE0jr-.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/Trips-BImocn08.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

81
dist/assets/TripsAdmin-BsKU9QUl.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/TripsHistory-BaHvBurw.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/Users-Vkyk_PMH.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/Vehicles-Dbkg5bxP.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/index-D_wrslmx.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{j as x}from"./vendor-animation-0s3FMHwK.js";import{r as t}from"./vendor-react-BVs3cwbi.js";import{a as L,c as O}from"./index-DR4BORa4.js";function J({column:e,sort:r,order:n}){return r!==e?null:x.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",style:{marginLeft:4,verticalAlign:"middle"},children:x.jsx("path",{d:n==="ASC"?"M18 15l-6-6-6 6":"M6 9l6 6 6-6"})})}function V(e,r="DESC"){const[n,a]=t.useState(e),[o,c]=t.useState(r),i=t.useRef(!1),S=t.useCallback(u=>{i.current=!0,a(m=>m===u?(c(h=>h==="ASC"?"DESC":"ASC"),m):(c("DESC"),u))},[]),d=i.current?n:null;return{sort:n,order:o,handleSort:S,activeSort:d}}function I(e,r=300){const[n,a]=t.useState(e);return t.useEffect(()=>{const o=setTimeout(()=>a(e),r);return()=>clearTimeout(o)},[e,r]),n}const N="/api/admin";function _(e,{dataKey:r,search:n,sort:a,order:o,page:c,perPage:i,extraParams:S,errorMsg:d="Nepodařilo se načíst data"}={}){const u=L(),[m,h]=t.useState([]),[j,D]=t.useState(!0),[w,k]=t.useState(null),l=t.useRef(null),p=S?JSON.stringify(S):"",b=I(n,300),C=t.useCallback(async()=>{l.current&&l.current.abort();const g=new AbortController;l.current=g;try{const s=new URLSearchParams;if(b&&s.set("search",b),a&&s.set("sort",a),o&&s.set("order",o),c&&s.set("page",c),i&&s.set("per_page",i),p){const R=JSON.parse(p);Object.entries(R).forEach(([y,A])=>{A&&s.set(y,A)})}const E=await O(`${N}/${e}?${s}`,{signal:g.signal});if(E.status===401)return;const f=await E.json();f.success?(h(f.data[r]||[]),f.data.pagination&&k(f.data.pagination)):u.error(f.error||d)}catch(s){if(s.name==="AbortError")return;u.error("Chyba připojení")}finally{D(!1)}},[u,e,r,b,a,o,c,i,p,d]);return t.useEffect(()=>(C(),()=>{l.current&&l.current.abort()}),[C]),{items:m,setItems:h,loading:j,pagination:w,refetch:C}}export{J as S,_ as a,V as u}; import{j as x}from"./vendor-animation-0s3FMHwK.js";import{r as t}from"./vendor-react-BVs3cwbi.js";import{a as L,c as O}from"./index-CIpK9ruO.js";function J({column:e,sort:r,order:n}){return r!==e?null:x.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",style:{marginLeft:4,verticalAlign:"middle"},children:x.jsx("path",{d:n==="ASC"?"M18 15l-6-6-6 6":"M6 9l6 6 6-6"})})}function V(e,r="DESC"){const[n,a]=t.useState(e),[o,c]=t.useState(r),i=t.useRef(!1),S=t.useCallback(u=>{i.current=!0,a(m=>m===u?(c(h=>h==="ASC"?"DESC":"ASC"),m):(c("DESC"),u))},[]),d=i.current?n:null;return{sort:n,order:o,handleSort:S,activeSort:d}}function I(e,r=300){const[n,a]=t.useState(e);return t.useEffect(()=>{const o=setTimeout(()=>a(e),r);return()=>clearTimeout(o)},[e,r]),n}const N="/api/admin";function _(e,{dataKey:r,search:n,sort:a,order:o,page:c,perPage:i,extraParams:S,errorMsg:d="Nepodařilo se načíst data"}={}){const u=L(),[m,h]=t.useState([]),[j,D]=t.useState(!0),[w,k]=t.useState(null),l=t.useRef(null),p=S?JSON.stringify(S):"",b=I(n,300),C=t.useCallback(async()=>{l.current&&l.current.abort();const g=new AbortController;l.current=g;try{const s=new URLSearchParams;if(b&&s.set("search",b),a&&s.set("sort",a),o&&s.set("order",o),c&&s.set("page",c),i&&s.set("per_page",i),p){const R=JSON.parse(p);Object.entries(R).forEach(([y,A])=>{A&&s.set(y,A)})}const E=await O(`${N}/${e}?${s}`,{signal:g.signal});if(E.status===401)return;const f=await E.json();f.success?(h(f.data[r]||[]),f.data.pagination&&k(f.data.pagination)):u.error(f.error||d)}catch(s){if(s.name==="AbortError")return;u.error("Chyba připojení")}finally{D(!1)}},[u,e,r,b,a,o,c,i,p,d]);return t.useEffect(()=>(C(),()=>{l.current&&l.current.abort()}),[C]),{items:m,setItems:h,loading:j,pagination:w,refetch:C}}export{J as S,_ as a,V as u};

4
dist/index.html vendored
View File

@@ -29,11 +29,11 @@
<link <link
href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Urbanist:wght@400;500;600;700;800&display=swap" href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Urbanist:wght@400;500;600;700;800&display=swap"
rel="stylesheet" /> rel="stylesheet" />
<script type="module" crossorigin src="/assets/index-DR4BORa4.js"></script> <script type="module" crossorigin src="/assets/index-CIpK9ruO.js"></script>
<link rel="modulepreload" crossorigin href="/assets/vendor-react-BVs3cwbi.js"> <link rel="modulepreload" crossorigin href="/assets/vendor-react-BVs3cwbi.js">
<link rel="modulepreload" crossorigin href="/assets/vendor-animation-0s3FMHwK.js"> <link rel="modulepreload" crossorigin href="/assets/vendor-animation-0s3FMHwK.js">
<link rel="modulepreload" crossorigin href="/assets/vendor-utils-Dyr8OjFr.js"> <link rel="modulepreload" crossorigin href="/assets/vendor-utils-Dyr8OjFr.js">
<link rel="stylesheet" crossorigin href="/assets/index-Fs-Ow1Zz.css"> <link rel="stylesheet" crossorigin href="/assets/index-D_wrslmx.css">
</head> </head>
<body style="background-color: var(--bg-primary, #12121a);"> <body style="background-color: var(--bg-primary, #12121a);">

View File

@@ -3,7 +3,7 @@
'name' => 'boha/website', 'name' => 'boha/website',
'pretty_version' => 'dev-master', 'pretty_version' => 'dev-master',
'version' => 'dev-master', 'version' => 'dev-master',
'reference' => 'd70620eb05aae34107bc1e3ae0cc59609bb7497d', 'reference' => 'f7466f06678d8ce75795387cce9ff72755ac9241',
'type' => 'project', 'type' => 'project',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@@ -13,7 +13,7 @@
'boha/website' => array( 'boha/website' => array(
'pretty_version' => 'dev-master', 'pretty_version' => 'dev-master',
'version' => 'dev-master', 'version' => 'dev-master',
'reference' => 'd70620eb05aae34107bc1e3ae0cc59609bb7497d', 'reference' => 'f7466f06678d8ce75795387cce9ff72755ac9241',
'type' => 'project', 'type' => 'project',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),

View File

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

View File

@@ -291,7 +291,29 @@ img {
text-decoration: underline; 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 Forms
@@ -434,11 +456,10 @@ img {
/* Checkbox */ /* Checkbox */
.admin-form-checkbox { .admin-form-checkbox {
display: inline-flex; display: inline-flex;
align-items: center; align-items: flex-start;
gap: 0; gap: 0;
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
white-space: nowrap;
} }
.admin-form-checkbox input { .admin-form-checkbox input {
@@ -508,7 +529,7 @@ img {
align-items: center; align-items: center;
font-size: 13px; font-size: 13px;
color: var(--text-secondary); color: var(--text-secondary);
line-height: 1; line-height: 1.4;
} }
/* Reorderable List */ /* Reorderable List */
@@ -1500,6 +1521,23 @@ img {
color: var(--danger); 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 Modals
============================================================================ */ ============================================================================ */

View File

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

View File

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

View File

@@ -24,7 +24,7 @@ export default class ErrorBoundary extends Component {
<line x1="12" y1="17" x2="12.01" y2="17" /> <line x1="12" y1="17" x2="12.01" y2="17" />
</svg> </svg>
</div> </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 && ( {import.meta.env.DEV && this.state.error && (
<pre className="admin-error-stack"> <pre className="admin-error-stack">
{this.state.error.message} {this.state.error.message}

View File

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

View File

@@ -14,7 +14,7 @@ export default function OfferScopeSection({
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.3 }} 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> <h3 className="admin-card-title" style={{ margin: 0 }}>Rozsah projektu</h3>
{!readOnly && ( {!readOnly && (
<div style={{ display: 'flex', gap: '0.5rem', position: 'relative' }}> <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 }) { function ProjectLogRow({ log, index, projectList, onUpdate, onRemove }) {
return ( return (
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center', marginBottom: '0.5rem' }}> <div className="flex-row gap-2 mb-2">
<select <select
value={log.project_id} value={log.project_id}
onChange={(e) => onUpdate(index, 'project_id', e.target.value)} onChange={(e) => onUpdate(index, 'project_id', e.target.value)}

View File

@@ -44,7 +44,7 @@ export default function DashActivityFeed({ activities }) {
return ( return (
<div className="admin-card dash-activity-card"> <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> <h2 className="admin-card-title">Audit log</h2>
<Link to="/audit-log" className="admin-btn admin-btn-primary admin-btn-sm">Detail &rarr;</Link> <Link to="/audit-log" className="admin-btn admin-btn-primary admin-btn-sm">Detail &rarr;</Link>
</div> </div>

View File

@@ -8,7 +8,7 @@ export default function DashAttendanceToday({ attendance }) {
return ( return (
<div className="admin-card dash-attendance-card"> <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> <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> <Link to="/attendance/admin" className="admin-btn admin-btn-primary admin-btn-sm">Detail &rarr;</Link>
</div> </div>

View File

@@ -81,7 +81,7 @@ export default function DashProfile({
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.25 }} 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> <h2 className="admin-card-title">Váš účet</h2>
<button onClick={openEditModal} className="admin-btn admin-btn-secondary admin-btn-sm"> <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"> <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 */} {/* 2FA Section */}
<div style={{ borderTop: '1px solid var(--border-color)', marginTop: '1rem', paddingTop: '1rem' }}> <div style={{ borderTop: '1px solid var(--border-color)', marginTop: '1rem', paddingTop: '1rem' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}> <div className="flex-between">
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}> <div className="flex-row-gap">
<div style={{ <div style={{
width: 36, height: 36, borderRadius: '50%', width: 36, height: 36, borderRadius: '50%',
display: 'flex', alignItems: 'center', justifyContent: 'center', display: 'flex', alignItems: 'center', justifyContent: 'center',
@@ -202,7 +202,7 @@ export default function DashProfile({
<div className="admin-modal-body"> <div className="admin-modal-body">
{backupCodes ? ( {backupCodes ? (
<div> <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"> <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" /> <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" /> <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> </div>
)} )}
{totpSecret && ( {totpSecret && (
<div style={{ marginBottom: '1rem' }}> <div className="mb-4">
<label className="admin-form-label" style={{ fontSize: '0.75rem' }}>Nebo zadejte klíč ručně:</label> <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' }}> <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> <span>{totpSecret}</span>

View File

@@ -125,7 +125,7 @@ export default function DashSessions() {
{[0, 1, 2].map(i => ( {[0, 1, 2].map(i => (
<div key={i} className="admin-skeleton-row"> <div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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/2" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
</div> </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" className="admin-form-textarea"
rows={3} rows={3}
/> />
<div style={{ marginTop: '0.5rem' }}> <div className="mt-2">
<button <button
onClick={handleSaveNotes} onClick={handleSaveNotes}
className="admin-btn admin-btn-secondary admin-btn-sm" className="admin-btn admin-btn-secondary admin-btn-sm"
@@ -543,7 +543,7 @@ export default function Attendance() {
{/* Completed Today */} {/* Completed Today */}
{completedToday.length > 0 && ( {completedToday.length > 0 && (
<div className="admin-card" style={{ marginTop: '1.5rem' }}> <div className="admin-card mt-6">
<div className="admin-card-header"> <div className="admin-card-header">
<h2 className="admin-card-title">Dnešní dokončené směny</h2> <h2 className="admin-card-title">Dnešní dokončené směny</h2>
</div> </div>

View File

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

View File

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

View File

@@ -235,8 +235,7 @@ export default function AttendanceHistory() {
{/* Filters */} {/* Filters */}
<motion.div <motion.div
className="admin-card" className="admin-card mb-6"
style={{ marginBottom: '1.5rem' }}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.1 }} transition={{ duration: 0.4, delay: 0.1 }}
@@ -256,8 +255,7 @@ export default function AttendanceHistory() {
{/* Monthly Fund Card */} {/* Monthly Fund Card */}
<motion.div <motion.div
className="admin-card" className="admin-card mb-6"
style={{ marginBottom: '1.5rem' }}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.15 }} 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" style={{ gap: '0.5rem' }}>
<div className="admin-skeleton-row" style={{ gap: '1rem' }}> <div className="admin-skeleton-row" style={{ gap: '1rem' }}>
<div className="admin-skeleton-line" style={{ width: '48px', height: '48px', borderRadius: '12px', flexShrink: 0 }} /> <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-1/2" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-full" style={{ height: '6px', borderRadius: '3px' }} /> <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' }} /> <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}`} href={`https://www.google.com/maps?q=${record.arrival_lat},${record.arrival_lng}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="admin-btn admin-btn-secondary admin-btn-sm" className="admin-btn admin-btn-secondary admin-btn-sm mt-2"
style={{ marginTop: '0.5rem' }}
> >
Otevřít v Google Maps Otevřít v Google Maps
</a> </a>
@@ -287,8 +286,7 @@ export default function AttendanceLocation() {
href={`https://www.google.com/maps?q=${record.departure_lat},${record.departure_lng}`} href={`https://www.google.com/maps?q=${record.departure_lat},${record.departure_lng}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="admin-btn admin-btn-secondary admin-btn-sm" className="admin-btn admin-btn-secondary admin-btn-sm mt-2"
style={{ marginTop: '0.5rem' }}
> >
Otevřít v Google Maps Otevřít v Google Maps
</a> </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: '80px' }} />
<div className="admin-skeleton-line" style={{ width: '70px', borderRadius: '10px' }} /> <div className="admin-skeleton-line" style={{ width: '70px', borderRadius: '10px' }} />
<div className="admin-skeleton-line" style={{ width: '80px' }} /> <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 className="admin-skeleton-line" style={{ width: '90px' }} />
</div> </div>
))} ))}
@@ -290,11 +290,10 @@ export default function AuditLog() {
)} )}
<motion.div <motion.div
className="admin-card" className="admin-card mb-4"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.1 }} transition={{ duration: 0.4, delay: 0.1 }}
style={{ marginBottom: '1rem' }}
> >
<div className="admin-card-body"> <div className="admin-card-body">
<div className="admin-form-row admin-form-row-5"> <div className="admin-form-row admin-form-row-5">
@@ -399,7 +398,7 @@ export default function AuditLog() {
{!loading && logs.map((log) => ( {!loading && logs.map((log) => (
<tr key={log.id}> <tr key={log.id}>
<td className="admin-mono">{formatDatetime(log.created_at)}</td> <td className="admin-mono">{formatDatetime(log.created_at)}</td>
<td style={{ fontWeight: 500 }}>{log.username || '-'}</td> <td className="fw-500">{log.username || '-'}</td>
<td> <td>
<span className={`admin-badge ${ACTION_BADGE_CLASS[log.action] || 'admin-badge-secondary'}`}> <span className={`admin-badge ${ACTION_BADGE_CLASS[log.action] || 'admin-badge-secondary'}`}>
{ACTION_LABELS[log.action] || log.action} {ACTION_LABELS[log.action] || log.action}

View File

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

View File

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

View File

@@ -420,7 +420,7 @@ export default function InvoiceCreate() {
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }} 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"> <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"> <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" /> <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}> <button onClick={handleSubmit} className="admin-btn admin-btn-primary" disabled={saving}>
{saving ? ( {saving ? (
<> <>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} /> <div className="admin-spinner admin-spinner-sm" />
Ukládání... Ukládání...
</> </>
) : 'Uložit'} ) : 'Uložit'}
@@ -602,7 +602,7 @@ export default function InvoiceCreate() {
</select> </select>
</FormField> </FormField>
<FormField label="DPH"> <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' }}> <label className="admin-form-checkbox" style={{ whiteSpace: 'nowrap' }}>
<input <input
type="checkbox" type="checkbox"
@@ -642,7 +642,7 @@ export default function InvoiceCreate() {
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.2 }} 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> <div>
<h3 className="admin-card-title" style={{ margin: 0 }}>Položky</h3> <h3 className="admin-card-title" style={{ margin: 0 }}>Položky</h3>
{errors.items && <span className="admin-form-error">{errors.items}</span>} {errors.items && <span className="admin-form-error">{errors.items}</span>}
@@ -684,15 +684,14 @@ export default function InvoiceCreate() {
<td style={{ width: '2rem' }}> <td style={{ width: '2rem' }}>
<DragHandle listeners={listeners} attributes={attributes} /> <DragHandle listeners={listeners} attributes={attributes} />
</td> </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> <td>
<input <input
type="text" type="text"
value={item.description} value={item.description}
onChange={(e) => updateItem(index, 'description', e.target.value)} onChange={(e) => updateItem(index, 'description', e.target.value)}
className="admin-form-input" className="admin-form-input fw-500"
placeholder="Popis položky..." placeholder="Popis položky..."
style={{ fontWeight: 500 }}
/> />
</td> </td>
<td> <td>

View File

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

View File

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

View File

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

View File

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

View File

@@ -163,11 +163,11 @@ export default function OfferDetail() {
return ( return (
<div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}> <div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}>
<div className="admin-skeleton-row" style={{ justifyContent: 'space-between' }}> <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" style={{ width: '32px', height: '32px', borderRadius: '8px' }} />
<div className="admin-skeleton-line h-8" style={{ width: '200px' }} /> <div className="admin-skeleton-line h-8" style={{ width: '200px' }} />
</div> </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 className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} /> <div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
</div> </div>
@@ -186,9 +186,9 @@ export default function OfferDetail() {
<div className="admin-skeleton" style={{ gap: '1.25rem' }}> <div className="admin-skeleton" style={{ gap: '1.25rem' }}>
{[0, 1, 2].map(i => ( {[0, 1, 2].map(i => (
<div key={i} className="admin-skeleton-row"> <div key={i} className="admin-skeleton-row">
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-full" /></div> <div className="flex-1"><div className="admin-skeleton-line w-full" /></div>
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-3/4" /></div> <div className="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-1/2" /></div>
</div> </div>
))} ))}
</div> </div>
@@ -216,7 +216,7 @@ export default function OfferDetail() {
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }} 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"> <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"> <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" /> <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}> <button onClick={handlePdf} className="admin-btn admin-btn-secondary" disabled={pdfLoading}>
{pdfLoading ? ( {pdfLoading ? (
<> <>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} /> <div className="admin-spinner admin-spinner-sm" />
PDF... PDF...
</> </>
) : ( ) : (
@@ -293,7 +293,7 @@ export default function OfferDetail() {
<button onClick={handleSave} className="admin-btn admin-btn-primary" disabled={saving}> <button onClick={handleSave} className="admin-btn admin-btn-primary" disabled={saving}>
{saving ? ( {saving ? (
<> <>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} /> <div className="admin-spinner admin-spinner-sm" />
Ukládání... Ukládání...
</> </>
) : 'Uložit'} ) : 'Uložit'}
@@ -410,14 +410,13 @@ export default function OfferDetail() {
<div className="offers-form-row-3"> <div className="offers-form-row-3">
<FormField label="Sazba DPH (%)"> <FormField label="Sazba DPH (%)">
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}> <div className="flex-row-gap">
<input <input
type="number" type="number"
value={form.vat_rate} value={form.vat_rate}
onChange={(e) => updateForm('vat_rate', parseFloat(e.target.value) || 0)} onChange={(e) => updateForm('vat_rate', parseFloat(e.target.value) || 0)}
className="admin-form-input" className="admin-form-input flex-1"
step="0.1" step="0.1"
style={{ flex: 1 }}
readOnly={isInvalidated} readOnly={isInvalidated}
/> />
<label className="admin-form-checkbox" style={{ whiteSpace: 'nowrap' }}> <label className="admin-form-checkbox" style={{ whiteSpace: 'nowrap' }}>
@@ -514,7 +513,7 @@ export default function OfferDetail() {
</FormField> </FormField>
<FormField label="Příloha (PDF)"> <FormField label="Příloha (PDF)">
{orderAttachment ? ( {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"> <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" /> <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" /> <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 => ( {[0, 1, 2, 3, 4].map(i => (
<div key={i} className="admin-skeleton-row"> <div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div> </div>
@@ -267,7 +267,7 @@ export default function Offers() {
transition={{ duration: 0.4, delay: 0.1 }} transition={{ duration: 0.4, delay: 0.1 }}
> >
<div className="admin-card-body"> <div className="admin-card-body">
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}> <div className="admin-search-bar mb-4">
<input <input
type="text" type="text"
value={search} value={search}
@@ -315,7 +315,7 @@ export default function Offers() {
<th style={{ cursor: 'pointer' }} onClick={() => handleSort('currency')}> <th style={{ cursor: 'pointer' }} onClick={() => handleSort('currency')}>
Měna <SortIcon column="currency" sort={activeSort} order={order} /> Měna <SortIcon column="currency" sort={activeSort} order={order} />
</th> </th>
<th style={{ textAlign: 'right' }}>Celkem</th> <th className="text-right">Celkem</th>
<th>Akce</th> <th>Akce</th>
</tr> </tr>
</thead> </thead>
@@ -395,7 +395,7 @@ export default function Offers() {
{q.currency} {q.currency}
</span> </span>
</td> </td>
<td className="admin-mono" style={{ textAlign: 'right', fontWeight: 500 }}> <td className="admin-mono text-right fw-500">
{formatCurrency(q.total, q.currency)} {formatCurrency(q.total, q.currency)}
</td> </td>
<td> <td>
@@ -578,7 +578,7 @@ export default function Offers() {
</FormField> </FormField>
<FormField label="Příloha (PDF)"> <FormField label="Příloha (PDF)">
{orderAttachment ? ( {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"> <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" /> <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" /> <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 => ( {[0, 1, 2, 3, 4].map(i => (
<div key={i} className="admin-skeleton-row"> <div key={i} className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div> </div>
@@ -289,7 +289,7 @@ export default function OffersCustomers() {
transition={{ duration: 0.4, delay: 0.1 }} transition={{ duration: 0.4, delay: 0.1 }}
> >
<div className="admin-card-body"> <div className="admin-card-body">
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}> <div className="admin-search-bar mb-4">
<input <input
type="text" type="text"
value={search} value={search}
@@ -606,7 +606,7 @@ export default function OffersCustomers() {
<button type="button" onClick={handleSubmit} className="admin-btn admin-btn-primary" disabled={saving}> <button type="button" onClick={handleSubmit} className="admin-btn admin-btn-primary" disabled={saving}>
{saving && ( {saving && (
<> <>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} /> <div className="admin-spinner admin-spinner-sm" />
Ukládání... Ukládání...
</> </>
)} )}

View File

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

View File

@@ -217,11 +217,11 @@ export default function OrderDetail() {
return ( return (
<div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}> <div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}>
<div className="admin-skeleton-row" style={{ justifyContent: 'space-between' }}> <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" style={{ width: '32px', height: '32px', borderRadius: '8px' }} />
<div className="admin-skeleton-line h-8" style={{ width: '200px' }} /> <div className="admin-skeleton-line h-8" style={{ width: '200px' }} />
</div> </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 className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} /> <div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
</div> </div>
@@ -240,9 +240,9 @@ export default function OrderDetail() {
<div className="admin-skeleton" style={{ gap: '1.25rem' }}> <div className="admin-skeleton" style={{ gap: '1.25rem' }}>
{[0, 1, 2].map(i => ( {[0, 1, 2].map(i => (
<div key={i} className="admin-skeleton-row"> <div key={i} className="admin-skeleton-row">
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-full" /></div> <div className="flex-1"><div className="admin-skeleton-line w-full" /></div>
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-3/4" /></div> <div className="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-1/2" /></div>
</div> </div>
))} ))}
</div> </div>
@@ -262,14 +262,14 @@ export default function OrderDetail() {
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }} 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"> <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"> <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" /> <path d="M19 12H5M12 19l-7-7 7-7" />
</svg> </svg>
</Link> </Link>
<div> <div>
<h1 className="admin-page-title" style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}> <h1 className="admin-page-title flex-row-gap">
{editingNumber ? ( {editingNumber ? (
<span style={{ display: 'inline-flex', alignItems: 'center', gap: '0.5rem' }}> <span style={{ display: 'inline-flex', alignItems: 'center', gap: '0.5rem' }}>
Objednávka Objednávka
@@ -345,7 +345,7 @@ export default function OrderDetail() {
disabled={statusChanging === status} disabled={statusChanging === status}
> >
{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 TRANSITION_LABELS[status] || status
)} )}
@@ -372,7 +372,7 @@ export default function OrderDetail() {
> >
<div className="admin-card-body"> <div className="admin-card-body">
<h3 className="admin-card-title">Informace</h3> <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"> <FormField label="Nabídka">
<div> <div>
<Link to={`/offers/${order.quotation_id}`} className="link-accent"> <Link to={`/offers/${order.quotation_id}`} className="link-accent">
@@ -393,9 +393,9 @@ export default function OrderDetail() {
</div> </div>
</FormField> </FormField>
</div> </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"> <FormField label="Zákazník">
<div style={{ fontWeight: 500 }}>{order.customer_name || '—'}</div> <div className="fw-500">{order.customer_name || '—'}</div>
</FormField> </FormField>
<FormField label="Číslo obj. zákazníka"> <FormField label="Číslo obj. zákazníka">
<div>{order.customer_order_number || '—'}</div> <div>{order.customer_order_number || '—'}</div>
@@ -404,7 +404,7 @@ export default function OrderDetail() {
<div>{order.currency}</div> <div>{order.currency}</div>
</FormField> </FormField>
</div> </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í"> <FormField label="Datum vytvoření">
<div>{formatDate(order.created_at)}</div> <div>{formatDate(order.created_at)}</div>
</FormField> </FormField>
@@ -418,7 +418,7 @@ export default function OrderDetail() {
disabled={attachmentLoading} disabled={attachmentLoading}
> >
{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"> <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" /> <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}> <tr key={item.id || index}>
<td style={{ color: 'var(--text-tertiary)', textAlign: 'center', fontWeight: 500 }}>{index + 1}</td> <td style={{ color: 'var(--text-tertiary)', textAlign: 'center', fontWeight: 500 }}>{index + 1}</td>
<td> <td>
<div style={{ fontWeight: 500 }}>{item.description || '—'}</div> <div className="fw-500">{item.description || '—'}</div>
{item.item_description && ( {item.item_description && (
<div style={{ fontSize: '0.8rem', color: 'var(--text-tertiary)', marginTop: '0.25rem' }}>{item.item_description}</div> <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> </FormField>
{hasPermission('orders.edit') && ( {hasPermission('orders.edit') && (
<div style={{ marginTop: '0.5rem' }}> <div className="mt-2">
<button <button
onClick={handleSaveNotes} onClick={handleSaveNotes}
className="admin-btn admin-btn-secondary admin-btn-sm" 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" style={{ gap: '1.25rem' }}>
<div className="admin-skeleton-row"> <div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div> </div>
@@ -91,7 +91,7 @@ export default function Orders() {
</div> </div>
<div className="admin-skeleton-row"> <div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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/2" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
</div> </div>
@@ -99,7 +99,7 @@ export default function Orders() {
</div> </div>
<div className="admin-skeleton-row"> <div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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-3/4" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div> </div>
@@ -107,7 +107,7 @@ export default function Orders() {
</div> </div>
<div className="admin-skeleton-row"> <div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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/2" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
</div> </div>
@@ -115,7 +115,7 @@ export default function Orders() {
</div> </div>
<div className="admin-skeleton-row"> <div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div> </div>
@@ -151,7 +151,7 @@ export default function Orders() {
transition={{ duration: 0.4, delay: 0.1 }} transition={{ duration: 0.4, delay: 0.1 }}
> >
<div className="admin-card-body"> <div className="admin-card-body">
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}> <div className="admin-search-bar mb-4">
<input <input
type="text" type="text"
value={search} value={search}
@@ -191,7 +191,7 @@ export default function Orders() {
<th style={{ cursor: 'pointer' }} onClick={() => handleSort('created_at')}> <th style={{ cursor: 'pointer' }} onClick={() => handleSort('created_at')}>
Datum <SortIcon column="created_at" sort={activeSort} order={order} /> Datum <SortIcon column="created_at" sort={activeSort} order={order} />
</th> </th>
<th style={{ textAlign: 'right' }}>Celkem</th> <th className="text-right">Celkem</th>
<th>Akce</th> <th>Akce</th>
</tr> </tr>
</thead> </thead>
@@ -217,7 +217,7 @@ export default function Orders() {
<td className="admin-mono"> <td className="admin-mono">
{formatDate(o.created_at)} {formatDate(o.created_at)}
</td> </td>
<td className="admin-mono" style={{ textAlign: 'right', fontWeight: 500 }}> <td className="admin-mono text-right fw-500">
{formatCurrency(o.total, o.currency)} {formatCurrency(o.total, o.currency)}
</td> </td>
<td> <td>

View File

@@ -158,7 +158,7 @@ export default function ProjectCreate() {
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }} 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"> <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"> <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" /> <path d="M19 12H5M12 19l-7-7 7-7" />

View File

@@ -210,7 +210,7 @@ export default function ProjectDetail() {
return ( return (
<div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}> <div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}>
<div className="admin-skeleton-row" style={{ justifyContent: 'space-between' }}> <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" style={{ width: '32px', height: '32px', borderRadius: '8px' }} />
<div className="admin-skeleton-line h-8" style={{ width: '200px' }} /> <div className="admin-skeleton-line h-8" style={{ width: '200px' }} />
</div> </div>
@@ -273,7 +273,7 @@ export default function ProjectDetail() {
<button onClick={handleSave} className="admin-btn admin-btn-primary" disabled={saving}> <button onClick={handleSave} className="admin-btn admin-btn-primary" disabled={saving}>
{saving ? ( {saving ? (
<> <>
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} /> <div className="admin-spinner admin-spinner-sm" />
Ukládání... Ukládání...
</> </>
) : 'Uložit'} ) : 'Uložit'}
@@ -380,7 +380,7 @@ export default function ProjectDetail() {
<h3 className="admin-card-title">Poznámky</h3> <h3 className="admin-card-title">Poznámky</h3>
{/* Add note */} {/* Add note */}
<div style={{ marginBottom: '1rem' }}> <div className="mb-4">
<textarea <textarea
value={newNote} value={newNote}
onChange={(e) => setNewNote(e.target.value)} onChange={(e) => setNewNote(e.target.value)}
@@ -394,14 +394,14 @@ export default function ProjectDetail() {
} }
}} }}
/> />
<div style={{ marginTop: '0.5rem' }}> <div className="mt-2">
<button <button
onClick={handleAddNote} onClick={handleAddNote}
className="admin-btn admin-btn-secondary admin-btn-sm" className="admin-btn admin-btn-secondary admin-btn-sm"
disabled={addingNote || !newNote.trim()} disabled={addingNote || !newNote.trim()}
> >
{addingNote ? ( {addingNote ? (
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} /> <div className="admin-spinner admin-spinner-sm" />
) : ( ) : (
'Přidat poznámku' '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={{ 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' }}> <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.25rem' }}>
<span style={{ fontWeight: 600, fontSize: '0.85rem' }}> <span style={{ fontWeight: 600, fontSize: '0.85rem' }}>
{note.user_name} {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" style={{ gap: '1.25rem' }}>
<div className="admin-skeleton-row"> <div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div> </div>
@@ -86,7 +86,7 @@ export default function Projects() {
</div> </div>
<div className="admin-skeleton-row"> <div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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/2" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
</div> </div>
@@ -94,7 +94,7 @@ export default function Projects() {
</div> </div>
<div className="admin-skeleton-row"> <div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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-3/4" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div> </div>
@@ -102,7 +102,7 @@ export default function Projects() {
</div> </div>
<div className="admin-skeleton-row"> <div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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/2" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
</div> </div>
@@ -110,7 +110,7 @@ export default function Projects() {
</div> </div>
<div className="admin-skeleton-row"> <div className="admin-skeleton-row">
<div className="admin-skeleton-line circle" /> <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/3" style={{ marginBottom: '0.5rem' }} />
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} /> <div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
</div> </div>
@@ -155,7 +155,7 @@ export default function Projects() {
transition={{ duration: 0.4, delay: 0.1 }} transition={{ duration: 0.4, delay: 0.1 }}
> >
<div className="admin-card-body"> <div className="admin-card-body">
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}> <div className="admin-search-bar mb-4">
<input <input
type="text" type="text"
value={search} value={search}
@@ -210,7 +210,7 @@ export default function Projects() {
{p.project_number} {p.project_number}
</Link> </Link>
</td> </td>
<td style={{ fontWeight: 500 }}>{p.name || '—'}</td> <td className="fw-500">{p.name || '—'}</td>
<td>{p.customer_name || '—'}</td> <td>{p.customer_name || '—'}</td>
<td> <td>
<span className={`admin-badge ${STATUS_CLASSES[p.status] || ''}`}> <span className={`admin-badge ${STATUS_CLASSES[p.status] || ''}`}>
@@ -242,7 +242,7 @@ export default function Projects() {
disabled={deletingId === p.id} disabled={deletingId === p.id}
> >
{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"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<polyline points="3 6 5 6 21 6" /> <polyline points="3 6 5 6 21 6" />

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More