Files
app/dist/assets/AttendanceHistory-Bs2_8Kg7.js
Simon 5550358b15 fix: oprava kritických bezpečnostních chyb a bugů z code review
- SEC-1: nahrazen exec('fsutil') za PHP-native is_link()+realpath() v NasFileManager - eliminace command injection
- SEC-2: přidáno ověření aktuálního hesla při změně hesla (profile.php + DashProfile.jsx)
- BUG-1: attendance punch obalen do transakce s SELECT FOR UPDATE - prevence race condition při dvojkliku
- BUG-2: eliminován N+1 SQL dotaz pro VAT v invoice listu - výpočet přesunut do subquery
- BUG-5/6: delete a update attendance záznamů obaleny do transakcí - prevence nekonzistentního stavu
- BUG-7: opravena duplikace nabídky - přidáno chybějící pole unit v offer items

ESLint: 0 errors | PHPCS: 0 errors | Build: OK

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 13:46:20 +01:00

89 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import{j as e,m as f}from"./vendor-animation-0s3FMHwK.js";import{r as m}from"./vendor-react-BVs3cwbi.js";import{a9 as T}from"./vendor-utils-Dyr8OjFr.js";import{a as C,u as A,c as O,F as B,A as H}from"./index-CCZhiEoc.js";import{F as I}from"./Forbidden-D25jV3Oq.js";import{c as W,b as k,g as w,d as z,e as S,a as v,h as E,i as y,f as b}from"./attendanceHelpers-D6sLEw0q.js";const L="/api/admin",R=s=>s.break_start&&s.break_end?`${b(s.break_start)} - ${b(s.break_end)}`:s.break_start?`${b(s.break_start)} - ?`:"—",Z=s=>s.project_logs&&s.project_logs.length>0?e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"0.125rem"},children:s.project_logs.map((n,g)=>{let d,c,o=!1;if(n.hours!==null&&n.hours!==void 0)d=parseInt(n.hours)||0,c=parseInt(n.minutes)||0;else{o=!n.ended_at;const x=n.ended_at?new Date(n.ended_at):new Date,p=Math.floor((x-new Date(n.started_at))/6e4);d=Math.floor(p/60),c=p%60}return e.jsxs("span",{className:"admin-badge",style:{fontSize:"0.7rem",display:"inline-block",background:o?"var(--accent-light)":void 0},children:[n.project_name||`#${n.project_id}`," (",d,":",String(c).padStart(2,"0"),"h",o?" ▸":"",")"]},n.id||g)})}):s.project_name?e.jsx("span",{className:"admin-badge admin-badge-wrap",style:{fontSize:"0.75rem"},children:s.project_name}):"—",Y=s=>s.overtime>0?e.jsxs("span",{className:"leave-badge badge-overtime",children:["+",s.overtime,"h přesčas"]}):s.remaining>0?e.jsxs("span",{style:{color:"#dc2626"},children:["",s.remaining,"h"]}):e.jsx("span",{style:{color:"#16a34a"},children:"splněno"});function Q(){const s=C(),{user:n,hasPermission:g}=A(),[d,c]=m.useState(!0),o=m.useRef(null),[x,p]=m.useState(()=>{const a=new Date;return`${a.getFullYear()}-${String(a.getMonth()+1).padStart(2,"0")}`}),[t,D]=m.useState({records:[],month_name:"",year:new Date().getFullYear(),total_minutes:0,vacation_hours:0,sick_hours:0,holiday_hours:0,unpaid_hours:0,leave_balance:null,monthly_fund:null}),_=m.useCallback(async()=>{c(!0);try{const a=await O(`${L}/attendance.php?action=history&month=${x}`);if(a.status===401)return;const i=await a.json();i.success&&D(i.data)}catch{s.error("Nepodařilo se načíst data")}finally{c(!1)}},[x,s]);if(m.useEffect(()=>{_()},[_]),!g("attendance.history"))return e.jsx(I,{});const $=()=>{if(!o.current)return;const a=window.open("","_blank");a.document.write(`
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Docházka - ${t.month_name}</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-size: 11px;
line-height: 1.4;
color: #000;
background: #fff;
padding: 15mm;
}
.print-wrapper-table { width: 100%; border-collapse: collapse; border: none; }
.print-wrapper-table > thead > tr > td,
.print-wrapper-table > tbody > tr > td { padding: 0; border: none; background: none; }
.print-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid #333;
}
.print-header-left { display: flex; align-items: center; gap: 12px; }
.print-logo { height: 40px; width: auto; }
.print-header-text { text-align: left; }
.print-header-right { text-align: right; }
.print-header h1 { font-size: 18px; font-weight: 700; margin-bottom: 3px; }
.print-header .company { font-size: 11px; color: #666; }
.print-header .period { font-size: 13px; font-weight: 600; color: #333; margin-bottom: 2px; }
.print-header .filters { font-size: 10px; color: #666; }
.print-header .generated { font-size: 9px; color: #888; margin-top: 5px; }
.user-section { margin-bottom: 25px; page-break-inside: avoid; }
.user-header {
background: #f5f5f5;
border: 1px solid #ddd;
padding: 10px 15px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.user-header h3 { font-size: 13px; font-weight: 600; }
.user-header .total { font-size: 12px; font-weight: 600; }
.leave-summary {
margin-top: 10px;
padding: 8px 15px;
background: #f9f9f9;
border: 1px solid #ddd;
font-size: 10px;
}
.user-section table { width: 100%; border-collapse: collapse; margin-bottom: 15px; }
.user-section th, .user-section td { border: 1px solid #333; padding: 6px 8px; text-align: left; }
.user-section th { background: #333; color: #fff; font-weight: 600; font-size: 10px; text-transform: uppercase; }
.user-section td { font-size: 10px; }
.user-section tr:nth-child(even) { background: #f9f9f9; }
.text-center { text-align: center; }
.text-right { text-align: right; }
.user-section tfoot td { background: #eee; font-weight: 600; }
.leave-badge {
display: inline-block;
padding: 2px 6px;
border-radius: 3px;
font-size: 9px;
font-weight: 500;
}
.badge-vacation { background: #dbeafe; color: #1d4ed8; }
.badge-sick { background: #fee2e2; color: #dc2626; }
.badge-holiday { background: #dcfce7; color: #16a34a; }
.badge-unpaid { background: #f3f4f6; color: #6b7280; }
.badge-overtime { background: #fef3c7; color: #d97706; }
@media print {
body { padding: 0; margin: 0; }
@page { size: A4 portrait; margin: 10mm; }
.user-section { page-break-inside: avoid; }
}
</style>
</head>
<body>
${T.sanitize(o.current.innerHTML)}
</body>
</html>
`),a.document.close(),a.onload=()=>{a.print()}};return e.jsxs("div",{children:[e.jsxs(f.div,{className:"admin-page-header",initial:{opacity:0,y:20},animate:{opacity:1,y:0},transition:{duration:.4},children:[e.jsxs("div",{children:[e.jsx("h1",{className:"admin-page-title",children:"Historie docházky"}),e.jsx("p",{className:"admin-page-subtitle",children:t.month_name})]}),e.jsx("div",{className:"admin-page-actions",children:t.records.length>0&&e.jsxs("button",{onClick:$,className:"admin-btn admin-btn-secondary",title:"Tisk docházky",children:[e.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",style:{marginRight:"0.5rem"},children:[e.jsx("polyline",{points:"6 9 6 2 18 2 18 9"}),e.jsx("path",{d:"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"}),e.jsx("rect",{x:"6",y:"14",width:"12",height:"8"})]}),"Tisk"]})})]}),e.jsx(f.div,{className:"admin-card mb-6",initial:{opacity:0,y:20},animate:{opacity:1,y:0},transition:{duration:.4,delay:.1},children:e.jsx("div",{className:"admin-card-body",children:e.jsx("div",{className:"admin-form-row",children:e.jsx(B,{label:"Měsíc",children:e.jsx(H,{mode:"month",value:x,onChange:a=>p(a)})})})})}),e.jsx(f.div,{className:"admin-card mb-6",initial:{opacity:0,y:20},animate:{opacity:1,y:0},transition:{duration:.4,delay:.15},children:e.jsxs("div",{className:"admin-card-body",children:[d&&e.jsx("div",{className:"admin-skeleton",style:{gap:"0.5rem"},children:e.jsxs("div",{className:"admin-skeleton-row",style:{gap:"1rem"},children:[e.jsx("div",{className:"admin-skeleton-line",style:{width:"48px",height:"48px",borderRadius:"12px",flexShrink:0}}),e.jsxs("div",{className:"flex-1",children:[e.jsx("div",{className:"admin-skeleton-line w-1/2",style:{marginBottom:"0.5rem"}}),e.jsx("div",{className:"admin-skeleton-line w-full",style:{height:"6px",borderRadius:"3px"}}),e.jsx("div",{className:"admin-skeleton-line w-1/3",style:{height:"10px",marginTop:"0.5rem"}})]})]})}),!d&&t.monthly_fund&&e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"1rem",flexWrap:"wrap"},children:[e.jsx("div",{className:"admin-stat-icon info",children:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[e.jsx("rect",{x:"3",y:"4",width:"18",height:"18",rx:"2",ry:"2"}),e.jsx("line",{x1:"16",y1:"2",x2:"16",y2:"6"}),e.jsx("line",{x1:"8",y1:"2",x2:"8",y2:"6"}),e.jsx("line",{x1:"3",y1:"10",x2:"21",y2:"10"})]})}),e.jsxs("div",{style:{flex:1,minWidth:"200px"},children:[e.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"baseline",marginBottom:"0.375rem"},children:[e.jsxs("span",{style:{fontWeight:600,fontSize:"1rem",color:"var(--text-primary)"},children:["Fond: ",t.monthly_fund.worked,"h / ",t.monthly_fund.fund,"h"]}),e.jsxs("span",{className:"text-secondary",style:{fontSize:"0.8125rem"},children:[t.monthly_fund.business_days," prac. dnů"]})]}),e.jsx("div",{className:"attendance-balance-bar",children:e.jsx("div",{className:"attendance-balance-progress",style:{width:`${Math.min(100,t.monthly_fund.fund>0?t.monthly_fund.covered/t.monthly_fund.fund*100:0)}%`,background:t.monthly_fund.covered>=t.monthly_fund.fund?"linear-gradient(135deg, var(--success), #059669)":"var(--gradient)"}})}),e.jsxs("div",{className:"text-muted",style:{display:"flex",justifyContent:"space-between",fontSize:"0.75rem",marginTop:"0.375rem"},children:[e.jsxs("span",{children:["Pokryto: ",t.monthly_fund.covered,"h (práce ",t.monthly_fund.worked,"h",t.vacation_hours>0&&` + dovolená ${t.vacation_hours}h`,t.sick_hours>0&&` + nemoc ${t.sick_hours}h`,t.holiday_hours>0&&` + svátek ${t.holiday_hours}h`,t.unpaid_hours>0&&` + neplacené ${t.unpaid_hours}h`,")"]}),t.monthly_fund.overtime>0?e.jsxs("span",{className:"text-warning fw-600",children:["Přesčas: +",t.monthly_fund.overtime,"h"]}):e.jsxs("span",{children:["Zbývá: ",t.monthly_fund.remaining,"h"]})]})]})]}),!d&&!t.monthly_fund&&e.jsx("div",{className:"text-muted",style:{fontSize:"0.875rem",textAlign:"center",padding:"0.5rem 0"},children:"Fond měsíce není k dispozici"})]})}),e.jsx(f.div,{className:"admin-card",initial:{opacity:0,y:20},animate:{opacity:1,y:0},transition:{duration:.4,delay:.2},children:e.jsxs("div",{className:"admin-card-body",children:[d&&e.jsx("div",{className:"admin-skeleton",style:{gap:"1.25rem"},children:[0,1,2,3,4].map(a=>e.jsxs("div",{className:"admin-skeleton-row",children:[e.jsx("div",{className:"admin-skeleton-line w-1/4"}),e.jsx("div",{className:"admin-skeleton-line w-1/3"}),e.jsx("div",{className:"admin-skeleton-line w-1/4"})]},a))}),!d&&t.records.length===0&&e.jsx("div",{className:"admin-empty-state",children:e.jsx("p",{children:"Za tento měsíc nejsou žádné záznamy."})}),!d&&t.records.length>0&&e.jsx("div",{className:"admin-table-responsive",children:e.jsxs("table",{className:"admin-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Datum"}),e.jsx("th",{children:"Typ"}),e.jsx("th",{children:"Příchod"}),e.jsx("th",{children:"Pauza"}),e.jsx("th",{children:"Odchod"}),e.jsx("th",{children:"Hodiny"}),e.jsx("th",{children:"Projekty"}),e.jsx("th",{children:"Poznámka"})]})}),e.jsx("tbody",{children:t.records.map(a=>{const i=a.leave_type||"work",l=i!=="work",h=l?(a.leave_hours||8)*60:W(a);return e.jsxs("tr",{children:[e.jsx("td",{className:"admin-mono",children:k(a.shift_date)}),e.jsx("td",{children:e.jsx("span",{className:`attendance-leave-badge ${z(i)}`,children:w(i)})}),e.jsx("td",{className:"admin-mono",children:l?"—":S(a.arrival_time)}),e.jsx("td",{className:"admin-mono",children:l?"—":R(a)}),e.jsx("td",{className:"admin-mono",children:l?"—":S(a.departure_time)}),e.jsx("td",{className:"admin-mono",children:h>0?v(h,!0):"—"}),e.jsx("td",{children:Z(a)}),e.jsx("td",{style:{maxWidth:"150px",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:a.notes||""})]},a.id)})})]})})]})}),t.records.length>0&&e.jsx("div",{ref:o,style:{display:"none"},children:e.jsxs("table",{className:"print-wrapper-table",children:[e.jsx("thead",{children:e.jsx("tr",{children:e.jsx("td",{children:e.jsxs("div",{className:"print-header",children:[e.jsxs("div",{className:"print-header-left",children:[e.jsx("img",{src:"/images/logo-light.png",alt:"BOHA",className:"print-logo"}),e.jsxs("div",{className:"print-header-text",children:[e.jsx("h1",{children:"EVIDENCE DOCHÁZKY"}),e.jsx("div",{className:"company",children:"BOHA Automation s.r.o."})]})]}),e.jsxs("div",{className:"print-header-right",children:[e.jsx("div",{className:"period",children:t.month_name}),e.jsxs("div",{className:"filters",children:["Zaměstnanec: ",n?.fullName||""]}),e.jsxs("div",{className:"generated",children:["Vygenerováno: ",new Date().toLocaleString("cs-CZ")]})]})]})})})}),e.jsx("tbody",{children:e.jsx("tr",{children:e.jsx("td",{children:e.jsxs("div",{className:"user-section",children:[e.jsxs("div",{className:"user-header",children:[e.jsx("h3",{children:n?.fullName||""}),e.jsxs("span",{className:"total",children:["Odpracováno: ",v(t.total_minutes,!0)]})]}),t.leave_balance&&e.jsxs("div",{className:"leave-summary",children:[e.jsxs("strong",{children:["Dovolená ",t.year,":"]})," Zbývá ",t.leave_balance.vacation_remaining.toFixed(1),"h z ",t.leave_balance.vacation_total,"h",t.vacation_hours>0&&e.jsxs(e.Fragment,{children:[" | ",e.jsxs("span",{className:"leave-badge badge-vacation",children:["Tento měsíc: ",t.vacation_hours,"h"]})]}),t.sick_hours>0&&e.jsxs(e.Fragment,{children:[" | ",e.jsxs("span",{className:"leave-badge badge-sick",children:["Nemoc: ",t.sick_hours,"h"]})]}),t.holiday_hours>0&&e.jsxs(e.Fragment,{children:[" | ",e.jsxs("span",{className:"leave-badge badge-holiday",children:["Svátek: ",t.holiday_hours,"h"]})]}),t.monthly_fund?.overtime>0&&e.jsxs(e.Fragment,{children:[" | ",e.jsxs("span",{className:"leave-badge badge-overtime",children:["Přesčas: +",t.monthly_fund.overtime,"h"]})]})]}),e.jsxs("table",{children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{style:{width:"70px"},children:"Datum"}),e.jsx("th",{style:{width:"70px"},children:"Typ"}),e.jsx("th",{className:"text-center",style:{width:"70px"},children:"Příchod"}),e.jsx("th",{className:"text-center",style:{width:"90px"},children:"Pauza"}),e.jsx("th",{className:"text-center",style:{width:"70px"},children:"Odchod"}),e.jsx("th",{className:"text-center",style:{width:"80px"},children:"Hodiny"}),e.jsx("th",{children:"Projekty"}),e.jsx("th",{children:"Poznámka"})]})}),e.jsx("tbody",{children:[...t.records].sort((a,i)=>a.shift_date.localeCompare(i.shift_date)).map(a=>{const i=a.leave_type||"work",l=i!=="work",h=E(a),P=Math.floor(h/60),F=h%60;return e.jsxs("tr",{children:[e.jsx("td",{children:k(a.shift_date)}),e.jsx("td",{children:e.jsx("span",{className:`leave-badge ${z(i)}`,children:w(i)})}),e.jsx("td",{className:"text-center",children:l?"—":y(a.arrival_time,a.shift_date)}),e.jsx("td",{className:"text-center",children:l||!a.break_start||!a.break_end?"—":`${y(a.break_start,a.shift_date)} - ${y(a.break_end,a.shift_date)}`}),e.jsx("td",{className:"text-center",children:l?"—":y(a.departure_time,a.shift_date)}),e.jsx("td",{className:"text-center",children:h>0?`${P}:${String(F).padStart(2,"0")}`:"—"}),e.jsx("td",{style:{fontSize:"8px"},children:a.project_logs&&a.project_logs.length>0?a.project_logs.map((r,M)=>{let u,j;if(r.hours!==null&&r.hours!==void 0)u=parseInt(r.hours)||0,j=parseInt(r.minutes)||0;else if(r.started_at&&r.ended_at){const N=Math.max(0,Math.floor((new Date(r.ended_at)-new Date(r.started_at))/6e4));u=Math.floor(N/60),j=N%60}else u=0,j=0;return e.jsxs("div",{children:[r.project_name||`#${r.project_id}`," (",u,":",String(j).padStart(2,"0"),"h)"]},r.id||M)}):a.project_name||"—"}),e.jsx("td",{children:a.notes||""})]},a.id)})}),e.jsxs("tfoot",{children:[e.jsxs("tr",{children:[e.jsx("td",{colSpan:6,className:"text-right",children:"Odpracováno:"}),e.jsx("td",{className:"text-center",children:v(t.total_minutes,!0)}),e.jsx("td",{colSpan:2})]}),t.monthly_fund&&e.jsxs("tr",{children:[e.jsx("td",{colSpan:6,className:"text-right",children:"Fond měsíce:"}),e.jsxs("td",{className:"text-center",children:[t.monthly_fund.covered,"h / ",t.monthly_fund.fund,"h"]}),e.jsx("td",{colSpan:2,children:Y(t.monthly_fund)})]})]})]})]})})})})]})})]})}export{Q as default};