Files
breakpilot-compliance/admin-compliance/app/sdk/process-tasks/_components/CalendarView.tsx
Sharang Parnerkar 1fcd8244b1 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>
2026-04-16 17:12:15 +02:00

82 lines
3.5 KiB
TypeScript

'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">&larr; 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 &rarr;</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>
)
}