ca-grow-ops-manager/frontend/src/pages/TimeclockPage.tsx
fullsizemalt 6b724386ba
Some checks failed
Deploy to Production / deploy (push) Failing after 0s
Test / backend-test (push) Failing after 0s
Test / frontend-test (push) Failing after 0s
feat: Phase 1 Complete (Backend + Frontend)
2025-12-09 09:24:00 -08:00

95 lines
4.4 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import api from '../lib/api';
export default function TimeclockPage() {
const [logs, setLogs] = useState<any[]>([]);
const [status, setStatus] = useState<'CLOCKED_OUT' | 'CLOCKED_IN'>('CLOCKED_OUT');
useEffect(() => {
fetchLogs();
}, []);
const fetchLogs = async () => {
try {
const { data } = await api.get('/timeclock/logs');
setLogs(data);
// Determine status
const active = data.find((l: any) => !l.endTime);
setStatus(active ? 'CLOCKED_IN' : 'CLOCKED_OUT');
} catch (e) {
console.error(e);
}
};
const handleClock = async (action: 'in' | 'out') => {
try {
if (action === 'in') {
await api.post('/timeclock/clock-in', { activityType: 'General' });
} else {
await api.post('/timeclock/clock-out', {});
}
await fetchLogs();
} catch (e: any) {
alert(e.response?.data?.message || 'Error clocking');
}
};
return (
<div className="space-y-8 max-w-2xl mx-auto">
<header className="text-center">
<h2 className="text-3xl font-bold text-neutral-800">Time Clock</h2>
<p className="text-neutral-500 mt-2">{new Date().toLocaleDateString()} {new Date().toLocaleTimeString()}</p>
</header>
<div className="bg-white p-8 rounded-2xl shadow-lg border border-neutral-200 text-center">
<div className="mb-8 p-4 bg-neutral-50 rounded-lg inline-block">
<span className="text-sm font-bold text-neutral-400 uppercase tracking-widest">Current Status</span>
<div className={`text-2xl font-bold mt-1 ${status === 'CLOCKED_IN' ? 'text-emerald-600' : 'text-neutral-600'}`}>
{status.replace('_', ' ')}
</div>
</div>
<div className="flex gap-4 justify-center">
<button
onClick={() => handleClock('in')}
disabled={status === 'CLOCKED_IN'}
className="w-40 h-40 rounded-full font-bold text-xl flex items-center justify-center transition-all disabled:opacity-50 disabled:cursor-not-allowed bg-emerald-600 text-white hover:bg-emerald-700 hover:scale-105 shadow-xl shadow-emerald-900/20"
>
CLOCK IN
</button>
<button
onClick={() => handleClock('out')}
disabled={status === 'CLOCKED_OUT'}
className="w-40 h-40 rounded-full font-bold text-xl flex items-center justify-center transition-all disabled:opacity-50 disabled:cursor-not-allowed bg-red-600 text-white hover:bg-red-700 hover:scale-105 shadow-xl shadow-red-900/20"
>
CLOCK OUT
</button>
</div>
</div>
<div className="bg-white rounded-xl shadow-sm border border-neutral-200 overflow-hidden">
<h3 className="text-lg font-bold text-neutral-800 p-6 border-b border-neutral-100">Recent Logs</h3>
<table className="w-full text-left text-sm">
<thead className="bg-neutral-50 text-neutral-500">
<tr>
<th className="p-4">Date</th>
<th className="p-4">Start</th>
<th className="p-4">End</th>
<th className="p-4">Activity</th>
</tr>
</thead>
<tbody className="divide-y divide-neutral-100">
{logs.map(log => (
<tr key={log.id}>
<td className="p-4">{new Date(log.startTime).toLocaleDateString()}</td>
<td className="p-4">{new Date(log.startTime).toLocaleTimeString()}</td>
<td className="p-4">{log.endTime ? new Date(log.endTime).toLocaleTimeString() : '-'}</td>
<td className="p-4">{log.activityType}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}