184 lines
6.9 KiB
TypeScript
184 lines
6.9 KiB
TypeScript
"use client"
|
|
|
|
import { useState, useEffect } from "react"
|
|
import { useRouter } from "next/navigation"
|
|
|
|
export default function CreateShiftPage() {
|
|
const router = useRouter()
|
|
const [loading, setLoading] = useState(false)
|
|
const [managerTeam, setManagerTeam] = useState<any>(null)
|
|
const [workers, setWorkers] = useState<any[]>([])
|
|
const [machines, setMachines] = useState<any[]>([])
|
|
const [formData, setFormData] = useState({
|
|
name: "AM",
|
|
shiftDate: new Date().toISOString().split('T')[0],
|
|
teamId: "",
|
|
operators: Array(7).fill(""),
|
|
level2Id: "",
|
|
engineerId: ""
|
|
})
|
|
|
|
useEffect(() => {
|
|
// Fetch manager's team
|
|
fetch(`/api/shift-manager/my-team`)
|
|
.then(r => r.json())
|
|
.then(team => {
|
|
if (team && !team.error) {
|
|
setManagerTeam(team)
|
|
setFormData(prev => ({ ...prev, teamId: team.id }))
|
|
|
|
// Fetch workers for this team only
|
|
fetch(`/api/workers?teamId=${team.id}`)
|
|
.then(r => r.json())
|
|
.then(setWorkers)
|
|
}
|
|
})
|
|
.catch(err => console.error("Error fetching team:", err))
|
|
|
|
fetch('/api/machines').then(r => r.json()).then(data => {
|
|
// Sort machines by name (T1, T2, T3, etc.)
|
|
const sortedMachines = data.sort((a: any, b: any) => {
|
|
const numA = parseInt(a.name.replace('T', ''))
|
|
const numB = parseInt(b.name.replace('T', ''))
|
|
return numA - numB
|
|
})
|
|
setMachines(sortedMachines)
|
|
})
|
|
}, [])
|
|
|
|
// Get available operators for a specific machine index
|
|
const getAvailableOperators = (currentIndex: number) => {
|
|
const selectedOperatorIds = formData.operators.filter((id, idx) => id && idx !== currentIndex)
|
|
return workers.filter((w: any) =>
|
|
w.jobPosition === "Blow Moulder Level 1" &&
|
|
!selectedOperatorIds.includes(w.id)
|
|
)
|
|
}
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
setLoading(true)
|
|
|
|
const response = await fetch('/api/shifts', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(formData)
|
|
})
|
|
|
|
if (response.ok) {
|
|
router.push('/shift-manager/shifts')
|
|
}
|
|
setLoading(false)
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 p-8">
|
|
<div className="max-w-4xl mx-auto">
|
|
<h1 className="text-3xl font-bold text-gray-800 mb-6">Create New Shift</h1>
|
|
|
|
<form onSubmit={handleSubmit} className="bg-white p-6 rounded-xl shadow-sm border border-gray-200 space-y-6">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium mb-2">Shift Type</label>
|
|
<select
|
|
value={formData.name}
|
|
onChange={(e) => setFormData({...formData, name: e.target.value})}
|
|
className="w-full px-4 py-2 border rounded-lg"
|
|
required
|
|
>
|
|
<option value="AM">AM (7:00 AM - 7:00 PM)</option>
|
|
<option value="PM">PM (7:00 PM - 7:00 AM)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium mb-2">Date</label>
|
|
<input
|
|
type="date"
|
|
value={formData.shiftDate}
|
|
onChange={(e) => setFormData({...formData, shiftDate: e.target.value})}
|
|
className="w-full px-4 py-2 border rounded-lg"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium mb-2">Team</label>
|
|
<input
|
|
type="text"
|
|
value={managerTeam?.name || "Loading..."}
|
|
className="w-full px-4 py-2 border rounded-lg bg-gray-100 cursor-not-allowed"
|
|
disabled
|
|
readOnly
|
|
/>
|
|
<p className="text-sm text-gray-500 mt-1">You can only create shifts for your assigned team</p>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="font-semibold mb-3">Assign Operators to Machines</h3>
|
|
<p className="text-sm text-gray-600 mb-3">
|
|
Assign one operator to each machine. Once selected, an operator won't appear in other dropdowns.
|
|
</p>
|
|
<div className="space-y-3">
|
|
{machines.slice(0, 7).map((machine: any, index: number) => {
|
|
const availableOps = getAvailableOperators(index)
|
|
const currentOperator = formData.operators[index]
|
|
|
|
return (
|
|
<div key={machine.id} className="flex items-center gap-4">
|
|
<span className="w-20 font-medium text-gray-700">{machine.name}</span>
|
|
<select
|
|
value={currentOperator}
|
|
onChange={(e) => {
|
|
const ops = [...formData.operators]
|
|
ops[index] = e.target.value
|
|
setFormData({...formData, operators: ops})
|
|
}}
|
|
className="flex-1 px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500"
|
|
required
|
|
>
|
|
<option value="">Select Operator</option>
|
|
{/* Show currently selected operator even if not in available list */}
|
|
{currentOperator && !availableOps.find((w: any) => w.id === currentOperator) && (() => {
|
|
const selectedWorker = workers.find((w: any) => w.id === currentOperator)
|
|
return selectedWorker ? (
|
|
<option value={currentOperator}>
|
|
{selectedWorker.firstName} {selectedWorker.surname}
|
|
</option>
|
|
) : null
|
|
})()}
|
|
{/* Show available operators */}
|
|
{availableOps.map((worker: any) => (
|
|
<option key={worker.id} value={worker.id}>
|
|
{worker.firstName} {worker.surname} ({worker.empNo})
|
|
</option>
|
|
))}
|
|
</select>
|
|
{currentOperator && (
|
|
<span className="text-green-600 text-sm">✓</span>
|
|
)}
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
{formData.operators.filter(op => op).length > 0 && (
|
|
<p className="text-sm text-gray-600 mt-3">
|
|
{formData.operators.filter(op => op).length} of 7 operators assigned
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="w-full bg-blue-600 text-white py-3 rounded-lg font-medium hover:bg-blue-700 transition-colors disabled:opacity-50"
|
|
>
|
|
{loading ? "Creating..." : "Create Shift"}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|