Initial commit: EatMe attendance tracker
Google-SSO PWA for bistro employee clock-in/out, admin employee management, and stats with CSV export. Express + SQLite backend, React + Zustand frontend in the light-mono-tui design language. Multi-stage Dockerfile, compose.yaml for image-based deploys, nginx reverse-proxy template, and an OKF documentation bundle in docs/. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
61
frontend/src/pages/EmployeeApp.tsx
Normal file
61
frontend/src/pages/EmployeeApp.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { useEffect } from 'react';
|
||||
import useAttendanceStore from '../store/attendanceStore';
|
||||
import { ClockControls } from '../components/ClockControls';
|
||||
import { StatusBadge } from '../components/StatusBadge';
|
||||
import { StatsSummary } from '../components/StatsSummary';
|
||||
import { SessionList } from '../components/SessionList';
|
||||
|
||||
export function EmployeeApp() {
|
||||
const status = useAttendanceStore((s) => s.status);
|
||||
const busy = useAttendanceStore((s) => s.busy);
|
||||
const error = useAttendanceStore((s) => s.error);
|
||||
const live = useAttendanceStore((s) => s.live);
|
||||
const load = useAttendanceStore((s) => s.load);
|
||||
const recordEvent = useAttendanceStore((s) => s.recordEvent);
|
||||
const startPolling = useAttendanceStore((s) => s.startPolling);
|
||||
const stopPolling = useAttendanceStore((s) => s.stopPolling);
|
||||
|
||||
useEffect(() => {
|
||||
load();
|
||||
}, [load]);
|
||||
|
||||
useEffect(() => {
|
||||
if (status === null || status === 'clocked_out') {
|
||||
stopPolling();
|
||||
return;
|
||||
}
|
||||
startPolling();
|
||||
return () => stopPolling();
|
||||
}, [status, startPolling, stopPolling]);
|
||||
|
||||
if (!status || !live) {
|
||||
return <div className="empty-line">> načítám…</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="panel">
|
||||
<p className="panel-title">Docházka</p>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '1rem' }}>
|
||||
<StatusBadge status={status} />
|
||||
</div>
|
||||
<ClockControls status={status} busy={busy} onAction={recordEvent} />
|
||||
{error && <div className="login-error">{error}</div>}
|
||||
</div>
|
||||
|
||||
<div className="panel">
|
||||
<p className="panel-title">Statistiky · tento měsíc</p>
|
||||
<StatsSummary
|
||||
totalWorkedMs={live.totalWorkedMs}
|
||||
totalBreakMs={live.totalBreakMs}
|
||||
shiftCount={live.shiftCount}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="panel">
|
||||
<p className="panel-title">Směny</p>
|
||||
<SessionList sessions={live.sessions} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user