muller-reporting-sys/app/shift-manager/shifts/[id]/page.tsx
2025-11-12 22:21:35 +03:00

260 lines
11 KiB
TypeScript

import DashboardLayout from "@/components/DashboardLayout"
import { auth } from "@/lib/auth"
import { prisma } from "@/lib/prisma"
import Link from "next/link"
import { notFound } from "next/navigation"
export default async function ShiftDetailsPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const session = await auth()
const manager = await prisma.shiftManager.findFirst({
where: { email: session?.user?.email || "" }
})
if (!manager) return <div>Manager not found</div>
const shift = await prisma.shift.findFirst({
where: {
id,
shiftManagerId: manager.id
},
include: {
shiftManager: true,
shiftTeamMembers: {
include: {
worker: true,
machine: true,
team: true
}
},
machineShiftReports: {
include: {
worker: true,
machine: true
}
}
}
})
if (!shift) notFound()
return (
<DashboardLayout requiredRole="shift_manager">
<div>
<div className="mb-6">
<Link
href="/shift-manager/shifts"
className="text-blue-600 hover:underline mb-4 inline-block"
>
Back to Shifts
</Link>
<h1 className="text-3xl font-bold text-gray-800">Shift Details</h1>
</div>
{/* Shift Information Card */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6">
<h2 className="text-xl font-semibold text-gray-800 mb-4">Shift Information</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Shift Name</p>
<p className="text-lg font-medium text-gray-900">{shift.name}</p>
</div>
<div>
<p className="text-sm text-gray-500">Status</p>
<span className={`inline-block px-3 py-1 rounded-full text-xs font-medium ${
shift.status === "active" ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-800"
}`}>
{shift.status}
</span>
</div>
<div>
<p className="text-sm text-gray-500">Date</p>
<p className="text-lg font-medium text-gray-900">
{new Date(shift.shiftDate).toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Team</p>
<p className="text-lg font-medium text-gray-900">
{shift.shiftTeamMembers[0]?.team.name || 'N/A'}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Start Time</p>
<p className="text-lg font-medium text-gray-900">
{new Date(shift.startTime).toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit'
})}
</p>
</div>
<div>
<p className="text-sm text-gray-500">End Time</p>
<p className="text-lg font-medium text-gray-900">
{new Date(shift.endTime).toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit'
})}
</p>
</div>
</div>
</div>
{/* Operator Assignments Card */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
<div className="p-6 border-b border-gray-200">
<h2 className="text-xl font-semibold text-gray-800">Operator Assignments</h2>
<p className="text-sm text-gray-500 mt-1">
{shift.shiftTeamMembers.length} operator(s) assigned
</p>
</div>
{shift.shiftTeamMembers.length === 0 ? (
<div className="p-6 text-center text-gray-500">
No operators assigned to this shift
</div>
) : (
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-gray-50 border-b border-gray-200">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Operator</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Role</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Machine</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Report Status</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Actions</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{shift.shiftTeamMembers.map((member) => {
const report = shift.machineShiftReports.find(
r => r.workerId === member.workerId && r.machineId === member.machineId
)
return (
<tr key={member.id}>
<td className="px-6 py-4">
<div>
<p className="text-sm font-medium text-gray-900">
{member.worker.firstName} {member.worker.surname}
</p>
<p className="text-xs text-gray-500">{member.worker.email || 'N/A'}</p>
</div>
</td>
<td className="px-6 py-4">
<span className="text-xs px-2 py-1 bg-blue-100 text-blue-800 rounded">
{member.shiftRole}
</span>
</td>
<td className="px-6 py-4">
<div>
<p className="text-sm font-medium text-gray-900">
{member.machine?.name || 'N/A'}
</p>
<p className="text-xs text-gray-500">
{member.machine?.machineType || 'N/A'}
</p>
</div>
</td>
<td className="px-6 py-4">
{report ? (
<span className="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">
<svg className="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
</svg>
Submitted
</span>
) : (
<span className="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">
<svg className="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clipRule="evenodd" />
</svg>
Pending
</span>
)}
</td>
<td className="px-6 py-4">
{report ? (
<Link
href={`/shift-manager/reports/${report.id}`}
className="text-blue-600 hover:underline text-sm"
>
View Report
</Link>
) : (
<span className="text-gray-400 text-sm">No report yet</span>
)}
</td>
</tr>
)
})}
</tbody>
</table>
</div>
)}
</div>
{/* Summary Statistics */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-6">
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<div className="flex items-center">
<div className="shrink-0 bg-blue-100 rounded-lg p-3">
<svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
</svg>
</div>
<div className="ml-4">
<p className="text-sm text-gray-500">Total Operators</p>
<p className="text-2xl font-bold text-gray-900">
{shift.shiftTeamMembers.length}
</p>
</div>
</div>
</div>
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<div className="flex items-center">
<div className="shrink-0 bg-green-100 rounded-lg p-3">
<svg className="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<div className="ml-4">
<p className="text-sm text-gray-500">Reports Submitted</p>
<p className="text-2xl font-bold text-gray-900">
{shift.machineShiftReports.length}
</p>
</div>
</div>
</div>
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<div className="flex items-center">
<div className="shrink-0 bg-yellow-100 rounded-lg p-3">
<svg className="w-6 h-6 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<div className="ml-4">
<p className="text-sm text-gray-500">Reports Pending</p>
<p className="text-2xl font-bold text-gray-900">
{Math.max(0, shift.shiftTeamMembers.length - shift.machineShiftReports.length)}
</p>
</div>
</div>
</div>
</div>
</div>
</DashboardLayout>
)
}