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:
@@ -1 +1 @@
|
||||
{"window_start":1773395802,"count":2}
|
||||
{"window_start":1773397556,"count":8}
|
||||
@@ -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
1
dist/assets/AttendanceBalances-BS1C5IIC.js
vendored
Normal file
1
dist/assets/AttendanceBalances-BS1C5IIC.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/AttendanceBalances-VuVWSDIF.js
vendored
1
dist/assets/AttendanceBalances-VuVWSDIF.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
88
dist/assets/AttendanceHistory-DXI4lNtw.js
vendored
Normal file
88
dist/assets/AttendanceHistory-DXI4lNtw.js
vendored
Normal file
File diff suppressed because one or more lines are too long
88
dist/assets/AttendanceHistory-t8y3xLMu.js
vendored
88
dist/assets/AttendanceHistory-t8y3xLMu.js
vendored
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
1
dist/assets/AuditLog-BQhFrceK.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/AuditLog-DhvkfUUn.js
vendored
1
dist/assets/AuditLog-DhvkfUUn.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/CompanySettings-CU7rsymA.js
vendored
1
dist/assets/CompanySettings-CU7rsymA.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/CompanySettings-u5jxKdiK.js
vendored
Normal file
1
dist/assets/CompanySettings-u5jxKdiK.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
dist/assets/InvoiceCreate-BleTc8jp.js
vendored
2
dist/assets/InvoiceCreate-BleTc8jp.js
vendored
File diff suppressed because one or more lines are too long
2
dist/assets/InvoiceCreate-CTHJkHkW.js
vendored
Normal file
2
dist/assets/InvoiceCreate-CTHJkHkW.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
dist/assets/InvoiceDetail-ByzPAOdj.js
vendored
2
dist/assets/InvoiceDetail-ByzPAOdj.js
vendored
File diff suppressed because one or more lines are too long
2
dist/assets/InvoiceDetail-PAg_BB2y.js
vendored
Normal file
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
2
dist/assets/Invoices-Cn75FAfB.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
dist/assets/Invoices-ITclZ9BQ.js
vendored
2
dist/assets/Invoices-ITclZ9BQ.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/LeaveApproval-CXy3WNBB.js
vendored
1
dist/assets/LeaveApproval-CXy3WNBB.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/LeaveApproval-kG7MLpMl.js
vendored
Normal file
1
dist/assets/LeaveApproval-kG7MLpMl.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/LeaveRequests-B44rObMU.js
vendored
1
dist/assets/LeaveRequests-B44rObMU.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/LeaveRequests-Bd0og4aD.js
vendored
Normal file
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
1
dist/assets/OfferDetail-Bh2jG-Bc.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/OfferDetail-C4qFNeVS.js
vendored
1
dist/assets/OfferDetail-C4qFNeVS.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/Offers-Bvh1o4A_.js
vendored
1
dist/assets/Offers-Bvh1o4A_.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/Offers-DSsqWWe8.js
vendored
Normal file
1
dist/assets/Offers-DSsqWWe8.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/OffersCustomers-2j5yM5VP.js
vendored
1
dist/assets/OffersCustomers-2j5yM5VP.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/OffersCustomers-CIlVz167.js
vendored
Normal file
1
dist/assets/OffersCustomers-CIlVz167.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/OffersTemplates-CDubG3Qc.js
vendored
1
dist/assets/OffersTemplates-CDubG3Qc.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/OffersTemplates-D_mMxLKr.js
vendored
Normal file
1
dist/assets/OffersTemplates-D_mMxLKr.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/OrderDetail-B5bb2z01.js
vendored
Normal file
1
dist/assets/OrderDetail-B5bb2z01.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/OrderDetail-CmnxSUbb.js
vendored
1
dist/assets/OrderDetail-CmnxSUbb.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/Orders-BmuD7hMO.js
vendored
Normal file
1
dist/assets/Orders-BmuD7hMO.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/Orders-LiFYL-f9.js
vendored
1
dist/assets/Orders-LiFYL-f9.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/ProjectCreate-BQdIo6Jy.js
vendored
1
dist/assets/ProjectCreate-BQdIo6Jy.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/ProjectCreate-DBrXwzbT.js
vendored
Normal file
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
1
dist/assets/ProjectDetail-DCOqYzFf.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/ProjectDetail-QM0fWoAA.js
vendored
1
dist/assets/ProjectDetail-QM0fWoAA.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/Projects-DBFc9xMy.js
vendored
1
dist/assets/Projects-DBFc9xMy.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/Projects-DimOmyc4.js
vendored
Normal file
1
dist/assets/Projects-DimOmyc4.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/ReceivedInvoices-BcHj9vza.js
vendored
Normal file
1
dist/assets/ReceivedInvoices-BcHj9vza.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/ReceivedInvoices-C5VyJ7z_.js
vendored
1
dist/assets/ReceivedInvoices-C5VyJ7z_.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/Settings-2mPE0jr-.js
vendored
Normal file
1
dist/assets/Settings-2mPE0jr-.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/Settings-C86hPQ2W.js
vendored
1
dist/assets/Settings-C86hPQ2W.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/Trips-BImocn08.js
vendored
Normal file
1
dist/assets/Trips-BImocn08.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/Trips-W46qWedf.js
vendored
1
dist/assets/Trips-W46qWedf.js
vendored
File diff suppressed because one or more lines are too long
81
dist/assets/TripsAdmin-BsKU9QUl.js
vendored
Normal file
81
dist/assets/TripsAdmin-BsKU9QUl.js
vendored
Normal file
File diff suppressed because one or more lines are too long
81
dist/assets/TripsAdmin-C3YsxaMm.js
vendored
81
dist/assets/TripsAdmin-C3YsxaMm.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/TripsHistory-BaHvBurw.js
vendored
Normal file
1
dist/assets/TripsHistory-BaHvBurw.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/TripsHistory-CLT3hbqV.js
vendored
1
dist/assets/TripsHistory-CLT3hbqV.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/Users-DV5EKtbD.js
vendored
1
dist/assets/Users-DV5EKtbD.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/Users-Vkyk_PMH.js
vendored
Normal file
1
dist/assets/Users-Vkyk_PMH.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/Vehicles-BTDEnTva.js
vendored
1
dist/assets/Vehicles-BTDEnTva.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/Vehicles-Dbkg5bxP.js
vendored
Normal file
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
1
dist/assets/index-D_wrslmx.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/index-Fs-Ow1Zz.css
vendored
1
dist/assets/index-Fs-Ow1Zz.css
vendored
File diff suppressed because one or more lines are too long
@@ -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
4
dist/index.html
vendored
@@ -29,11 +29,11 @@
|
||||
<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"
|
||||
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-animation-0s3FMHwK.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>
|
||||
|
||||
<body style="background-color: var(--bg-primary, #12121a);">
|
||||
|
||||
4
dist/vendor/composer/installed.php
vendored
4
dist/vendor/composer/installed.php
vendored
@@ -3,7 +3,7 @@
|
||||
'name' => 'boha/website',
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => 'd70620eb05aae34107bc1e3ae0cc59609bb7497d',
|
||||
'reference' => 'f7466f06678d8ce75795387cce9ff72755ac9241',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@@ -13,7 +13,7 @@
|
||||
'boha/website' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => 'd70620eb05aae34107bc1e3ae0cc59609bb7497d',
|
||||
'reference' => 'f7466f06678d8ce75795387cce9ff72755ac9241',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
||||
@@ -11,9 +11,6 @@ import './admin.css'
|
||||
import './login.css'
|
||||
import './dashboard.css'
|
||||
import './attendance.css'
|
||||
import './leave.css'
|
||||
import './orders.css'
|
||||
import './projects.css'
|
||||
import './settings.css'
|
||||
import './offers.css'
|
||||
import './invoices.css'
|
||||
|
||||
@@ -291,7 +291,29 @@ img {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Login styles moved to login.css */
|
||||
/* Layout utilities */
|
||||
.flex-1 { flex: 1; }
|
||||
.flex-row { display: flex; align-items: center; }
|
||||
.flex-row-gap { display: flex; align-items: center; gap: var(--space-3); }
|
||||
.flex-between { display: flex; align-items: center; justify-content: space-between; }
|
||||
|
||||
/* Spacing utilities */
|
||||
.mb-2 { margin-bottom: var(--space-2); }
|
||||
.mb-4 { margin-bottom: var(--space-4); }
|
||||
.mb-6 { margin-bottom: var(--space-6); }
|
||||
.mt-2 { margin-top: var(--space-2); }
|
||||
.mt-6 { margin-top: var(--space-6); }
|
||||
.gap-2 { gap: var(--space-2); }
|
||||
.gap-4 { gap: var(--space-4); }
|
||||
.gap-5 { gap: var(--space-5); }
|
||||
|
||||
/* Typography utilities */
|
||||
.fw-500 { font-weight: 500; }
|
||||
.text-right { text-align: right; }
|
||||
.text-center { text-align: center; }
|
||||
|
||||
/* Spinner variant */
|
||||
.admin-spinner-sm { width: 16px; height: 16px; border-width: 2px; }
|
||||
|
||||
/* ============================================================================
|
||||
Forms
|
||||
@@ -434,11 +456,10 @@ img {
|
||||
/* Checkbox */
|
||||
.admin-form-checkbox {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
gap: 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.admin-form-checkbox input {
|
||||
@@ -508,7 +529,7 @@ img {
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Reorderable List */
|
||||
@@ -1500,6 +1521,23 @@ img {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
/* Status Badges - Leave Requests */
|
||||
.badge-pending { background: color-mix(in srgb, var(--warning) 15%, transparent); color: var(--warning); }
|
||||
.badge-approved { background: color-mix(in srgb, var(--success) 15%, transparent); color: var(--success); }
|
||||
.badge-rejected { background: color-mix(in srgb, var(--danger) 15%, transparent); color: var(--danger); }
|
||||
.badge-cancelled { background: var(--muted-light); color: var(--muted); }
|
||||
|
||||
/* Status Badges - Orders */
|
||||
.admin-badge-order-prijata { background: color-mix(in srgb, var(--info) 15%, transparent); color: var(--info); }
|
||||
.admin-badge-order-realizace { background: color-mix(in srgb, var(--warning) 15%, transparent); color: var(--warning); }
|
||||
.admin-badge-order-dokoncena { background: color-mix(in srgb, var(--success) 15%, transparent); color: var(--success); }
|
||||
.admin-badge-order-stornovana { background: color-mix(in srgb, var(--danger) 15%, transparent); color: var(--danger); }
|
||||
|
||||
/* Status Badges - Projects */
|
||||
.admin-badge-project-aktivni { background: color-mix(in srgb, var(--success) 15%, transparent); color: var(--success); }
|
||||
.admin-badge-project-dokonceny { background: color-mix(in srgb, var(--info) 15%, transparent); color: var(--info); }
|
||||
.admin-badge-project-zruseny { background: color-mix(in srgb, var(--danger) 15%, transparent); color: var(--danger); }
|
||||
|
||||
/* ============================================================================
|
||||
Modals
|
||||
============================================================================ */
|
||||
|
||||
@@ -75,7 +75,7 @@ export default function AdminLayout() {
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div style={{ flex: 1 }} />
|
||||
<div className="flex-1" />
|
||||
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
|
||||
@@ -107,7 +107,7 @@ export default function ConfirmModal({
|
||||
>
|
||||
{loading ? (
|
||||
<>
|
||||
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
Zpracování...
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -24,7 +24,7 @@ export default class ErrorBoundary extends Component {
|
||||
<line x1="12" y1="17" x2="12.01" y2="17" />
|
||||
</svg>
|
||||
</div>
|
||||
<p style={{ marginBottom: '0.5rem' }}>Něco se pokazilo při načítání stránky.</p>
|
||||
<p className="mb-2">Něco se pokazilo při načítání stránky.</p>
|
||||
{import.meta.env.DEV && this.state.error && (
|
||||
<pre className="admin-error-stack">
|
||||
{this.state.error.message}
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function OfferItemsSection({
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.2 }}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
|
||||
<div className="flex-between mb-4">
|
||||
<div>
|
||||
<h3 className="admin-card-title" style={{ margin: 0 }}>Položky</h3>
|
||||
{itemsError && <span className="admin-form-error">{itemsError}</span>}
|
||||
@@ -49,7 +49,7 @@ export default function OfferItemsSection({
|
||||
className="offers-template-menu-item"
|
||||
onClick={() => addItemFromTemplate(t)}
|
||||
>
|
||||
<div style={{ fontWeight: 500 }}>{t.name}</div>
|
||||
<div className="fw-500">{t.name}</div>
|
||||
{t.default_price > 0 && (
|
||||
<div style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)' }}>
|
||||
{Number(t.default_price).toFixed(2)}
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function OfferScopeSection({
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.3 }}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
|
||||
<div className="flex-between mb-4">
|
||||
<h3 className="admin-card-title" style={{ margin: 0 }}>Rozsah projektu</h3>
|
||||
{!readOnly && (
|
||||
<div style={{ display: 'flex', gap: '0.5rem', position: 'relative' }}>
|
||||
|
||||
@@ -31,7 +31,7 @@ function ProjectTimeStatus({ form, projectLogs }) {
|
||||
|
||||
function ProjectLogRow({ log, index, projectList, onUpdate, onRemove }) {
|
||||
return (
|
||||
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center', marginBottom: '0.5rem' }}>
|
||||
<div className="flex-row gap-2 mb-2">
|
||||
<select
|
||||
value={log.project_id}
|
||||
onChange={(e) => onUpdate(index, 'project_id', e.target.value)}
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function DashActivityFeed({ activities }) {
|
||||
|
||||
return (
|
||||
<div className="admin-card dash-activity-card">
|
||||
<div className="admin-card-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<div className="admin-card-header flex-between">
|
||||
<h2 className="admin-card-title">Audit log</h2>
|
||||
<Link to="/audit-log" className="admin-btn admin-btn-primary admin-btn-sm">Detail →</Link>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@ export default function DashAttendanceToday({ attendance }) {
|
||||
|
||||
return (
|
||||
<div className="admin-card dash-attendance-card">
|
||||
<div className="admin-card-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<div className="admin-card-header flex-between">
|
||||
<h2 className="admin-card-title">Docházka dnes</h2>
|
||||
<Link to="/attendance/admin" className="admin-btn admin-btn-primary admin-btn-sm">Detail →</Link>
|
||||
</div>
|
||||
|
||||
@@ -81,7 +81,7 @@ export default function DashProfile({
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.25 }}
|
||||
>
|
||||
<div className="admin-card-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<div className="admin-card-header flex-between">
|
||||
<h2 className="admin-card-title">Váš účet</h2>
|
||||
<button onClick={openEditModal} className="admin-btn admin-btn-secondary admin-btn-sm">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
@@ -113,8 +113,8 @@ export default function DashProfile({
|
||||
|
||||
{/* 2FA Section */}
|
||||
<div style={{ borderTop: '1px solid var(--border-color)', marginTop: '1rem', paddingTop: '1rem' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||
<div className="flex-between">
|
||||
<div className="flex-row-gap">
|
||||
<div style={{
|
||||
width: 36, height: 36, borderRadius: '50%',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
@@ -202,7 +202,7 @@ export default function DashProfile({
|
||||
<div className="admin-modal-body">
|
||||
{backupCodes ? (
|
||||
<div>
|
||||
<div className="admin-role-locked-notice" style={{ marginBottom: '1rem' }}>
|
||||
<div className="admin-role-locked-notice mb-4">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
||||
<line x1="12" y1="9" x2="12" y2="13" /><line x1="12" y1="17" x2="12.01" y2="17" />
|
||||
@@ -241,7 +241,7 @@ export default function DashProfile({
|
||||
</div>
|
||||
)}
|
||||
{totpSecret && (
|
||||
<div style={{ marginBottom: '1rem' }}>
|
||||
<div className="mb-4">
|
||||
<label className="admin-form-label" style={{ fontSize: '0.75rem' }}>Nebo zadejte klíč ručně:</label>
|
||||
<div style={{ padding: '0.5rem 0.75rem', background: 'var(--bg-secondary)', borderRadius: '0.375rem', fontFamily: 'monospace', fontSize: '0.875rem', wordBreak: 'break-all', color: 'var(--text-primary)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '0.5rem' }}>
|
||||
<span>{totpSecret}</span>
|
||||
|
||||
@@ -125,7 +125,7 @@ export default function DashSessions() {
|
||||
{[0, 1, 2].map(i => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
|
||||
</div>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -509,7 +509,7 @@ export default function Attendance() {
|
||||
className="admin-form-textarea"
|
||||
rows={3}
|
||||
/>
|
||||
<div style={{ marginTop: '0.5rem' }}>
|
||||
<div className="mt-2">
|
||||
<button
|
||||
onClick={handleSaveNotes}
|
||||
className="admin-btn admin-btn-secondary admin-btn-sm"
|
||||
@@ -543,7 +543,7 @@ export default function Attendance() {
|
||||
|
||||
{/* Completed Today */}
|
||||
{completedToday.length > 0 && (
|
||||
<div className="admin-card" style={{ marginTop: '1.5rem' }}>
|
||||
<div className="admin-card mt-6">
|
||||
<div className="admin-card-header">
|
||||
<h2 className="admin-card-title">Dnešní dokončené směny</h2>
|
||||
</div>
|
||||
|
||||
@@ -147,8 +147,7 @@ export default function AttendanceAdmin() {
|
||||
|
||||
{/* Filters */}
|
||||
<motion.div
|
||||
className="admin-card"
|
||||
style={{ marginBottom: '1.5rem' }}
|
||||
className="admin-card mb-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.1 }}
|
||||
@@ -181,8 +180,7 @@ export default function AttendanceAdmin() {
|
||||
{/* User Totals */}
|
||||
{Object.keys(data.user_totals).length > 0 && (
|
||||
<motion.div
|
||||
className="admin-grid admin-grid-3"
|
||||
style={{ marginBottom: '1.5rem' }}
|
||||
className="admin-grid admin-grid-3 mb-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.15 }}
|
||||
@@ -190,7 +188,7 @@ export default function AttendanceAdmin() {
|
||||
{Object.entries(data.user_totals).map(([uid, userData]) => (
|
||||
<div key={uid} className="admin-card">
|
||||
<div className="admin-card-body">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.5rem' }}>
|
||||
<div className="flex-row gap-2 mb-2">
|
||||
<span style={{ fontWeight: 600 }}>{userData.name}</span>
|
||||
<span className={`attendance-working-badge ${userData.working ? 'working' : 'finished'}`}>
|
||||
{userData.working ? '✓' : '✗'}
|
||||
@@ -213,7 +211,7 @@ export default function AttendanceAdmin() {
|
||||
)}
|
||||
</div>
|
||||
{userData.fund !== null && (
|
||||
<div style={{ marginTop: '0.5rem' }}>
|
||||
<div className="mt-2">
|
||||
<div className="text-secondary" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', fontSize: '0.8rem' }}>
|
||||
<span>Fond: {userData.worked_hours}h / {userData.fund}h</span>
|
||||
{userData.overtime > 0 && (
|
||||
|
||||
@@ -297,7 +297,7 @@ export default function AttendanceBalances() {
|
||||
const yf = getYearFundTotals(userId)
|
||||
return (
|
||||
<tr key={userId}>
|
||||
<td style={{ fontWeight: 500 }}>{balance.name}</td>
|
||||
<td className="fw-500">{balance.name}</td>
|
||||
<td className="admin-mono">{balance.vacation_total}</td>
|
||||
<td className="admin-mono">{balance.vacation_used.toFixed(1)}</td>
|
||||
<td className="admin-mono">
|
||||
@@ -355,9 +355,9 @@ export default function AttendanceBalances() {
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.2 }}
|
||||
style={{ marginTop: '1.5rem' }}
|
||||
className="mt-6"
|
||||
>
|
||||
<h2 className="admin-page-title" style={{ fontSize: '1.25rem', marginBottom: '1rem' }}>
|
||||
<h2 className="admin-page-title mb-4" style={{ fontSize: '1.25rem' }}>
|
||||
Měsíční přehled fondu {year}
|
||||
</h2>
|
||||
<div className="admin-grid admin-grid-3">
|
||||
@@ -437,7 +437,7 @@ export default function AttendanceBalances() {
|
||||
)}
|
||||
|
||||
{fundLoading && (
|
||||
<div style={{ marginTop: '1.5rem' }}>
|
||||
<div className="mt-6">
|
||||
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
|
||||
{[0, 1, 2].map(i => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
@@ -456,9 +456,9 @@ export default function AttendanceBalances() {
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.3 }}
|
||||
style={{ marginTop: '1.5rem' }}
|
||||
className="mt-6"
|
||||
>
|
||||
<h2 className="admin-page-title" style={{ fontSize: '1.25rem', marginBottom: '1rem' }}>
|
||||
<h2 className="admin-page-title mb-4" style={{ fontSize: '1.25rem' }}>
|
||||
Měsíční přehled projektů {year}
|
||||
</h2>
|
||||
<div className="admin-grid admin-grid-3">
|
||||
@@ -555,7 +555,7 @@ export default function AttendanceBalances() {
|
||||
)}
|
||||
|
||||
{projectLoading && (
|
||||
<div style={{ marginTop: '1.5rem' }}>
|
||||
<div className="mt-6">
|
||||
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
|
||||
{[0, 1, 2].map(i => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
|
||||
@@ -235,8 +235,7 @@ export default function AttendanceHistory() {
|
||||
|
||||
{/* Filters */}
|
||||
<motion.div
|
||||
className="admin-card"
|
||||
style={{ marginBottom: '1.5rem' }}
|
||||
className="admin-card mb-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.1 }}
|
||||
@@ -256,8 +255,7 @@ export default function AttendanceHistory() {
|
||||
|
||||
{/* Monthly Fund Card */}
|
||||
<motion.div
|
||||
className="admin-card"
|
||||
style={{ marginBottom: '1.5rem' }}
|
||||
className="admin-card mb-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.15 }}
|
||||
@@ -267,7 +265,7 @@ export default function AttendanceHistory() {
|
||||
<div className="admin-skeleton" style={{ gap: '0.5rem' }}>
|
||||
<div className="admin-skeleton-row" style={{ gap: '1rem' }}>
|
||||
<div className="admin-skeleton-line" style={{ width: '48px', height: '48px', borderRadius: '12px', flexShrink: 0 }} />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-full" style={{ height: '6px', borderRadius: '3px' }} />
|
||||
<div className="admin-skeleton-line w-1/3" style={{ height: '10px', marginTop: '0.5rem' }} />
|
||||
|
||||
@@ -254,8 +254,7 @@ export default function AttendanceLocation() {
|
||||
href={`https://www.google.com/maps?q=${record.arrival_lat},${record.arrival_lng}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="admin-btn admin-btn-secondary admin-btn-sm"
|
||||
style={{ marginTop: '0.5rem' }}
|
||||
className="admin-btn admin-btn-secondary admin-btn-sm mt-2"
|
||||
>
|
||||
Otevřít v Google Maps
|
||||
</a>
|
||||
@@ -287,8 +286,7 @@ export default function AttendanceLocation() {
|
||||
href={`https://www.google.com/maps?q=${record.departure_lat},${record.departure_lng}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="admin-btn admin-btn-secondary admin-btn-sm"
|
||||
style={{ marginTop: '0.5rem' }}
|
||||
className="admin-btn admin-btn-secondary admin-btn-sm mt-2"
|
||||
>
|
||||
Otevřít v Google Maps
|
||||
</a>
|
||||
|
||||
@@ -195,7 +195,7 @@ export default function AuditLog() {
|
||||
<div className="admin-skeleton-line" style={{ width: '80px' }} />
|
||||
<div className="admin-skeleton-line" style={{ width: '70px', borderRadius: '10px' }} />
|
||||
<div className="admin-skeleton-line" style={{ width: '80px' }} />
|
||||
<div className="admin-skeleton-line" style={{ flex: 1 }} />
|
||||
<div className="admin-skeleton-line flex-1" />
|
||||
<div className="admin-skeleton-line" style={{ width: '90px' }} />
|
||||
</div>
|
||||
))}
|
||||
@@ -290,11 +290,10 @@ export default function AuditLog() {
|
||||
)}
|
||||
|
||||
<motion.div
|
||||
className="admin-card"
|
||||
className="admin-card mb-4"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.1 }}
|
||||
style={{ marginBottom: '1rem' }}
|
||||
>
|
||||
<div className="admin-card-body">
|
||||
<div className="admin-form-row admin-form-row-5">
|
||||
@@ -399,7 +398,7 @@ export default function AuditLog() {
|
||||
{!loading && logs.map((log) => (
|
||||
<tr key={log.id}>
|
||||
<td className="admin-mono">{formatDatetime(log.created_at)}</td>
|
||||
<td style={{ fontWeight: 500 }}>{log.username || '-'}</td>
|
||||
<td className="fw-500">{log.username || '-'}</td>
|
||||
<td>
|
||||
<span className={`admin-badge ${ACTION_BADGE_CLASS[log.action] || 'admin-badge-secondary'}`}>
|
||||
{ACTION_LABELS[log.action] || log.action}
|
||||
|
||||
@@ -323,7 +323,7 @@ export default function CompanySettings() {
|
||||
|
||||
function renderBankButtonContent() {
|
||||
if (bankSaving) {
|
||||
return <><div className="admin-spinner" style={{ width: 14, height: 14, borderWidth: 2 }} />Ukládání...</>
|
||||
return <><div className="admin-spinner admin-spinner-sm" />Ukládání...</>
|
||||
}
|
||||
if (editingBank !== null) return 'Uložit změny'
|
||||
return (
|
||||
@@ -351,7 +351,7 @@ export default function CompanySettings() {
|
||||
<button onClick={handleSave} className="admin-btn admin-btn-primary" disabled={saving}>
|
||||
{saving ? (
|
||||
<>
|
||||
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
Ukládání...
|
||||
</>
|
||||
) : 'Uložit nastavení'}
|
||||
@@ -569,7 +569,7 @@ export default function CompanySettings() {
|
||||
<td className="admin-mono">{acc.iban}</td>
|
||||
<td className="admin-mono">{acc.bic}</td>
|
||||
<td>{acc.currency}</td>
|
||||
<td style={{ textAlign: 'center' }}>
|
||||
<td className="text-center">
|
||||
{acc.is_default ? (
|
||||
<span className="text-accent fw-600">✓</span>
|
||||
) : '–'}
|
||||
@@ -780,7 +780,7 @@ export default function CompanySettings() {
|
||||
<label className="admin-btn admin-btn-secondary" style={{ cursor: 'pointer' }}>
|
||||
{uploadingLogo ? (
|
||||
<>
|
||||
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
Nahrávání...
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -226,7 +226,7 @@ export default function Dashboard() {
|
||||
style={{ border: '2px solid var(--danger)', background: 'var(--danger-light)' }}
|
||||
>
|
||||
<div className="admin-card-body" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '1rem', flexWrap: 'wrap' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||
<div className="flex-row-gap">
|
||||
<div style={{
|
||||
width: 40, height: 40, borderRadius: '50%',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
@@ -306,7 +306,7 @@ export default function Dashboard() {
|
||||
<div className="dash-right-col">
|
||||
{dashData?.projects && (
|
||||
<div className="admin-card">
|
||||
<div className="admin-card-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<div className="admin-card-header flex-between">
|
||||
<h2 className="admin-card-title">Aktivní projekty</h2>
|
||||
<Link to="/projects" className="admin-btn admin-btn-primary admin-btn-sm">Vše →</Link>
|
||||
</div>
|
||||
@@ -326,7 +326,7 @@ export default function Dashboard() {
|
||||
|
||||
{dashData?.offers && (
|
||||
<div className="admin-card">
|
||||
<div className="admin-card-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<div className="admin-card-header flex-between">
|
||||
<h2 className="admin-card-title">Nabídky</h2>
|
||||
<Link to="/offers" className="admin-btn admin-btn-primary admin-btn-sm">Zobrazit →</Link>
|
||||
</div>
|
||||
|
||||
@@ -420,7 +420,7 @@ export default function InvoiceCreate() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
||||
<div className="flex-row gap-4">
|
||||
<Link to="/invoices" className="admin-btn-icon" title="Zpět" aria-label="Zpět">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M19 12H5M12 19l-7-7 7-7" />
|
||||
@@ -446,7 +446,7 @@ export default function InvoiceCreate() {
|
||||
<button onClick={handleSubmit} className="admin-btn admin-btn-primary" disabled={saving}>
|
||||
{saving ? (
|
||||
<>
|
||||
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
Ukládání...
|
||||
</>
|
||||
) : 'Uložit'}
|
||||
@@ -602,7 +602,7 @@ export default function InvoiceCreate() {
|
||||
</select>
|
||||
</FormField>
|
||||
<FormField label="DPH">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||
<div className="flex-row-gap">
|
||||
<label className="admin-form-checkbox" style={{ whiteSpace: 'nowrap' }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -642,7 +642,7 @@ export default function InvoiceCreate() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.2 }}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
|
||||
<div className="flex-between mb-4">
|
||||
<div>
|
||||
<h3 className="admin-card-title" style={{ margin: 0 }}>Položky</h3>
|
||||
{errors.items && <span className="admin-form-error">{errors.items}</span>}
|
||||
@@ -684,15 +684,14 @@ export default function InvoiceCreate() {
|
||||
<td style={{ width: '2rem' }}>
|
||||
<DragHandle listeners={listeners} attributes={attributes} />
|
||||
</td>
|
||||
<td className="text-tertiary" style={{ textAlign: 'center', fontWeight: 500 }}>{index + 1}</td>
|
||||
<td className="text-tertiary text-center fw-500">{index + 1}</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
value={item.description}
|
||||
onChange={(e) => updateItem(index, 'description', e.target.value)}
|
||||
className="admin-form-input"
|
||||
className="admin-form-input fw-500"
|
||||
placeholder="Popis položky..."
|
||||
style={{ fontWeight: 500 }}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -254,11 +254,11 @@ export default function InvoiceDetail() {
|
||||
return (
|
||||
<div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}>
|
||||
<div className="admin-skeleton-row" style={{ justifyContent: 'space-between' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||
<div className="flex-row-gap">
|
||||
<div className="admin-skeleton-line" style={{ width: '32px', height: '32px', borderRadius: '8px' }} />
|
||||
<div className="admin-skeleton-line h-8" style={{ width: '200px' }} />
|
||||
</div>
|
||||
<div className="admin-skeleton-row" style={{ gap: '0.5rem' }}>
|
||||
<div className="admin-skeleton-row gap-2">
|
||||
<div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
|
||||
<div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
|
||||
</div>
|
||||
@@ -277,9 +277,9 @@ export default function InvoiceDetail() {
|
||||
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
|
||||
{[0, 1, 2].map(i => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-full" /></div>
|
||||
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-3/4" /></div>
|
||||
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-1/2" /></div>
|
||||
<div className="flex-1"><div className="admin-skeleton-line w-full" /></div>
|
||||
<div className="flex-1"><div className="admin-skeleton-line w-3/4" /></div>
|
||||
<div className="flex-1"><div className="admin-skeleton-line w-1/2" /></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -302,14 +302,14 @@ export default function InvoiceDetail() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
||||
<div className="flex-row gap-4">
|
||||
<Link to="/invoices" className="admin-btn-icon" title="Zpět" aria-label="Zpět">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M19 12H5M12 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</Link>
|
||||
<div>
|
||||
<h1 className="admin-page-title" style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||
<h1 className="admin-page-title flex-row-gap">
|
||||
Faktura {invoice.invoice_number}
|
||||
<span className={`admin-badge ${STATUS_CLASSES[invoice.status] || ''}`}>
|
||||
{STATUS_LABELS[invoice.status] || invoice.status}
|
||||
@@ -326,7 +326,7 @@ export default function InvoiceDetail() {
|
||||
>
|
||||
{pdfLoading ? (
|
||||
<>
|
||||
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
PDF...
|
||||
</>
|
||||
) : (
|
||||
@@ -376,9 +376,9 @@ export default function InvoiceDetail() {
|
||||
>
|
||||
<h3 className="admin-card-title">Informace</h3>
|
||||
<div className="admin-form">
|
||||
<div className="offers-form-row-3" style={{ marginBottom: '0.5rem' }}>
|
||||
<div className="offers-form-row-3 mb-2">
|
||||
<FormField label="Zákazník">
|
||||
<div style={{ fontWeight: 500 }}>{invoice.customer_name || '—'}</div>
|
||||
<div className="fw-500">{invoice.customer_name || '—'}</div>
|
||||
{invoice.customer && (
|
||||
<div className="text-tertiary" style={{ fontSize: '0.8rem', marginTop: '0.2rem' }}>
|
||||
{invoice.customer.company_id && `IČ: ${invoice.customer.company_id}`}
|
||||
@@ -399,7 +399,7 @@ export default function InvoiceDetail() {
|
||||
<div>{invoice.currency}</div>
|
||||
</FormField>
|
||||
</div>
|
||||
<div className="offers-form-row-3" style={{ marginBottom: '0.5rem' }}>
|
||||
<div className="offers-form-row-3 mb-2">
|
||||
<FormField label="Datum vystavení">
|
||||
<div>{formatDate(invoice.issue_date)}</div>
|
||||
</FormField>
|
||||
@@ -424,7 +424,7 @@ export default function InvoiceDetail() {
|
||||
</FormField>
|
||||
</div>
|
||||
{invoice.paid_date && (
|
||||
<div className="admin-form-row" style={{ marginTop: '0.5rem' }}>
|
||||
<div className="admin-form-row mt-2">
|
||||
<FormField label="Datum úhrady">
|
||||
<div style={{ color: 'var(--success)', fontWeight: 500 }}>{formatDate(invoice.paid_date)}</div>
|
||||
</FormField>
|
||||
@@ -440,11 +440,11 @@ export default function InvoiceDetail() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.2 }}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
|
||||
<div className="flex-between mb-4">
|
||||
<h3 className="admin-card-title" style={{ margin: 0 }}>Položky</h3>
|
||||
{isDraft && hasPermission('invoices.edit') && (
|
||||
editingItems ? (
|
||||
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
||||
<div className="flex-row gap-2">
|
||||
<button type="button" onClick={addEditItem} className="admin-btn admin-btn-secondary admin-btn-sm">
|
||||
+ Přidat položku
|
||||
</button>
|
||||
@@ -487,9 +487,8 @@ export default function InvoiceDetail() {
|
||||
type="text"
|
||||
value={item.description}
|
||||
onChange={(e) => updateEditItem(index, 'description', e.target.value)}
|
||||
className="admin-form-input"
|
||||
className="admin-form-input fw-500"
|
||||
placeholder="Popis položky..."
|
||||
style={{ fontWeight: 500 }}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
@@ -578,10 +577,10 @@ export default function InvoiceDetail() {
|
||||
return (
|
||||
<tr key={item.id || index}>
|
||||
<td className="text-tertiary" style={{ textAlign: 'center', fontWeight: 500 }}>{index + 1}</td>
|
||||
<td style={{ fontWeight: 500 }}>{item.description || '—'}</td>
|
||||
<td className="fw-500">{item.description || '—'}</td>
|
||||
<td style={{ textAlign: 'center' }}>{item.quantity} {item.unit && <span className="text-tertiary">{item.unit}</span>}</td>
|
||||
<td style={{ textAlign: 'center' }}>{item.unit || '—'}</td>
|
||||
<td className="admin-mono" style={{ textAlign: 'right' }}>{formatCurrency(item.unit_price, invoice.currency)}</td>
|
||||
<td className="admin-mono text-right">{formatCurrency(item.unit_price, invoice.currency)}</td>
|
||||
<td style={{ textAlign: 'center' }}>{Number(invoice.apply_vat) ? Number(item.vat_rate) : 0}%</td>
|
||||
<td className="admin-mono" style={{ textAlign: 'right', fontWeight: 600 }}>{formatCurrency(lineSubtotal + lineVat, invoice.currency)}</td>
|
||||
</tr>
|
||||
@@ -644,7 +643,7 @@ export default function InvoiceDetail() {
|
||||
/>
|
||||
</Suspense>
|
||||
{hasPermission('invoices.edit') && (
|
||||
<div style={{ marginTop: '0.5rem' }}>
|
||||
<div className="mt-2">
|
||||
<button
|
||||
onClick={handleSaveNotes}
|
||||
className="admin-btn admin-btn-secondary admin-btn-sm"
|
||||
|
||||
@@ -313,7 +313,7 @@ export default function Invoices() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="offers-tabs" style={{ marginBottom: '1rem', justifyContent: 'center' }}>
|
||||
<div className="offers-tabs mb-4" style={{ justifyContent: 'center' }}>
|
||||
<button className={`offers-tab ${activeTab === 'issued' ? 'active' : ''}`} onClick={() => setActiveTab('issued')}>
|
||||
Vydané
|
||||
</button>
|
||||
@@ -427,7 +427,7 @@ export default function Invoices() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.2 }}
|
||||
>
|
||||
<div className="offers-tabs" style={{ marginBottom: '1.5rem' }}>
|
||||
<div className="offers-tabs mb-6">
|
||||
{STATUS_FILTERS.map(f => (
|
||||
<button
|
||||
key={f.value}
|
||||
@@ -448,7 +448,7 @@ export default function Invoices() {
|
||||
transition={{ duration: 0.4, delay: 0.25 }}
|
||||
>
|
||||
<div className="admin-card-body">
|
||||
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}>
|
||||
<div className="admin-search-bar mb-4">
|
||||
<input
|
||||
type="text"
|
||||
value={search}
|
||||
@@ -494,7 +494,7 @@ export default function Invoices() {
|
||||
<th style={{ cursor: 'pointer' }} onClick={() => handleSort('due_date')}>
|
||||
Splatnost <SortIcon column="due_date" sort={activeSort} order={order} />
|
||||
</th>
|
||||
<th style={{ textAlign: 'right' }}>Celkem</th>
|
||||
<th className="text-right">Celkem</th>
|
||||
<th>Akce</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -179,8 +179,8 @@ export default function LeaveApproval() {
|
||||
{[0, 1, 2, 3, 4].map(i => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/3 mb-2" />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
<div className="admin-skeleton-line w-1/4" />
|
||||
@@ -217,7 +217,7 @@ export default function LeaveApproval() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.1 }}
|
||||
>
|
||||
<div className="offers-tabs" style={{ marginBottom: '1.5rem' }}>
|
||||
<div className="offers-tabs mb-6">
|
||||
<button
|
||||
className={`offers-tab ${activeTab === 'pending' ? 'active' : ''}`}
|
||||
onClick={() => setActiveTab('pending')}
|
||||
@@ -249,7 +249,7 @@ export default function LeaveApproval() {
|
||||
<div className="admin-card">
|
||||
<div className="admin-card-body">
|
||||
<div className="admin-empty-state">
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="text-muted" style={{ marginBottom: '1rem' }}>
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="text-muted mb-4">
|
||||
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
||||
<polyline points="22 4 12 14.01 9 11.01" />
|
||||
</svg>
|
||||
@@ -263,8 +263,8 @@ export default function LeaveApproval() {
|
||||
<div key={req.id} className="admin-card">
|
||||
<div className="admin-card-body" style={{ padding: '1.25rem' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', flexWrap: 'wrap', gap: '1rem' }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '0.5rem' }}>
|
||||
<div className="flex-1">
|
||||
<div className="flex-row-gap mb-2">
|
||||
<strong style={{ fontSize: '1rem' }}>{req.employee_name}</strong>
|
||||
<span className={`attendance-leave-badge ${leaveTypeClasses[req.leave_type] || ''}`}>
|
||||
{leaveTypeLabels[req.leave_type] || req.leave_type}
|
||||
@@ -413,7 +413,7 @@ export default function LeaveApproval() {
|
||||
</div>
|
||||
<div className="admin-modal-body">
|
||||
{rejectModal.request && (
|
||||
<p className="text-secondary" style={{ marginBottom: '1rem' }}>
|
||||
<p className="text-secondary mb-4">
|
||||
{rejectModal.request.employee_name} — {leaveTypeLabels[rejectModal.request.leave_type]},{' '}
|
||||
{formatDate(rejectModal.request.date_from)} — {formatDate(rejectModal.request.date_to)} ({rejectModal.request.total_days} dnů)
|
||||
</p>
|
||||
|
||||
@@ -106,40 +106,40 @@ export default function LeaveRequests() {
|
||||
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/3 mb-2" />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
<div className="admin-skeleton-line w-1/4" />
|
||||
</div>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/2 mb-2" />
|
||||
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
|
||||
</div>
|
||||
<div className="admin-skeleton-line w-1/4" />
|
||||
</div>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="admin-skeleton-line w-3/4" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-3/4 mb-2" />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
<div className="admin-skeleton-line w-1/4" />
|
||||
</div>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/2 mb-2" />
|
||||
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
|
||||
</div>
|
||||
<div className="admin-skeleton-line w-1/4" />
|
||||
</div>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/3 mb-2" />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
<div className="admin-skeleton-line w-1/4" />
|
||||
|
||||
@@ -163,11 +163,11 @@ export default function OfferDetail() {
|
||||
return (
|
||||
<div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}>
|
||||
<div className="admin-skeleton-row" style={{ justifyContent: 'space-between' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||
<div className="flex-row-gap">
|
||||
<div className="admin-skeleton-line" style={{ width: '32px', height: '32px', borderRadius: '8px' }} />
|
||||
<div className="admin-skeleton-line h-8" style={{ width: '200px' }} />
|
||||
</div>
|
||||
<div className="admin-skeleton-row" style={{ gap: '0.5rem' }}>
|
||||
<div className="admin-skeleton-row gap-2">
|
||||
<div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
|
||||
<div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
|
||||
</div>
|
||||
@@ -186,9 +186,9 @@ export default function OfferDetail() {
|
||||
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
|
||||
{[0, 1, 2].map(i => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-full" /></div>
|
||||
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-3/4" /></div>
|
||||
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-1/2" /></div>
|
||||
<div className="flex-1"><div className="admin-skeleton-line w-full" /></div>
|
||||
<div className="flex-1"><div className="admin-skeleton-line w-3/4" /></div>
|
||||
<div className="flex-1"><div className="admin-skeleton-line w-1/2" /></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -216,7 +216,7 @@ export default function OfferDetail() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
||||
<div className="flex-row gap-4">
|
||||
<Link to="/offers" className="admin-btn-icon" title="Zpět" aria-label="Zpět">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M19 12H5M12 19l-7-7 7-7" />
|
||||
@@ -246,7 +246,7 @@ export default function OfferDetail() {
|
||||
<button onClick={handlePdf} className="admin-btn admin-btn-secondary" disabled={pdfLoading}>
|
||||
{pdfLoading ? (
|
||||
<>
|
||||
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
PDF...
|
||||
</>
|
||||
) : (
|
||||
@@ -293,7 +293,7 @@ export default function OfferDetail() {
|
||||
<button onClick={handleSave} className="admin-btn admin-btn-primary" disabled={saving}>
|
||||
{saving ? (
|
||||
<>
|
||||
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
Ukládání...
|
||||
</>
|
||||
) : 'Uložit'}
|
||||
@@ -410,14 +410,13 @@ export default function OfferDetail() {
|
||||
|
||||
<div className="offers-form-row-3">
|
||||
<FormField label="Sazba DPH (%)">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||
<div className="flex-row-gap">
|
||||
<input
|
||||
type="number"
|
||||
value={form.vat_rate}
|
||||
onChange={(e) => updateForm('vat_rate', parseFloat(e.target.value) || 0)}
|
||||
className="admin-form-input"
|
||||
className="admin-form-input flex-1"
|
||||
step="0.1"
|
||||
style={{ flex: 1 }}
|
||||
readOnly={isInvalidated}
|
||||
/>
|
||||
<label className="admin-form-checkbox" style={{ whiteSpace: 'nowrap' }}>
|
||||
@@ -514,7 +513,7 @@ export default function OfferDetail() {
|
||||
</FormField>
|
||||
<FormField label="Příloha (PDF)">
|
||||
{orderAttachment ? (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
||||
<div className="flex-row gap-2">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent-color)" strokeWidth="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
<polyline points="14 2 14 8 20 8" />
|
||||
|
||||
@@ -211,7 +211,7 @@ export default function Offers() {
|
||||
{[0, 1, 2, 3, 4].map(i => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -267,7 +267,7 @@ export default function Offers() {
|
||||
transition={{ duration: 0.4, delay: 0.1 }}
|
||||
>
|
||||
<div className="admin-card-body">
|
||||
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}>
|
||||
<div className="admin-search-bar mb-4">
|
||||
<input
|
||||
type="text"
|
||||
value={search}
|
||||
@@ -315,7 +315,7 @@ export default function Offers() {
|
||||
<th style={{ cursor: 'pointer' }} onClick={() => handleSort('currency')}>
|
||||
Měna <SortIcon column="currency" sort={activeSort} order={order} />
|
||||
</th>
|
||||
<th style={{ textAlign: 'right' }}>Celkem</th>
|
||||
<th className="text-right">Celkem</th>
|
||||
<th>Akce</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -395,7 +395,7 @@ export default function Offers() {
|
||||
{q.currency}
|
||||
</span>
|
||||
</td>
|
||||
<td className="admin-mono" style={{ textAlign: 'right', fontWeight: 500 }}>
|
||||
<td className="admin-mono text-right fw-500">
|
||||
{formatCurrency(q.total, q.currency)}
|
||||
</td>
|
||||
<td>
|
||||
@@ -578,7 +578,7 @@ export default function Offers() {
|
||||
</FormField>
|
||||
<FormField label="Příloha (PDF)">
|
||||
{orderAttachment ? (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
||||
<div className="flex-row gap-2">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent-color)" strokeWidth="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
<polyline points="14 2 14 8 20 8" />
|
||||
|
||||
@@ -244,7 +244,7 @@ export default function OffersCustomers() {
|
||||
{[0, 1, 2, 3, 4].map(i => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -289,7 +289,7 @@ export default function OffersCustomers() {
|
||||
transition={{ duration: 0.4, delay: 0.1 }}
|
||||
>
|
||||
<div className="admin-card-body">
|
||||
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}>
|
||||
<div className="admin-search-bar mb-4">
|
||||
<input
|
||||
type="text"
|
||||
value={search}
|
||||
@@ -606,7 +606,7 @@ export default function OffersCustomers() {
|
||||
<button type="button" onClick={handleSubmit} className="admin-btn admin-btn-primary" disabled={saving}>
|
||||
{saving && (
|
||||
<>
|
||||
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
Ukládání...
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -151,7 +151,7 @@ function ItemTemplatesTab() {
|
||||
{[0, 1, 2, 3, 4].map(i => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -171,7 +171,7 @@ function ItemTemplatesTab() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.1 }}
|
||||
>
|
||||
<div className="admin-card-header" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div className="admin-card-header flex-between">
|
||||
<h3 className="admin-card-title">Šablony položek ({templates.length})</h3>
|
||||
<button onClick={openCreate} className="admin-btn admin-btn-primary admin-btn-sm">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
@@ -199,7 +199,7 @@ function ItemTemplatesTab() {
|
||||
<tbody>
|
||||
{templates.map((t) => (
|
||||
<tr key={t.id}>
|
||||
<td style={{ fontWeight: 500 }}>{t.name}</td>
|
||||
<td className="fw-500">{t.name}</td>
|
||||
<td style={{ color: 'var(--text-secondary)' }}>{t.description || '—'}</td>
|
||||
<td>{Number(t.default_price).toFixed(2)}</td>
|
||||
<td style={{ color: 'var(--text-secondary)' }}>{t.category || '—'}</td>
|
||||
@@ -258,7 +258,7 @@ function ItemTemplatesTab() {
|
||||
<div className="admin-modal-footer">
|
||||
<button type="button" onClick={() => setShowModal(false)} className="admin-btn admin-btn-secondary" disabled={saving}>Zrušit</button>
|
||||
<button type="button" onClick={handleSubmit} className="admin-btn admin-btn-primary" disabled={saving}>
|
||||
{saving && (<><div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />Ukládání...</>)}
|
||||
{saving && (<><div className="admin-spinner admin-spinner-sm" />Ukládání...</>)}
|
||||
{!saving && (editingTemplate ? 'Uložit' : 'Vytvořit')}
|
||||
</button>
|
||||
</div>
|
||||
@@ -424,7 +424,7 @@ function ScopeTemplatesTab() {
|
||||
{[0, 1, 2, 3, 4].map(i => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -444,7 +444,7 @@ function ScopeTemplatesTab() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.1 }}
|
||||
>
|
||||
<div className="admin-card-header" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div className="admin-card-header flex-between">
|
||||
<h3 className="admin-card-title">Šablony rozsahu ({templates.length})</h3>
|
||||
<button onClick={openCreate} className="admin-btn admin-btn-primary admin-btn-sm">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
@@ -469,7 +469,7 @@ function ScopeTemplatesTab() {
|
||||
<tbody>
|
||||
{templates.map((t) => (
|
||||
<tr key={t.id}>
|
||||
<td style={{ fontWeight: 500 }}>{t.name}</td>
|
||||
<td className="fw-500">{t.name}</td>
|
||||
<td>
|
||||
<div className="admin-table-actions">
|
||||
<button onClick={() => openEdit(t)} className="admin-btn-icon" title="Upravit" aria-label="Upravit">
|
||||
@@ -511,7 +511,7 @@ function ScopeTemplatesTab() {
|
||||
</FormField>
|
||||
|
||||
<div className="admin-form-group">
|
||||
<label className="admin-form-label" style={{ marginBottom: '0.5rem' }}>Sekce</label>
|
||||
<label className="admin-form-label mb-2">Sekce</label>
|
||||
<div className="offers-scope-list">
|
||||
{form.sections.map((section, index) => (
|
||||
<div key={section._key} className="offers-scope-section">
|
||||
@@ -564,7 +564,7 @@ function ScopeTemplatesTab() {
|
||||
<div className="admin-modal-footer">
|
||||
<button type="button" onClick={() => setShowModal(false)} className="admin-btn admin-btn-secondary" disabled={saving}>Zrušit</button>
|
||||
<button type="button" onClick={handleSubmit} className="admin-btn admin-btn-primary" disabled={saving}>
|
||||
{saving && (<><div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />Ukládání...</>)}
|
||||
{saving && (<><div className="admin-spinner admin-spinner-sm" />Ukládání...</>)}
|
||||
{!saving && (editingTemplate ? 'Uložit' : 'Vytvořit')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -217,11 +217,11 @@ export default function OrderDetail() {
|
||||
return (
|
||||
<div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}>
|
||||
<div className="admin-skeleton-row" style={{ justifyContent: 'space-between' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||
<div className="flex-row-gap">
|
||||
<div className="admin-skeleton-line" style={{ width: '32px', height: '32px', borderRadius: '8px' }} />
|
||||
<div className="admin-skeleton-line h-8" style={{ width: '200px' }} />
|
||||
</div>
|
||||
<div className="admin-skeleton-row" style={{ gap: '0.5rem' }}>
|
||||
<div className="admin-skeleton-row gap-2">
|
||||
<div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
|
||||
<div className="admin-skeleton-line h-10" style={{ width: '100px', borderRadius: '8px' }} />
|
||||
</div>
|
||||
@@ -240,9 +240,9 @@ export default function OrderDetail() {
|
||||
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
|
||||
{[0, 1, 2].map(i => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-full" /></div>
|
||||
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-3/4" /></div>
|
||||
<div style={{ flex: 1 }}><div className="admin-skeleton-line w-1/2" /></div>
|
||||
<div className="flex-1"><div className="admin-skeleton-line w-full" /></div>
|
||||
<div className="flex-1"><div className="admin-skeleton-line w-3/4" /></div>
|
||||
<div className="flex-1"><div className="admin-skeleton-line w-1/2" /></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -262,14 +262,14 @@ export default function OrderDetail() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
||||
<div className="flex-row gap-4">
|
||||
<Link to="/orders" className="admin-btn-icon" title="Zpět" aria-label="Zpět">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M19 12H5M12 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</Link>
|
||||
<div>
|
||||
<h1 className="admin-page-title" style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||
<h1 className="admin-page-title flex-row-gap">
|
||||
{editingNumber ? (
|
||||
<span style={{ display: 'inline-flex', alignItems: 'center', gap: '0.5rem' }}>
|
||||
Objednávka
|
||||
@@ -345,7 +345,7 @@ export default function OrderDetail() {
|
||||
disabled={statusChanging === status}
|
||||
>
|
||||
{statusChanging === status ? (
|
||||
<div className="admin-spinner" style={{ width: 14, height: 14, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
) : (
|
||||
TRANSITION_LABELS[status] || status
|
||||
)}
|
||||
@@ -372,7 +372,7 @@ export default function OrderDetail() {
|
||||
>
|
||||
<div className="admin-card-body">
|
||||
<h3 className="admin-card-title">Informace</h3>
|
||||
<div className="admin-form-row" style={{ marginBottom: '0.5rem' }}>
|
||||
<div className="admin-form-row mb-2">
|
||||
<FormField label="Nabídka">
|
||||
<div>
|
||||
<Link to={`/offers/${order.quotation_id}`} className="link-accent">
|
||||
@@ -393,9 +393,9 @@ export default function OrderDetail() {
|
||||
</div>
|
||||
</FormField>
|
||||
</div>
|
||||
<div className="admin-form-row admin-form-row-3" style={{ marginBottom: '0.5rem' }}>
|
||||
<div className="admin-form-row admin-form-row-3 mb-2">
|
||||
<FormField label="Zákazník">
|
||||
<div style={{ fontWeight: 500 }}>{order.customer_name || '—'}</div>
|
||||
<div className="fw-500">{order.customer_name || '—'}</div>
|
||||
</FormField>
|
||||
<FormField label="Číslo obj. zákazníka">
|
||||
<div>{order.customer_order_number || '—'}</div>
|
||||
@@ -404,7 +404,7 @@ export default function OrderDetail() {
|
||||
<div>{order.currency}</div>
|
||||
</FormField>
|
||||
</div>
|
||||
<div className="admin-form-row admin-form-row-3" style={{ marginBottom: '0.5rem' }}>
|
||||
<div className="admin-form-row admin-form-row-3 mb-2">
|
||||
<FormField label="Datum vytvoření">
|
||||
<div>{formatDate(order.created_at)}</div>
|
||||
</FormField>
|
||||
@@ -418,7 +418,7 @@ export default function OrderDetail() {
|
||||
disabled={attachmentLoading}
|
||||
>
|
||||
{attachmentLoading ? (
|
||||
<div className="admin-spinner" style={{ width: 14, height: 14, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
) : (
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
@@ -464,7 +464,7 @@ export default function OrderDetail() {
|
||||
<tr key={item.id || index}>
|
||||
<td style={{ color: 'var(--text-tertiary)', textAlign: 'center', fontWeight: 500 }}>{index + 1}</td>
|
||||
<td>
|
||||
<div style={{ fontWeight: 500 }}>{item.description || '—'}</div>
|
||||
<div className="fw-500">{item.description || '—'}</div>
|
||||
{item.item_description && (
|
||||
<div style={{ fontSize: '0.8rem', color: 'var(--text-tertiary)', marginTop: '0.25rem' }}>{item.item_description}</div>
|
||||
)}
|
||||
@@ -561,7 +561,7 @@ export default function OrderDetail() {
|
||||
/>
|
||||
</FormField>
|
||||
{hasPermission('orders.edit') && (
|
||||
<div style={{ marginTop: '0.5rem' }}>
|
||||
<div className="mt-2">
|
||||
<button
|
||||
onClick={handleSaveNotes}
|
||||
className="admin-btn admin-btn-secondary admin-btn-sm"
|
||||
|
||||
@@ -83,7 +83,7 @@ export default function Orders() {
|
||||
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -91,7 +91,7 @@ export default function Orders() {
|
||||
</div>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -99,7 +99,7 @@ export default function Orders() {
|
||||
</div>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-3/4" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -107,7 +107,7 @@ export default function Orders() {
|
||||
</div>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -115,7 +115,7 @@ export default function Orders() {
|
||||
</div>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -151,7 +151,7 @@ export default function Orders() {
|
||||
transition={{ duration: 0.4, delay: 0.1 }}
|
||||
>
|
||||
<div className="admin-card-body">
|
||||
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}>
|
||||
<div className="admin-search-bar mb-4">
|
||||
<input
|
||||
type="text"
|
||||
value={search}
|
||||
@@ -191,7 +191,7 @@ export default function Orders() {
|
||||
<th style={{ cursor: 'pointer' }} onClick={() => handleSort('created_at')}>
|
||||
Datum <SortIcon column="created_at" sort={activeSort} order={order} />
|
||||
</th>
|
||||
<th style={{ textAlign: 'right' }}>Celkem</th>
|
||||
<th className="text-right">Celkem</th>
|
||||
<th>Akce</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -217,7 +217,7 @@ export default function Orders() {
|
||||
<td className="admin-mono">
|
||||
{formatDate(o.created_at)}
|
||||
</td>
|
||||
<td className="admin-mono" style={{ textAlign: 'right', fontWeight: 500 }}>
|
||||
<td className="admin-mono text-right fw-500">
|
||||
{formatCurrency(o.total, o.currency)}
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -158,7 +158,7 @@ export default function ProjectCreate() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
||||
<div className="flex-row gap-4">
|
||||
<Link to="/projects" className="admin-btn-icon" title="Zpět" aria-label="Zpět">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M19 12H5M12 19l-7-7 7-7" />
|
||||
|
||||
@@ -210,7 +210,7 @@ export default function ProjectDetail() {
|
||||
return (
|
||||
<div className="admin-skeleton" style={{ padding: 0, gap: '1.5rem' }}>
|
||||
<div className="admin-skeleton-row" style={{ justifyContent: 'space-between' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||
<div className="flex-row-gap">
|
||||
<div className="admin-skeleton-line" style={{ width: '32px', height: '32px', borderRadius: '8px' }} />
|
||||
<div className="admin-skeleton-line h-8" style={{ width: '200px' }} />
|
||||
</div>
|
||||
@@ -273,7 +273,7 @@ export default function ProjectDetail() {
|
||||
<button onClick={handleSave} className="admin-btn admin-btn-primary" disabled={saving}>
|
||||
{saving ? (
|
||||
<>
|
||||
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
Ukládání...
|
||||
</>
|
||||
) : 'Uložit'}
|
||||
@@ -380,7 +380,7 @@ export default function ProjectDetail() {
|
||||
<h3 className="admin-card-title">Poznámky</h3>
|
||||
|
||||
{/* Add note */}
|
||||
<div style={{ marginBottom: '1rem' }}>
|
||||
<div className="mb-4">
|
||||
<textarea
|
||||
value={newNote}
|
||||
onChange={(e) => setNewNote(e.target.value)}
|
||||
@@ -394,14 +394,14 @@ export default function ProjectDetail() {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div style={{ marginTop: '0.5rem' }}>
|
||||
<div className="mt-2">
|
||||
<button
|
||||
onClick={handleAddNote}
|
||||
className="admin-btn admin-btn-secondary admin-btn-sm"
|
||||
disabled={addingNote || !newNote.trim()}
|
||||
>
|
||||
{addingNote ? (
|
||||
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
) : (
|
||||
'Přidat poznámku'
|
||||
)}
|
||||
@@ -452,7 +452,7 @@ export default function ProjectDetail() {
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: '0.5rem' }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.25rem' }}>
|
||||
<span style={{ fontWeight: 600, fontSize: '0.85rem' }}>
|
||||
{note.user_name}
|
||||
|
||||
@@ -78,7 +78,7 @@ export default function Projects() {
|
||||
<div className="admin-skeleton" style={{ gap: '1.25rem' }}>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -86,7 +86,7 @@ export default function Projects() {
|
||||
</div>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -94,7 +94,7 @@ export default function Projects() {
|
||||
</div>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-3/4" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -102,7 +102,7 @@ export default function Projects() {
|
||||
</div>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/2" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/3" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -110,7 +110,7 @@ export default function Projects() {
|
||||
</div>
|
||||
<div className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
@@ -155,7 +155,7 @@ export default function Projects() {
|
||||
transition={{ duration: 0.4, delay: 0.1 }}
|
||||
>
|
||||
<div className="admin-card-body">
|
||||
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}>
|
||||
<div className="admin-search-bar mb-4">
|
||||
<input
|
||||
type="text"
|
||||
value={search}
|
||||
@@ -210,7 +210,7 @@ export default function Projects() {
|
||||
{p.project_number}
|
||||
</Link>
|
||||
</td>
|
||||
<td style={{ fontWeight: 500 }}>{p.name || '—'}</td>
|
||||
<td className="fw-500">{p.name || '—'}</td>
|
||||
<td>{p.customer_name || '—'}</td>
|
||||
<td>
|
||||
<span className={`admin-badge ${STATUS_CLASSES[p.status] || ''}`}>
|
||||
@@ -242,7 +242,7 @@ export default function Projects() {
|
||||
disabled={deletingId === p.id}
|
||||
>
|
||||
{deletingId === p.id ? (
|
||||
<div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
) : (
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<polyline points="3 6 5 6 21 6" />
|
||||
|
||||
@@ -369,7 +369,7 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
||||
const renderKpi = () => {
|
||||
if (!hasLoadedOnce.current && statsLoading) {
|
||||
return (
|
||||
<div className="dash-kpi-grid dash-kpi-4" style={{ marginBottom: '1.5rem' }}>
|
||||
<div className="dash-kpi-grid dash-kpi-4 mb-6">
|
||||
{[0, 1, 2, 3].map(i => (
|
||||
<div key={i} className="admin-stat-card">
|
||||
<div className="admin-skeleton-line" style={{ width: '60%', height: '11px', marginBottom: '0.5rem' }} />
|
||||
@@ -456,7 +456,7 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
||||
transition={{ duration: 0.4, delay: 0.15 }}
|
||||
>
|
||||
<div className="admin-card-body">
|
||||
<div className="admin-search-bar" style={{ marginBottom: '1rem' }}>
|
||||
<div className="admin-search-bar mb-4">
|
||||
<input
|
||||
type="text"
|
||||
value={search}
|
||||
@@ -549,7 +549,7 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
||||
</td>
|
||||
<td className="admin-mono">{formatDate(inv.issue_date)}</td>
|
||||
<td className="admin-mono">{formatDate(inv.due_date)}</td>
|
||||
<td className="admin-mono" style={{ textAlign: 'right', fontWeight: 500 }}>
|
||||
<td className="admin-mono text-right fw-500">
|
||||
{formatCurrency(inv.amount, inv.currency)}
|
||||
</td>
|
||||
<td>
|
||||
@@ -615,7 +615,7 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
||||
<h2 className="admin-modal-title">Nahrát přijaté faktury</h2>
|
||||
</div>
|
||||
<div className="admin-modal-body">
|
||||
<div style={{ marginBottom: '1rem' }}>
|
||||
<div className="mb-4">
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
|
||||
@@ -276,8 +276,8 @@ export default function Settings() {
|
||||
{[0, 1, 2, 3, 4].map(i => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="admin-skeleton-line w-1/3" style={{ marginBottom: '0.5rem' }} />
|
||||
<div className="flex-1">
|
||||
<div className="admin-skeleton-line w-1/3 mb-2" />
|
||||
<div className="admin-skeleton-line w-1/4" style={{ height: '10px' }} />
|
||||
</div>
|
||||
<div className="admin-skeleton-line w-1/4" />
|
||||
@@ -306,7 +306,7 @@ export default function Settings() {
|
||||
|
||||
function renderRoleButtonContent() {
|
||||
if (saving) {
|
||||
return <><div className="admin-spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />Ukládání...</>
|
||||
return <><div className="admin-spinner admin-spinner-sm" />Ukládání...</>
|
||||
}
|
||||
return editingRole ? 'Uložit změny' : 'Vytvořit roli'
|
||||
}
|
||||
@@ -347,7 +347,7 @@ export default function Settings() {
|
||||
</div>
|
||||
<div className="admin-card-body">
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '1rem' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||
<div className="flex-row-gap">
|
||||
<div style={{
|
||||
width: 36, height: 36, borderRadius: '50%',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user