From ec7bfd7e3410d3ed469b61f4bb3eca2450c84ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20S=C3=B6derberg?= Date: Tue, 17 Mar 2026 02:44:24 +0100 Subject: [PATCH] UI tweaks --- app/admin/AttendanceTab.tsx | 318 ++++++++++++------------------------ app/admin/ReportTab.tsx | 184 ++++++++------------- app/admin/SetupTab.tsx | 113 +++++-------- app/admin/page.tsx | 101 ++++-------- app/documents/page.tsx | 59 +++---- app/emergency/page.tsx | 60 +++---- app/faq/page.tsx | 20 +-- app/layout.tsx | 2 +- app/page.tsx | 54 +++--- app/schedule/page.tsx | 93 +++-------- 10 files changed, 352 insertions(+), 652 deletions(-) diff --git a/app/admin/AttendanceTab.tsx b/app/admin/AttendanceTab.tsx index 9e04a43..afc859a 100644 --- a/app/admin/AttendanceTab.tsx +++ b/app/admin/AttendanceTab.tsx @@ -11,76 +11,46 @@ import scheduleData from '../data/schedule.json'; import { type AttendanceDataMap, calculateShiftDuration, formatTimeHHMM, getAttendanceKey, isWeekend, parseTimeInput, type Period, toIsoDate } from './adminTypes'; interface Props { - periods: Period[]; - attendance: AttendanceDataMap; + periods: Period[]; attendance: AttendanceDataMap; setManualAttendance: (date: string, youthId: string, shiftId: 'MORNING' | 'AFTERNOON', status: 'Absent' | 'Late' | 'Present', hours: number, note?: string) => void; bulkSetManualAttendance: (records: any[]) => void; addPendingAttendance: (date: string, youthId: string, shiftId: 'MORNING' | 'AFTERNOON') => void; removeAttendanceEntry: (date: string, youthId: string, shiftId: 'MORNING' | 'AFTERNOON') => void; - activePeriodId: string; - setActivePeriodId: (id: string) => void; + activePeriodId: string; setActivePeriodId: (id: string) => void; } -// NEW: Dynamic Shift Helper export const getTeamShiftInfo = (daySchedule: any, team: 'PF' | 'TU') => { if (!daySchedule) return { time: 'Ledig', shiftId: 'MORNING' as 'MORNING' | 'AFTERNOON' }; - const pfTime = daySchedule.pilgrimsfalkarna?.time || 'Ledig'; const tuTime = daySchedule.tumlarna?.time || 'Ledig'; - const pfStart = pfTime !== 'Ledig' ? parseInt(pfTime.match(/(\d+):/)?.[1] || '99') : 99; const tuStart = tuTime !== 'Ledig' ? parseInt(tuTime.match(/(\d+):/)?.[1] || '99') : 99; - - if (team === 'PF') { - return { time: pfTime, shiftId: (pfStart > tuStart) ? 'AFTERNOON' : 'MORNING' as 'MORNING' | 'AFTERNOON' }; - } else { - return { time: tuTime, shiftId: (tuStart > pfStart) ? 'AFTERNOON' : (pfStart === tuStart ? 'AFTERNOON' : 'MORNING') as 'MORNING' | 'AFTERNOON' }; - } + if (team === 'PF') return { time: pfTime, shiftId: (pfStart > tuStart) ? 'AFTERNOON' : 'MORNING' as 'MORNING' | 'AFTERNOON' }; + else return { time: tuTime, shiftId: (tuStart > pfStart) ? 'AFTERNOON' : (pfStart === tuStart ? 'AFTERNOON' : 'MORNING') as 'MORNING' | 'AFTERNOON' }; }; const getDaysInPeriod = (start: string, end: string) => { - const days = []; - let curr = new Date(start + 'T12:00:00'); - const endDate = new Date(end + 'T12:00:00'); - while (curr <= endDate) { - days.push(toIsoDate(curr)); - curr.setDate(curr.getDate() + 1); - } + const days = []; let curr = new Date(start + 'T12:00:00'); const endDate = new Date(end + 'T12:00:00'); + while (curr <= endDate) { days.push(toIsoDate(curr)); curr.setDate(curr.getDate() + 1); } return days; }; const getDailyCompletionStats = (date: string, period: Period, attendance: AttendanceDataMap) => { const dayNameStr = new Date(date + 'T12:00:00').toLocaleDateString('sv-SE', { weekday: 'long' }).toLowerCase(); const daySchedule = scheduleData.find(d => d.day.toLowerCase() === dayNameStr); - if (!daySchedule) return { expected: 0, completed: 0, isComplete: true, hasWork: false }; - let expected = 0; - let completed = 0; - let hasWork = false; - - // Use dynamic shift checking + let expected = 0; let completed = 0; let hasWork = false; if (daySchedule.pilgrimsfalkarna && daySchedule.pilgrimsfalkarna.time !== 'Ledig') { - hasWork = true; - const { shiftId } = getTeamShiftInfo(daySchedule, 'PF'); - const pfYouth = period.youthList.filter(y => y.team === 'PF'); - expected += pfYouth.length; - pfYouth.forEach(y => { - const entry = attendance[getAttendanceKey(date, y.id, shiftId)]; - if (entry && entry.status !== 'Pending') completed++; - }); + hasWork = true; const { shiftId } = getTeamShiftInfo(daySchedule, 'PF'); + const pfYouth = period.youthList.filter(y => y.team === 'PF'); expected += pfYouth.length; + pfYouth.forEach(y => { const entry = attendance[getAttendanceKey(date, y.id, shiftId)]; if (entry && entry.status !== 'Pending') completed++; }); } if (daySchedule.tumlarna && daySchedule.tumlarna.time !== 'Ledig') { - hasWork = true; - const { shiftId } = getTeamShiftInfo(daySchedule, 'TU'); - const tuYouth = period.youthList.filter(y => y.team === 'TU'); - expected += tuYouth.length; - tuYouth.forEach(y => { - const entry = attendance[getAttendanceKey(date, y.id, shiftId)]; - if (entry && entry.status !== 'Pending') completed++; - }); + hasWork = true; const { shiftId } = getTeamShiftInfo(daySchedule, 'TU'); + const tuYouth = period.youthList.filter(y => y.team === 'TU'); expected += tuYouth.length; + tuYouth.forEach(y => { const entry = attendance[getAttendanceKey(date, y.id, shiftId)]; if (entry && entry.status !== 'Pending') completed++; }); } - return { expected, completed, isComplete: expected > 0 && completed >= expected, hasWork }; }; @@ -91,25 +61,17 @@ export const AttendanceTab: React.FC = ({ periods, attendance, setManualA useEffect(() => { if (activePeriod) { const today = toIsoDate(new Date()); - if (today >= activePeriod.startDate && today <= activePeriod.endDate) { - setCurrentDate(today); - } else { - setCurrentDate(activePeriod.startDate); - } + if (today >= activePeriod.startDate && today <= activePeriod.endDate) setCurrentDate(today); + else setCurrentDate(activePeriod.startDate); } }, [activePeriodId, activePeriod]); if (!activePeriod) return

Ingen period aktiv.

; const changeDate = (days: number) => { - const newDateObj = new Date(currentDate + 'T12:00:00'); - newDateObj.setDate(newDateObj.getDate() + days); - const startObj = new Date(activePeriod.startDate + 'T12:00:00'); - const endObj = new Date(activePeriod.endDate + 'T12:00:00'); - - if (newDateObj >= startObj && newDateObj <= endObj) { - setCurrentDate(toIsoDate(newDateObj)); - } + const newDateObj = new Date(currentDate + 'T12:00:00'); newDateObj.setDate(newDateObj.getDate() + days); + const startObj = new Date(activePeriod.startDate + 'T12:00:00'); const endObj = new Date(activePeriod.endDate + 'T12:00:00'); + if (newDateObj >= startObj && newDateObj <= endObj) setCurrentDate(toIsoDate(newDateObj)); }; const dayNameStr = new Date(currentDate + 'T12:00:00').toLocaleDateString('sv-SE', { weekday: 'long' }); @@ -125,7 +87,6 @@ export const AttendanceTab: React.FC = ({ periods, attendance, setManualA if (actualHours > 0) setManualAttendance(currentDate, youthId, shiftId, 'Present', actualHours); }; - // Sort teams chronologically const teamsToRender = ['PF', 'TU'].sort((a, b) => { const timeA = getTeamShiftInfo(daySchedule, a as 'PF' | 'TU').time; const timeB = getTeamShiftInfo(daySchedule, b as 'PF' | 'TU').time; @@ -136,71 +97,56 @@ export const AttendanceTab: React.FC = ({ periods, attendance, setManualA return (
- {/* Timeline Overview */} -
-
-

Periodöversikt

- setActivePeriodId(e.target.value)} className="bg-white border border-slate-teal/10 py-1.5 px-3 rounded-xl font-bold text-slate-teal text-xs cursor-pointer focus:outline-none shadow-sm"> {periods.map(p => )}
- -
+
{timelineDays.map(day => { const stats = getDailyCompletionStats(day, activePeriod, attendance); - const isPast = day < todayIso; - const isToday = day === todayIso; const isSelected = day === currentDate; - - let bgClass = "bg-white text-ebony border-slate-teal/20"; + let bgClass = "bg-white text-ebony border-slate-teal/10"; if (!stats.hasWork) bgClass = "bg-slate-teal/5 text-ebony/40 border-transparent"; - else if (stats.isComplete) bgClass = "bg-moss text-white border-moss"; - else if (isPast || isToday) bgClass = "bg-goldenrod text-white border-goldenrod"; + else if (stats.isComplete) bgClass = "bg-moss text-white border-moss shadow-inner"; + else if (day < todayIso || day === todayIso) bgClass = "bg-goldenrod text-white border-goldenrod shadow-inner"; return ( - ); })}
- {/* Date Navigation */} -
-
- -
-

{dayNameStr}

-

{currentDate}

-
- +
+ +
+

{dayNameStr}

+

{currentDate}

- - {currentDayStats.hasWork && ( -
- {currentDayStats.isComplete ? <> Dagen är komplett! : `Ifyllt: ${currentDayStats.completed} av ${currentDayStats.expected} pers`} -
- )} +
- {/* Attendance Lists */} + {/* FIX: Status banner is fully restored and green when done! */} + {currentDayStats.hasWork && ( +
+ {currentDayStats.isComplete ? <> All närvaro rapporterad : `Ifyllt: ${currentDayStats.completed} av ${currentDayStats.expected} pers`} +
+ )} + {!daySchedule || !currentDayStats.hasWork ? ( -

Inga schemalagda pass denna dag.

+
+

Inga schemalagda pass denna dag.

+
) : ( teamsToRender.map(teamStr => { const team = teamStr as 'PF' | 'TU'; const { time: standardTime, shiftId } = getTeamShiftInfo(daySchedule, team); - if (standardTime === 'Ledig') return null; const rawDuration = calculateShiftDuration(standardTime); @@ -211,66 +157,40 @@ export const AttendanceTab: React.FC = ({ periods, attendance, setManualA const scheduledYouth = activePeriod.youthList.filter(y => y.team === team); const extraYouth = activePeriod.youthList.filter(y => y.team !== team && attendance[getAttendanceKey(currentDate, y.id, shiftId)]); - - // Display youth sorted by name alphabetically const displayYouth = [...scheduledYouth, ...extraYouth].sort((a, b) => a.name.localeCompare(b.name)); const availableExtras = activePeriod.youthList.filter(y => !displayYouth.some(dy => dy.id === y.id)).sort((a, b) => a.name.localeCompare(b.name)); const handleQuickLogAll = () => { const recordsToUpdate: any[] = []; - displayYouth.forEach(y => { const entry = attendance[getAttendanceKey(currentDate, y.id, shiftId)]; if (!entry || entry.status === 'Pending') { - recordsToUpdate.push({ - date: currentDate, - youthId: y.id, - shiftId: shiftId, - status: 'Present', - hoursWorked: actualDuration, - weightedHours: weightedDuration, - note: '' - }); + recordsToUpdate.push({ date: currentDate, youthId: y.id, shiftId, status: 'Present', hoursWorked: actualDuration, weightedHours: weightedDuration, note: '' }); } }); - if (recordsToUpdate.length > 0) { - bulkSetManualAttendance(recordsToUpdate); - } + if (recordsToUpdate.length > 0) bulkSetManualAttendance(recordsToUpdate); }; return ( -
-
-

-
- {team +
+
+
+
+ {team
- {team === 'PF' ? 'Pilgrimsfalkarna' : 'Tumlarna'} -

- -
-
- - {standardTime} ({formatTimeHHMM(rawDuration)}) - {isWknd && Helg (-30m)} +

{team === 'PF' ? 'Pilgrimsfalkarna' : 'Tumlarna'}

+
+
+
+ {standardTime} ({formatTimeHHMM(rawDuration)})
- -
-
+
{displayYouth.map(youth => { const entry = attendance[getAttendanceKey(currentDate, youth.id, shiftId)]; const isExtra = youth.team !== team; @@ -278,90 +198,68 @@ export const AttendanceTab: React.FC = ({ periods, attendance, setManualA const isCompleted = entry && !isPending; return ( -
- -
- {isCompleted ? :
} - - {youth.name} - {isExtra && Extra pass} +
+
+ {isCompleted ? :
} +
+ {youth.team +
+ + {youth.name} {isExtra && Extra pass}
-
- {isPending && ( - - Väntar på tid... - - )} +
+ {isPending && Väntar...} {isCompleted && ( - - {entry.status === 'Absent' ? entry.note : `${formatTimeHHMM(entry.hoursWorked)} arbetat (+${entry.weightedHours.toFixed(1)}t pott)`} + + {entry.status === 'Absent' ? entry.note : `${formatTimeHHMM(entry.hoursWorked)} (+${entry.weightedHours.toFixed(1)}h)`} )} -
- {(!entry || isPending) && ( - <> - - - - - )} - - {entry && ( - - )} -
+ + + + )} + + {entry && ( + + )}
); })} {availableExtras.length > 0 && ( -
- { if (e.target.value) addPendingAttendance(currentDate, e.target.value, shiftId); }} className="bg-white border border-slate-teal/10 p-3 rounded-2xl font-bold text-slate-teal text-sm w-full outline-none shadow-sm"> + + {availableExtras.map(y => )}
)} diff --git a/app/admin/ReportTab.tsx b/app/admin/ReportTab.tsx index f70ba1b..45a1f97 100644 --- a/app/admin/ReportTab.tsx +++ b/app/admin/ReportTab.tsx @@ -12,11 +12,7 @@ import { type AttendanceDataMap, formatTimeHHMM, isWeekend, type Period, type Ro import { getTeamShiftInfo } from './AttendanceTab'; interface Props { - periods: Period[]; - attendance: AttendanceDataMap; - activePeriodId: string; - setActivePeriodId: (id: string) => void; - currentUserRole: Role; + periods: Period[]; attendance: AttendanceDataMap; activePeriodId: string; setActivePeriodId: (id: string) => void; currentUserRole: Role; } const POT_HOUR_LIMIT = 90; @@ -32,25 +28,18 @@ export const ReportTab: React.FC = ({ periods, attendance, activePeriodId let total = 0; for (const key in attendance) { const entry = attendance[key]; - if (entry.youthId === youthId && entry.date >= activePeriod.startDate && entry.date <= activePeriod.endDate) { - total += entry.weightedHours; - } + if (entry.youthId === youthId && entry.date >= activePeriod.startDate && entry.date <= activePeriod.endDate) total += entry.weightedHours; } return total; }; const getTimelineForYouth = (youthId: string) => { - return Object.values(attendance) - .filter(a => a.youthId === youthId && a.date >= activePeriod.startDate && a.date <= activePeriod.endDate) - .sort((a, b) => { - if (a.date !== b.date) return a.date.localeCompare(b.date); - return a.shiftId === 'MORNING' ? -1 : 1; - }); + return Object.values(attendance).filter(a => a.youthId === youthId && a.date >= activePeriod.startDate && a.date <= activePeriod.endDate) + .sort((a, b) => { if (a.date !== b.date) return a.date.localeCompare(b.date); return a.shiftId === 'MORNING' ? -1 : 1; }); }; const exportToCSV = () => { if (!activePeriod) return; - let csvContent = "Datum;Pass;Namn;Lag;Status;Arbetad Tid (HH:MM);Viktad Pott;Anteckning\n"; const entries = Object.values(attendance).filter(a => a.date >= activePeriod.startDate && a.date <= activePeriod.endDate); @@ -65,22 +54,16 @@ export const ReportTab: React.FC = ({ periods, attendance, activePeriodId entries.forEach(entry => { const youth = activePeriod.youthList.find(y => y.id === entry.youthId); if (!youth) return; - - const isWknd = isWeekend(entry.date); - const shift = isWknd ? 'Hela dagen' : (entry.shiftId === 'MORNING' ? 'Morgon' : 'Eftermiddag'); - + const shift = isWeekend(entry.date) ? 'Hela dagen' : (entry.shiftId === 'MORNING' ? 'Morgon' : 'Eftermiddag'); const teamName = youth.team === 'PF' ? 'Pilgrimsfalk' : 'Tumlare'; const worked = formatTimeHHMM(entry.hoursWorked); const weighted = entry.weightedHours.toFixed(2).replace('.', ','); const note = entry.note || ''; - csvContent += `${entry.date};${shift};${youth.name};${teamName};${entry.status};${worked};${weighted};${note}\n`; }); csvContent += "\nSummering (Timpott)\nNamn;Lag;Total Viktad Pott\n"; - const sortedYouth = [...activePeriod.youthList].sort((a, b) => a.name.localeCompare(b.name)); - sortedYouth.forEach(youth => { const total = getPeriodHoursTotal(youth.id).toFixed(2).replace('.', ','); const teamName = youth.team === 'PF' ? 'Pilgrimsfalk' : 'Tumlare'; @@ -90,149 +73,121 @@ export const ReportTab: React.FC = ({ periods, attendance, activePeriodId const blob = new Blob(["\uFEFF" + csvContent], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); - link.setAttribute("href", url); - link.setAttribute("download", `Narvaro_${activePeriod.name.replace(/ /g, '_')}.csv`); - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); + link.setAttribute("href", url); link.setAttribute("download", `Narvaro_${activePeriod.name.replace(/ /g, '_')}.csv`); + document.body.appendChild(link); link.click(); document.body.removeChild(link); }; const sortedActiveYouth = [...activePeriod.youthList].sort((a, b) => a.name.localeCompare(b.name)); return (
- {/* Header / Period Selector */} -
- { setActivePeriodId(e.target.value); setExpandedYouthId(null); }} className="bg-white border border-slate-teal/10 p-3 rounded-xl font-bold text-slate-teal text-sm w-full md:w-auto focus:outline-none shadow-sm"> {periods.map(p => )} -
-

{activePeriod.name}

-

{activePeriod.startDate} — {activePeriod.endDate}

+
+

{activePeriod.name}

+

{activePeriod.startDate} — {activePeriod.endDate}

- {/* PERIOD HOUR POT REPORT */} -
-
-

- - Timrapport +
+
+

+ Timrapport

{currentUserRole !== 'Viewer' && ( - )}
-
+
{sortedActiveYouth.map(youth => { const total = getPeriodHoursTotal(youth.id); - const warningStatus: 'none' | 'yellow' | 'red' = total > POT_HOUR_LIMIT ? 'red' : (total >= POT_HOUR_LIMIT - 10 ? 'yellow' : 'none'); + const warningStatus = total > POT_HOUR_LIMIT ? 'red' : (total >= POT_HOUR_LIMIT - 10 ? 'yellow' : 'none'); const isExpanded = expandedYouthId === youth.id; const timeline = getTimelineForYouth(youth.id); return ( -
-
-
-
-
- {youth.team +
+ +
setExpandedYouthId(isExpanded ? null : youth.id)} + className="flex flex-col sm:flex-row sm:justify-between sm:items-center p-4 gap-4 cursor-pointer hover:bg-slate-teal/5 transition-colors" + > +
+
+ {youth.team +
+
+ {youth.name} +
+ {isExpanded ? : } + {isExpanded ? 'Dölj detaljer' : 'Klicka för detaljer'}
- {youth.name}
-
-
- {warningStatus === 'red' && } -
+
+ {warningStatus === 'red' && } +
- {formatHours(total)} / {POT_HOUR_LIMIT}t + {formatHours(total)} / {POT_HOUR_LIMIT}h
-

Viktade timmar

+

Viktade timmar

{isExpanded && ( -
-

- Arbetspass & Frånvaro +
+

+ Arbetspass

{timeline.length === 0 ? ( -

Ingen närvaro loggad ännu.

+

Ingen närvaro loggad.

) : (
{timeline.map((entry, idx) => { const isWknd = isWeekend(entry.date); - - // Grab schedule for this exact day to check for 'Extra' shifts accurately! const dayNameStr = new Date(entry.date + 'T12:00:00').toLocaleDateString('sv-SE', { weekday: 'long' }).toLowerCase(); const daySchedule = scheduleData.find(d => d.day.toLowerCase() === dayNameStr); + // FIX: Bulletproof isExtra logic that catches weekend-workers too! let isExtra = false; - if (!isWknd && daySchedule) { - const expectedShiftId = getTeamShiftInfo(daySchedule, youth.team).shiftId; - isExtra = entry.shiftId !== expectedShiftId; + if (daySchedule) { + const expectedShift = getTeamShiftInfo(daySchedule, youth.team); + if (expectedShift.time.toLowerCase() === 'ledig') { + isExtra = true; + } else if (!isWknd) { + isExtra = entry.shiftId !== expectedShift.shiftId; + } + } else { + isExtra = true; } - // Correct display logic: "Hela dagen" for weekends - const shiftLabel = isWknd ? 'Hela dagen' : (entry.shiftId === 'MORNING' ? 'Morgon' : 'Eftermiddag'); + const shiftLabel = isWknd ? 'Heldag' : (entry.shiftId === 'MORNING' ? 'Morgon' : 'Eftermiddag'); return ( -
-
- {entry.date} +
+
+ {entry.date} + {shiftLabel} - - {shiftLabel} - - - {isExtra && ( - - Extra pass - - )} - - {isWknd && Helg} + {isExtra && Extra} + {isWknd && Helg}
+
+ {entry.status === 'Pending' ? Väntar... : + entry.status === 'Absent' ? {entry.note || 'Frånvarande'} : + {entry.status === 'Late' ? 'Manuell' : 'Närvarande'}} -
- {entry.status === 'Pending' ? ( - Väntar på registrering - ) : entry.status === 'Absent' ? ( - {entry.note || 'Frånvarande'} - ) : ( - {entry.status === 'Late' ? 'Manuell tid' : 'Närvarande'} - )} - -
- {formatTimeHHMM(entry.hoursWorked)} arbetat - → +{entry.weightedHours.toFixed(1)} pott -
+ + {formatTimeHHMM(entry.hoursWorked)} (+{entry.weightedHours.toFixed(1)}h) +
); @@ -244,9 +199,6 @@ export const ReportTab: React.FC = ({ periods, attendance, activePeriodId
); })} - {activePeriod.youthList.length === 0 && ( -

Inga ungdomar i denna period.

- )}
diff --git a/app/admin/SetupTab.tsx b/app/admin/SetupTab.tsx index 2111820..2df283e 100644 --- a/app/admin/SetupTab.tsx +++ b/app/admin/SetupTab.tsx @@ -10,12 +10,9 @@ import porpoiseIcon from '../assets/porpoise.svg'; import { type Period, type Youth } from './adminTypes'; interface Props { - periods: Period[]; - createPeriod: (name: string, start: string) => void; - deletePeriod: (id: string) => void; - bulkAddYouth: (periodId: string, text: string, team: 'PF' | 'TU') => void; - removeYouth: (periodId: string, youthId: string) => void; - isOffline: boolean; + periods: Period[]; createPeriod: (name: string, start: string) => void; + deletePeriod: (id: string) => void; bulkAddYouth: (periodId: string, text: string, team: 'PF' | 'TU') => void; + removeYouth: (periodId: string, youthId: string) => void; isOffline: boolean; } export const SetupTab: React.FC = ({ periods, createPeriod, deletePeriod, bulkAddYouth, removeYouth, isOffline }) => { @@ -24,40 +21,24 @@ export const SetupTab: React.FC = ({ periods, createPeriod, deletePeriod, const [expandedPeriod, setExpandedPeriod] = useState(null); const handleCreate = () => { - if (isOffline) { - alert("Du måste vara ansluten till internet för att skapa en ny period."); - return; - } + if (isOffline) { alert("Du måste vara ansluten till internet för att skapa en ny period."); return; } const name = (document.getElementById('periodName') as HTMLInputElement).value; const start = (document.getElementById('periodStart') as HTMLInputElement).value; if (name && start) createPeriod(name, start); }; const handleDeletePeriod = (id: string) => { - if (isOffline) { - alert("Åtgärd nekad: Du måste vara ansluten till internet för att ta bort en period."); - return; - } - if (window.confirm('Är du säker på att du vill ta bort hela perioden? All närvarodata kopplad till perioden kommer försvinna!')) { - deletePeriod(id); - } + if (isOffline) { alert("Åtgärd nekad: Du måste vara ansluten till internet för att ta bort en period."); return; } + if (window.confirm('Är du säker på att du vill ta bort hela perioden?')) deletePeriod(id); }; const handleRemoveYouth = (periodId: string, youthId: string, youthName: string) => { - if (isOffline) { - alert("Åtgärd nekad: Du måste vara ansluten till internet för att ta bort en ungdom."); - return; - } - if (window.confirm(`Är du säker på att du vill ta bort ${youthName} från perioden?`)) { - removeYouth(periodId, youthId); - } + if (isOffline) { alert("Åtgärd nekad: Du måste vara ansluten till internet för att ta bort en ungdom."); return; } + if (window.confirm(`Är du säker på att du vill ta bort ${youthName} från perioden?`)) removeYouth(periodId, youthId); }; const handleBulkAdd = () => { - if (isOffline) { - alert("Åtgärd nekad: Du måste vara ansluten till internet för att importera ungdomar."); - return; - } + if (isOffline) { alert("Åtgärd nekad!"); return; } if (expandedPeriod) { const processedText = bulkText.split('\n').map(line => { const parts = line.split(/[,|-]/).map(p => p.trim()); @@ -69,37 +50,25 @@ export const SetupTab: React.FC = ({ periods, createPeriod, deletePeriod, } return line; }).join('\n'); - bulkAddYouth(expandedPeriod, processedText, bulkTeam); setBulkText(''); } }; - // Helper to render a group of youth const renderYouthGroup = (youthList: Youth[], periodId: string) => { if (youthList.length === 0) return null; - return (
{youthList.map(youth => ( -
+
-
- {youth.team +
+ {youth.team
- {youth.name} + {youth.name}
-
))} @@ -109,70 +78,64 @@ export const SetupTab: React.FC = ({ periods, createPeriod, deletePeriod, return (
- {/* Create New Period */} -
-

- - Skapa Ny Period +
+

+ Skapa Ny Period

- - -
- {/* List Existing Periods */} {periods.map(period => { - // Sort by name alphabetically const pfYouth = period.youthList.filter(y => y.team === 'PF').sort((a, b) => a.name.localeCompare(b.name)); const tuYouth = period.youthList.filter(y => y.team === 'TU').sort((a, b) => a.name.localeCompare(b.name)); return ( -
-
setExpandedPeriod(expandedPeriod === period.id ? null : period.id)}> +
+
setExpandedPeriod(expandedPeriod === period.id ? null : period.id)}>
-

{period.name}

-

{period.startDate} till {period.endDate} • {period.youthList.length} ungdomar

+

{period.name}

+

{period.startDate} till {period.endDate} • {period.youthList.length} ungdomar

-
- {/* Expandable Youth Management */} {expandedPeriod === period.id && ( -
-

Bulk-lägg till ungdomar

-
+
+

+ Bulk-lägg till ungdomar +

+
+ {/* FIX: Much larger textarea for easier importing */}