263 lines
13 KiB
TypeScript
263 lines
13 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import axiosInstance from '../../src/axiosSecure';
|
||
import PublisherSearchBox from '../publisher/PublisherSearchBox'; // Update the path
|
||
import Modal from '../Modal';
|
||
|
||
import LocalShippingIcon from '@mui/icons-material/LocalShipping';
|
||
|
||
const common = require('src/helpers/common');
|
||
|
||
|
||
|
||
function ShiftComponent({ shift, onShiftSelect, isSelected, onPublisherSelect, allPublishersInfo }) {
|
||
const [isDeleted, setIsDeleted] = useState(false);
|
||
const [assignments, setAssignments] = useState(shift.assignments);
|
||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||
const [useFilterDate, setUseFilterDate] = useState(true);
|
||
const [selectedPublisher, setSelectedPublisher] = useState(null);
|
||
const [showCopyHint, setShowCopyHint] = useState(false);
|
||
const [transportProvided, setTransportProvided] = useState(false);
|
||
// Update assignments when shift changes
|
||
useEffect(() => {
|
||
setAssignments(shift.assignments);
|
||
setTransportProvided(!shift.requiresTransport || shift.assignments.some(ass => ass.isWithTransport));
|
||
}, [shift.assignments]);
|
||
|
||
const handleShiftClick = (shiftId) => {
|
||
if (onShiftSelect) {
|
||
onShiftSelect(shift);
|
||
}
|
||
};
|
||
|
||
const handlePublisherClick = (publisher) => {
|
||
setSelectedPublisher(publisher);
|
||
|
||
console.log("Publisher clicked:", publisher, "selected publisher:", selectedPublisher);
|
||
shift.selectedPublisher = publisher;
|
||
if (onShiftSelect) {
|
||
onShiftSelect(shift);
|
||
}
|
||
// if (onPublisherSelect) {
|
||
// onPublisherSelect(publisher);
|
||
// }
|
||
|
||
common.copyToClipboard(null, publisher.firstName + ' ' + publisher.lastName);
|
||
}
|
||
|
||
const deleteShift = async (id) => {
|
||
try {
|
||
console.log("Removing shift with id:", id);
|
||
await axiosInstance.delete("/api/data/shifts/" + id);
|
||
setIsDeleted(true);
|
||
} catch (error) {
|
||
console.error("Error removing shift:", error);
|
||
}
|
||
}
|
||
|
||
|
||
const removeAssignment = async (id) => {
|
||
try {
|
||
console.log("Removing assignment with id:", id);
|
||
await axiosInstance.delete("/api/data/assignments/" + id);
|
||
setAssignments(prevAssignments => prevAssignments.filter(ass => ass.id !== id));
|
||
} catch (error) {
|
||
console.error("Error removing assignment:", error);
|
||
}
|
||
};
|
||
|
||
const addAssignment = async (publisher, shiftId) => {
|
||
try {
|
||
console.log(`new assignment for publisher ${publisher.id} - ${publisher.firstName} ${publisher.lastName}`);
|
||
const newAssignment = {
|
||
publisher: { connect: { id: publisher.id } },
|
||
shift: { connect: { id: shiftId } },
|
||
//isActive: true,
|
||
isConfirmed: true,
|
||
};
|
||
const { data } = await axiosInstance.post("/api/data/assignments", newAssignment);
|
||
// Update the 'publisher' property of the returned data with the full publisher object
|
||
data.publisher = publisher;
|
||
setAssignments(prevAssignments => [...prevAssignments, data]);
|
||
} catch (error) {
|
||
console.error("Error adding assignment:", error);
|
||
}
|
||
};
|
||
const copyAllPublisherNames = () => {
|
||
const names = assignments.map(ass => `${ass.publisher.firstName} ${ass.publisher.lastName}`).join(', ');
|
||
common.copyToClipboard(null, names);
|
||
// Show hint and set a timer to hide it
|
||
setShowCopyHint(true);
|
||
setTimeout(() => setShowCopyHint(false), 1500);
|
||
};
|
||
|
||
async function toggleTransport(assignment): Promise<void> {
|
||
try {
|
||
assignment.isWithTransport = !assignment.isWithTransport;
|
||
const { data } = await axiosInstance.put("/api/data/assignments/" + assignment.id,
|
||
{ isWithTransport: assignment.isWithTransport })
|
||
.then(() => {
|
||
setTransportProvided(assignments.some(ass => ass.isWithTransport))
|
||
});
|
||
} catch (error) { }
|
||
}
|
||
|
||
async function toggleRequiresTransport(shiftId): Promise<void> {
|
||
try {
|
||
shift.requiresTransport = !shift.requiresTransport;
|
||
const { data } = await axiosInstance.put("/api/data/shifts/" + shiftId,
|
||
{ requiresTransport: shift.requiresTransport })
|
||
.then(() => {
|
||
console.log("shift '" + shiftId + "' transport required:" + shift.requiresTransport);
|
||
// setTransportProvided(assignments.some(ass => ass.isWithTransport))
|
||
});
|
||
} catch (error) { }
|
||
}
|
||
|
||
return (
|
||
<>{!isDeleted && (
|
||
<div className={`flow w-full p-4 py-2 border-2 border-gray-300 rounded-md my-1 ${isSelected ? 'bg-gray-200' : ''}`}
|
||
onClick={handleShiftClick} onDoubleClick={copyAllPublisherNames}>
|
||
{/* Time Window Header */}
|
||
<div className="flex justify-between items-center mb-2 border-b pb-1">
|
||
<span className="flex text-lg font-semibold">
|
||
{`${common.getTimeRange(new Date(shift.startTime), new Date(shift.endTime))}`}
|
||
{/* {shift.requiresTransport && (<LocalShippingIcon />)} */}
|
||
{/* Toggle for Transport Requirement */}
|
||
<label className="ml-4 flex items-center">
|
||
<input type="checkbox" checked={shift.requiresTransport}
|
||
onChange={() => toggleRequiresTransport(shift.id)}
|
||
className="form-checkbox h-5 w-5 text-green-600" />
|
||
<span className="ml-2 text-sm text-gray-700">транспорт</span>
|
||
</label>
|
||
</span>
|
||
|
||
{/* Copy All Names Button */}
|
||
<button onClick={copyAllPublisherNames} className="bg-green-500 text-white py-1 px-2 text-sm rounded-md">
|
||
копирай имената {/* Placeholder for Copy icon */}
|
||
</button>
|
||
{/* Hint Message */}
|
||
{showCopyHint && (
|
||
<div className="absolute top-0 right-0 p-2 bg-green-200 text-green-800 rounded">
|
||
Имената са копирани
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Assignments */}
|
||
{assignments.map((ass, index) => {
|
||
const publisherInfo = allPublishersInfo.find(info => info?.id === ass.publisher.id) || ass.publisher;
|
||
|
||
// Determine border styles
|
||
let borderStyles = '';
|
||
let canTransport = false;
|
||
if (selectedPublisher && selectedPublisher.id === ass.publisher.id) {
|
||
borderStyles += 'bg-blue-200 font-bold '; // Bottom border for selected publishers
|
||
}
|
||
|
||
if (publisherInfo.availabilityCount == 0 || !publisherInfo.availabilities || publisherInfo.availabilities.length == 0)
|
||
//user has never the filled the avalabilities form or if there is no publisherInfo - draw red border - publisher is no longer available for the day!
|
||
{
|
||
borderStyles += 'border-2 border-red-500 ';
|
||
} else {
|
||
|
||
// checkig if the publisher is available for this assignment
|
||
//ToDo: verify if that check is correct
|
||
const av = publisherInfo.availabilities?.find(av =>
|
||
av.startTime <= shift.startTime && av.endTime >= shift.endTime
|
||
);
|
||
if (av) {
|
||
borderStyles += 'border-b-2 border-blue-500 '; // Left border for specific availability conditions
|
||
ass.canTransport = av.isWithTransportIn || av.isWithTransportOut;
|
||
// console.log(publisherInfo.firstName, "available for shift", shift.id, "at", common.getDateFormattedShort(new Date(shift.startTime)), "av-" + av.id, ": a." + common.getTimeFormatted(av.startTime), "<=", common.getTimeFormatted(shift.startTime), "; a." + common.getTimeFormatted(av.endTime), ">=", common.getTimeFormatted(shift.endTime));
|
||
}
|
||
else {
|
||
borderStyles += 'border-l-4 border-red-500 ';
|
||
}
|
||
|
||
if (publisherInfo.hasUpToDateAvailabilities) {
|
||
//add green right border
|
||
borderStyles += 'border-r-2 border-green-300';
|
||
}
|
||
|
||
//the pub is the same time as last month
|
||
// if (publisherInfo.availabilities?.some(av =>
|
||
// (!av.dayOfMonth || av.isFromPreviousMonth) &&
|
||
// av.startTime <= ass.startTime &&
|
||
// av.endTime >= ass.endTime)) {
|
||
// borderStyles += 'border-t-2 border-yellow-500 '; // Left border for specific availability conditions
|
||
// }
|
||
|
||
}
|
||
|
||
|
||
return (
|
||
<div key={index}
|
||
className={`flow rounded-md px-2 py-1 sm:py-0.5 my-1 ${(ass.isConfirmed && !ass.isBySystem) ? 'bg-green-100' : 'bg-gray-100'} ${borderStyles}`}
|
||
>
|
||
<div className="flex justify-between items-center" onClick={() => handlePublisherClick(ass.publisher)}>
|
||
<span className="text-gray-700">{publisherInfo.firstName} {publisherInfo.lastName}</span>
|
||
<div className="flex items-left" >
|
||
{/* //if shift.isWithTransport, add trnsport button toggle, which sets ass.isWithTransportIn */}
|
||
{shift.requiresTransport && (
|
||
<span
|
||
onClick={ass.canTransport || true ? () => toggleTransport(ass) : undefined}
|
||
className={`material-icons ${ass.isWithTransport ? 'text-green-500 font-bold' : (transportProvided ? 'text-gray-400 ' : 'text-orange-400 font-bold')} ${ass.canTransport || ass.isWithTransport || true ? ' cursor-pointer' : 'cursor-not-allowed'} px-3 py-0 ml-2 rounded-md`}
|
||
>
|
||
{ass.isWithTransport ? "транспорт" : ass.canTransport ? "може транспорт" : "без транспорт"} <LocalShippingIcon />
|
||
</span>
|
||
)}
|
||
<button onClick={() => removeAssignment(ass.id)} className="items-center leading-snug text-white text-center bg-red-500 hover:bg-red-600 px-3 py-1 md:py-0.5 ml-2 rounded-md" >
|
||
махни
|
||
</button>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
|
||
|
||
{/* This is a placeholder for the dropdown to add a publisher. You'll need to implement or integrate a dropdown component */}
|
||
|
||
<div className="flex space-x-2 items-center">
|
||
{/* Add Button */}
|
||
<button onClick={() => setIsModalOpen(true)} className="bg-blue-500 text-white p-2 py-1 rounded-md">
|
||
добави участник{/* Placeholder for Add icon */}
|
||
</button>
|
||
{assignments.length == 0 && (
|
||
<button onClick={() => deleteShift(shift.id)} className="bg-red-500 text-white p-2 py-1 rounded-md"
|
||
>изтрий смяната</button>
|
||
)}
|
||
</div>
|
||
|
||
{/* Modal for Publisher Search
|
||
forDate={new Date(shift.startTime)}
|
||
*/}
|
||
<Modal isOpen={isModalOpen}
|
||
onClose={() => setIsModalOpen(false)}
|
||
forDate={new Date(shift.startTime)}
|
||
useFilterDate={useFilterDate}
|
||
onUseFilterDateChange={(value) => setUseFilterDate(value)}>
|
||
|
||
<PublisherSearchBox
|
||
selectedId={null}
|
||
isFocused={isModalOpen}
|
||
filterDate={useFilterDate ? new Date(shift.startTime) : null}
|
||
onChange={(publisher) => {
|
||
// Add publisher as assignment logic
|
||
setIsModalOpen(false);
|
||
addAssignment(publisher, shift.id);
|
||
}}
|
||
showAllAuto={true}
|
||
showSearch={true}
|
||
showList={false}
|
||
/>
|
||
</Modal>
|
||
</div >
|
||
)}</>
|
||
);
|
||
}
|
||
|
||
export default ShiftComponent;
|