260 lines
11 KiB
TypeScript
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>
|
|
)
|
|
}
|