96 lines
5.4 KiB
TypeScript
96 lines
5.4 KiB
TypeScript
// app/admin/YouthAttendanceRow.tsx
|
|
|
|
"use client";
|
|
|
|
import { CheckCircle, ClipboardCheck, Edit3, Undo } from 'lucide-react';
|
|
import Image from 'next/image';
|
|
import React from 'react';
|
|
import falconIcon from '../assets/falcon.svg';
|
|
import porpoiseIcon from '../assets/porpoise.svg';
|
|
import { formatTimeHHMM, parseTimeInput, type Youth } from './adminTypes';
|
|
|
|
interface YouthAttendanceRowProps {
|
|
youth: Youth;
|
|
entry: any;
|
|
currentDate: string;
|
|
shiftId: 'MORNING' | 'AFTERNOON';
|
|
team: 'PF' | 'TU';
|
|
actualDuration: number;
|
|
onMarkStandard: () => void;
|
|
setManualAttendance: (date: string, youthId: string, shiftId: 'MORNING' | 'AFTERNOON', status: 'Absent' | 'Late' | 'Present', hours: number, note?: string) => void;
|
|
removeAttendanceEntry: (date: string, youthId: string, shiftId: 'MORNING' | 'AFTERNOON') => void;
|
|
}
|
|
|
|
export const YouthAttendanceRow: React.FC<YouthAttendanceRowProps> = ({
|
|
youth,
|
|
entry,
|
|
currentDate,
|
|
shiftId,
|
|
team,
|
|
actualDuration,
|
|
onMarkStandard,
|
|
setManualAttendance,
|
|
removeAttendanceEntry
|
|
}) => {
|
|
// Logic moved inside the component
|
|
const isExtra = youth.team !== team;
|
|
const isPending = entry?.status === 'Pending';
|
|
const isCompleted = !!entry && !isPending;
|
|
|
|
return (
|
|
<div className={`flex flex-col md:flex-row md:justify-between md:items-center p-3 rounded-2xl border transition-colors ${isCompleted ? 'bg-moss/5 border-moss/20' : (isPending ? 'bg-goldenrod/5 border-goldenrod/30' : 'bg-white border-slate-teal/5 shadow-sm')}`}>
|
|
<div className="flex items-center gap-3 mb-3 md:mb-0">
|
|
{isCompleted ? <CheckCircle size={20} className="text-moss shrink-0" /> : <div className="w-5 h-5 rounded-full border-2 border-slate-teal/20 shrink-0 bg-white"></div>}
|
|
<div className={`flex items-center justify-center w-6 h-6 rounded-full ${youth.team === 'PF' ? 'bg-gold' : 'bg-seafoam'} shrink-0 shadow-inner`}>
|
|
<Image src={youth.team === 'PF' ? falconIcon : porpoiseIcon} alt={youth.team === 'PF' ? 'PF' : 'TU'} width={12} height={12} />
|
|
</div>
|
|
<span className={`font-bold text-base ${isCompleted ? 'text-moss' : 'text-ebony'}`}>
|
|
{youth.name} {isExtra && <span className="ml-2 text-[10px] text-slate-teal bg-slate-teal/10 px-2 py-0.5 rounded-lg uppercase tracking-widest">Extra pass</span>}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-end gap-3 w-full md:w-auto flex-wrap">
|
|
{isPending && <span className="text-[10px] font-black uppercase tracking-widest bg-goldenrod/10 text-goldenrod px-3 py-1.5 rounded-xl">Väntar...</span>}
|
|
{isCompleted && (
|
|
<span className="text-xs font-black uppercase tracking-widest bg-white text-moss px-3 py-1.5 rounded-xl border border-moss/10 shadow-sm">
|
|
{entry.status === 'Absent' ? entry.note : `${formatTimeHHMM(entry.hoursWorked)} (+${entry.weightedHours.toFixed(1)}h)`}
|
|
</span>
|
|
)}
|
|
|
|
{(!entry || isPending) && (
|
|
<>
|
|
<button onClick={onMarkStandard} className="bg-slate-teal/10 text-slate-teal hover:bg-slate-teal hover:text-white px-3 py-1.5 rounded-xl font-black uppercase tracking-widest text-[10px] flex items-center gap-1.5 transition-colors shadow-sm">
|
|
<ClipboardCheck size={14} /> Hela passet
|
|
</button>
|
|
<button onClick={() => {
|
|
const input = prompt(`Timmar arbetade:`, formatTimeHHMM(actualDuration));
|
|
const hrs = input ? parseTimeInput(input) : 0;
|
|
if (hrs > 0) setManualAttendance(currentDate, youth.id, shiftId, 'Present', hrs);
|
|
}} className="bg-goldenrod/10 text-goldenrod hover:bg-goldenrod hover:text-white p-1.5 rounded-xl transition-colors shadow-sm">
|
|
<Edit3 size={16} />
|
|
</button>
|
|
<select
|
|
value=""
|
|
onChange={(e) => {
|
|
if (!e.target.value) return;
|
|
let reason = e.target.value;
|
|
if (reason === 'Custom') reason = prompt('Ange anledning:') || 'Frånvarande';
|
|
setManualAttendance(currentDate, youth.id, shiftId, 'Absent', 0, reason);
|
|
}}
|
|
className="bg-goldenrod/10 text-goldenrod px-2 py-1.5 rounded-xl font-black uppercase tracking-widest text-[10px] outline-none shadow-sm"
|
|
>
|
|
<option value="">+ Frånvaro</option>
|
|
<option value="Sjuk">Sjuk</option><option value="Uteblev">Uteblev</option><option value="Ledig">Ledig</option><option value="Custom">Annan...</option>
|
|
</select>
|
|
</>
|
|
)}
|
|
|
|
{entry && (
|
|
<button onClick={() => removeAttendanceEntry(currentDate, youth.id, shiftId)} className="text-emergency/60 hover:text-emergency p-1.5 bg-white rounded-xl border border-emergency/10 transition-colors shadow-sm">
|
|
<Undo size={16} />
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}; |