refactor(admin): split evidence, process-tasks, iace/hazards pages
Extract components and hooks into _components/ and _hooks/ subdirectories to reduce each page.tsx to under 500 LOC (was 1545/1383/1316). Final line counts: evidence=213, process-tasks=304, hazards=157. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { ProcessTask, daysUntil } from './types'
|
||||
|
||||
export function CalendarView({ tasks }: { tasks: ProcessTask[] }) {
|
||||
const [currentMonth, setCurrentMonth] = useState(() => {
|
||||
const now = new Date()
|
||||
return new Date(now.getFullYear(), now.getMonth(), 1)
|
||||
})
|
||||
|
||||
const year = currentMonth.getFullYear()
|
||||
const month = currentMonth.getMonth()
|
||||
const daysInMonth = new Date(year, month + 1, 0).getDate()
|
||||
const firstDayOfWeek = new Date(year, month, 1).getDay()
|
||||
const startOffset = firstDayOfWeek === 0 ? 6 : firstDayOfWeek - 1
|
||||
|
||||
const monthLabel = currentMonth.toLocaleDateString('de-DE', { month: 'long', year: 'numeric' })
|
||||
|
||||
const tasksByDate: Record<string, ProcessTask[]> = {}
|
||||
tasks.forEach(t => {
|
||||
if (!t.next_due_date) return
|
||||
const key = t.next_due_date.substring(0, 10)
|
||||
if (!tasksByDate[key]) tasksByDate[key] = []
|
||||
tasksByDate[key].push(t)
|
||||
})
|
||||
|
||||
const prev = () => setCurrentMonth(new Date(year, month - 1, 1))
|
||||
const next = () => setCurrentMonth(new Date(year, month + 1, 1))
|
||||
const today = new Date().toISOString().substring(0, 10)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<button onClick={prev} className="px-3 py-1 text-sm text-gray-600 hover:bg-gray-100 rounded-lg">← Vorher</button>
|
||||
<h3 className="text-lg font-semibold text-gray-900">{monthLabel}</h3>
|
||||
<button onClick={next} className="px-3 py-1 text-sm text-gray-600 hover:bg-gray-100 rounded-lg">Weiter →</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-7 gap-px bg-gray-200 rounded-xl overflow-hidden border border-gray-200">
|
||||
{['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'].map(d => (
|
||||
<div key={d} className="bg-gray-50 p-2 text-xs font-medium text-gray-500 text-center">{d}</div>
|
||||
))}
|
||||
{Array.from({ length: startOffset }).map((_, i) => (
|
||||
<div key={`empty-${i}`} className="bg-white p-2 min-h-[80px]"></div>
|
||||
))}
|
||||
{Array.from({ length: daysInMonth }).map((_, i) => {
|
||||
const day = i + 1
|
||||
const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`
|
||||
const dayTasks = tasksByDate[dateStr] || []
|
||||
const isToday = dateStr === today
|
||||
|
||||
return (
|
||||
<div key={day} className={`bg-white p-2 min-h-[80px] ${isToday ? 'ring-2 ring-purple-400 ring-inset' : ''}`}>
|
||||
<span className={`text-xs font-medium ${isToday ? 'text-purple-600' : 'text-gray-500'}`}>{day}</span>
|
||||
<div className="mt-1 space-y-0.5">
|
||||
{dayTasks.slice(0, 3).map(t => {
|
||||
const days = daysUntil(t.next_due_date)
|
||||
let dotColor = 'bg-gray-400'
|
||||
if (t.status === 'completed') dotColor = 'bg-green-500'
|
||||
else if (days !== null && days < 0) dotColor = 'bg-red-500'
|
||||
else if (days !== null && days <= 7) dotColor = 'bg-orange-500'
|
||||
|
||||
return (
|
||||
<div key={t.id} className="flex items-center gap-1" title={t.title}>
|
||||
<span className={`w-1.5 h-1.5 rounded-full flex-shrink-0 ${dotColor}`}></span>
|
||||
<span className="text-[10px] text-gray-600 truncate">{t.title}</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
{dayTasks.length > 3 && (
|
||||
<span className="text-[10px] text-gray-400">+{dayTasks.length - 3} mehr</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user