Files
mwitnessing/components/calendar/ShiftComponent.tsx
2024-02-22 04:19:38 +02:00

241 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react';
import axiosInstance from '../../src/axiosSecure';
import PublisherSearchBox from '../publisher/PublisherSearchBox'; // Update the path
const common = require('src/helpers/common');
interface ModalProps {
children: React.ReactNode;
isOpen: boolean;
onClose: () => void;
forDate: Date;
useFilterDate: boolean;
onUseFilterDateChange: (value: boolean) => void;
}
function Modal({ children, isOpen, onClose, forDate, useFilterDate, onUseFilterDateChange }: ModalProps) {
if (!isOpen) return null;
const isValidDate = forDate instanceof Date && !isNaN(forDate.getTime());
console.log("forDate", forDate, isValidDate);
return (
<div className="fixed inset-0 flex items-center justify-center z-50">
<div className="bg-white p-4 rounded-md shadow-lg modal-content">
{isValidDate && (
<h2 className="text-xl font-bold mb-2">
<label className="cursor-pointer">
<input
type="checkbox"
checked={useFilterDate}
onChange={(e) => onUseFilterDateChange(e.target.checked)}
/>
{` на разположение ${common.getDateFormated(forDate)} или ${common.getDayOfWeekName(forDate)}`}
</label>
</h2>
)}
{children}
<button type="button" onClick={onClose} className="mt-4 text-red-500">
Close
</button>
</div>
<div className="fixed inset-0 bg-black opacity-50 modal-overlay" onClick={onClose}></div>
</div>
);
}
function ShiftComponent({ shift, onShiftSelect, isSelected, onPublisherSelect, allPublishersInfo }) {
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);
// Update assignments when shift changes
useEffect(() => {
setAssignments(shift.assignments);
}, [shift.assignments]);
const handleShiftClick = (shiftId) => {
// console.log("onShiftSelect prop:", onShiftSelect);
// console.log("Shift clicked:", shift);
//shift.selectedPublisher = selectedPublisher;
if (onShiftSelect) {
onShiftSelect(shift);
}
};
const handlePublisherClick = (publisher) => {
//toggle selected
// if (selectedPublisher != null) {
// setSelectedPublisher(null);
// }
// else {
setSelectedPublisher(publisher);
console.log("Publisher clicked:", publisher, "selected publisher:", selectedPublisher);
shift.selectedPublisher = publisher;
if (onShiftSelect) {
onShiftSelect(shift);
}
// if (onPublisherSelect) {
// onPublisherSelect(publisher);
// }
}
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);
};
return (
<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="text-lg font-semibold">
{`${common.getTimeRange(new Date(shift.startTime), new Date(shift.endTime))}`}
</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 = '';
//if there is no publisherInfo - draw red border - publisher is no longer available for the day!
if (!publisherInfo.availabilities || publisherInfo.availabilities.length == 0) {
borderStyles = 'border-2 border-red-500 ';
}
else {
//pub is not available for that shift assignment.
if (publisherInfo.availabilities?.length === 0 ||
publisherInfo.availabilities?.every(avail => avail.isFromPreviousAssignment)) {
borderStyles += 'border-l-3 border-r-3 border-orange-500 '; // Top border for manual publishers
}
// checkig if the publisher is available for this assignment
if (publisherInfo.availabilities?.some(av =>
av.startTime <= ass.startTime &&
av.endTime >= ass.endTime)) {
borderStyles += 'border-t-2 border-red-500 '; // Left border for specific availability conditions
}
//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
// }
if (selectedPublisher && selectedPublisher.id === ass.publisher.id) {
borderStyles += 'border-2 border-blue-300'; // Bottom border for selected publishers
}
if (publisherInfo.hasUpToDateAvailabilities) {
//add green right border
borderStyles += 'border-r-2 border-green-300';
}
}
return (
<div key={index}
className={`flow space-x-2 rounded-md px-2 py-1 my-1 ${ass.isConfirmed ? 'bg-yellow-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>
<button onClick={() => removeAssignment(ass.id)}
className="text-white bg-red-500 hover:bg-red-600 px-3 py-1 ml-2 rounded-md"
>
махни
</button>
</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>
</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;