Add assignment UI change in calendar
This commit is contained in:
10
_doc/ToDo.md
10
_doc/ToDo.md
@ -262,4 +262,12 @@ in schedule admin - if a publisher is always pair & family is not in the shift -
|
|||||||
[] allow blocking of inputs (different from publishing) TODO: fix to keep previous occurances when repeating evert week
|
[] allow blocking of inputs (different from publishing) TODO: fix to keep previous occurances when repeating evert week
|
||||||
[] user - add createdAt field
|
[] user - add createdAt field
|
||||||
|
|
||||||
[] FIX insecure logins
|
[x] FIX insecure logins
|
||||||
|
|
||||||
|
[] nove push to form, - reorganize pWAManager to have session, role, subscriptions, etc...
|
||||||
|
[] add shift name in calendar/ show in schedule if no assignments.
|
||||||
|
[] show unpublished schedule if admin
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import e from 'express';
|
|||||||
import ProtectedRoute from './protectedRoute';
|
import ProtectedRoute from './protectedRoute';
|
||||||
import { UserRole } from '@prisma/client';
|
import { UserRole } from '@prisma/client';
|
||||||
|
|
||||||
function PwaManager({ subs }) {
|
function PwaManager({ userId, subs }) {
|
||||||
//ToDo: for iOS, try to use apn? https://github.com/node-apn/node-apn/blob/master/doc/apn.markdown
|
//ToDo: for iOS, try to use apn? https://github.com/node-apn/node-apn/blob/master/doc/apn.markdown
|
||||||
const isSupported = () =>
|
const isSupported = () =>
|
||||||
'Notification' in window &&
|
'Notification' in window &&
|
||||||
@ -271,6 +271,37 @@ function PwaManager({ subs }) {
|
|||||||
{ action: 'close', title: 'Затвори', icon: '❌' }]
|
{ action: 'close', title: 'Затвори', icon: '❌' }]
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
await fetch('/api/notify', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: pub.id,
|
||||||
|
message: "Тестово съобщение",
|
||||||
|
title: "Това е тестово съобщение от https://sofia.mwitnessing.com",
|
||||||
|
actions: [
|
||||||
|
{ action: 'OK', title: 'OK', icon: '✅' },
|
||||||
|
{ action: 'close', title: 'Затвори', icon: '❌' }
|
||||||
|
]
|
||||||
|
// actions: [
|
||||||
|
// {
|
||||||
|
// title: 'Open URL',
|
||||||
|
// action: 'open_url',
|
||||||
|
// icon: '/images/open-url.png'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Dismiss',
|
||||||
|
// action: 'dismiss',
|
||||||
|
// icon: '/images/dismiss.png'
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// async function sendTestReminder(event: MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> {
|
// async function sendTestReminder(event: MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> {
|
||||||
@ -382,7 +413,7 @@ function PwaManager({ subs }) {
|
|||||||
>
|
>
|
||||||
Тестово уведомление
|
Тестово уведомление
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div >
|
||||||
{isAdmin &&
|
{isAdmin &&
|
||||||
<div>
|
<div>
|
||||||
{/* <button
|
{/* <button
|
||||||
@ -403,28 +434,31 @@ function PwaManager({ subs }) {
|
|||||||
</button> */}
|
</button> */}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{notificationPermission !== "granted" && (
|
{
|
||||||
<button
|
notificationPermission !== "granted" && (
|
||||||
onClick={togglePushNotifications}
|
<button
|
||||||
className={`text-xs py-1 px-2 rounded-full focus:outline-none transition duration-150 ease-in-out ${notificationPermission === "denied" ? 'bg-red-500 hover:bg-red-700 text-white' : 'bg-green-500 hover:bg-green-700 text-white'
|
onClick={togglePushNotifications}
|
||||||
}`}
|
className={`text-xs py-1 px-2 rounded-full focus:outline-none transition duration-150 ease-in-out ${notificationPermission === "denied" ? 'bg-red-500 hover:bg-red-700 text-white' : 'bg-green-500 hover:bg-green-700 text-white'
|
||||||
>
|
}`}
|
||||||
{notificationPermission === "denied" ? 'Notifications Denied!' : 'Enable Notifications'}
|
>
|
||||||
</button>
|
{notificationPermission === "denied" ? 'Notifications Denied!' : 'Enable Notifications'}
|
||||||
)}
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{isAdmin && <div>
|
{
|
||||||
<div>
|
isAdmin && <div>
|
||||||
<a href="https://t.me/mwhitnessing_bot" className="inline-flex items-center ml-4" target="_blank">
|
<div>
|
||||||
<img src="/content/icons/telegram-svgrepo-com.svg" alt="Телеграм" width="32" height="32" className="align-middle" />
|
<a href="https://t.me/mwhitnessing_bot" className="inline-flex items-center ml-4" target="_blank">
|
||||||
<span className="align-middle">Телеграм</span>
|
<img src="/content/icons/telegram-svgrepo-com.svg" alt="Телеграм" width="32" height="32" className="align-middle" />
|
||||||
</a>
|
<span className="align-middle">Телеграм</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
<a href="/api/auth/apple-signin" className="inline-flex items-center ml-4 bg-gray-100 button" target="_blank">
|
<a href="/api/auth/apple-signin" className="inline-flex items-center ml-4 bg-gray-100 button" target="_blank">
|
||||||
<span className="align-middle">Apple sign-in</span>
|
<span className="align-middle">Apple sign-in</span>
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -303,7 +303,8 @@ export default function PublisherForm({ item, me }) {
|
|||||||
{/* In-App notifications group */}
|
{/* In-App notifications group */}
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h3 className="text-md font-semibold mb-2">Известия в приложението</h3>
|
<h3 className="text-md font-semibold mb-2">Известия в приложението</h3>
|
||||||
<PwaManager />
|
<PwaManager userId={publisher.userId || session.user.id} />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
128
components/publisher/PublisherShiftsModal.js
Normal file
128
components/publisher/PublisherShiftsModal.js
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
//Refactor ToDo: show the whole month instead of just the current week by showing the shift start time in front of the rows, and show all shifts in the month from the first to the last week in the cell where we show one shift now
|
||||||
|
function PublisherShiftsModal({ publisher, shifts, onClose }) {
|
||||||
|
const monthInfo = common.getMonthDatesInfo(new Date(value));
|
||||||
|
const monthShifts = shifts.filter(shift => {
|
||||||
|
const shiftDate = new Date(shift.startTime);
|
||||||
|
return shiftDate > monthInfo.firstDay && shiftDate < monthInfo.lastDay;
|
||||||
|
});
|
||||||
|
const weekShifts = monthShifts.filter(shift => {
|
||||||
|
const shiftDate = new Date(shift.startTime);
|
||||||
|
return common.getStartOfWeek(value) <= shiftDate && shiftDate <= common.getEndOfWeek(value);
|
||||||
|
});
|
||||||
|
const dayShifts = weekShifts.map(shift => {
|
||||||
|
const isAvailable = publisher.availabilities?.some(avail =>
|
||||||
|
avail.startTime <= shift.startTime && avail.endTime >= shift.endTime
|
||||||
|
);
|
||||||
|
let color = isAvailable ? getColorForShift(shift) : 'bg-gray-300';
|
||||||
|
if (shift.isFromPreviousMonth) {
|
||||||
|
color += ' border-l-4 border-orange-500 ';
|
||||||
|
}
|
||||||
|
if (shift.isFromPreviousAssignment) {
|
||||||
|
color += ' border-l-4 border-red-500 ';
|
||||||
|
}
|
||||||
|
return { ...shift, isAvailable, color };
|
||||||
|
}).reduce((acc, shift) => {
|
||||||
|
const dayIndex = new Date(shift.startTime).getDay();
|
||||||
|
acc[dayIndex] = acc[dayIndex] || [];
|
||||||
|
acc[dayIndex].push(shift);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
console.log("dayShifts:", dayShifts);
|
||||||
|
|
||||||
|
const hasAssignment = (shiftId) => {
|
||||||
|
// return publisher.assignments.some(ass => ass.shift.id == shiftId);
|
||||||
|
return publisher.assignments?.some(ass => {
|
||||||
|
//console.log(`Comparing: ${ass.shift.id} to ${shiftId}: ${ass.shift.id === shiftId}`);
|
||||||
|
return ass.shift.id === shiftId;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (event) => {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
console.log('ESC: closing modal.');
|
||||||
|
onClose(); // Call the onClose function when ESC key is pressed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add event listener
|
||||||
|
window.addEventListener('keydown', handleKeyDown);
|
||||||
|
|
||||||
|
// Remove event listener on cleanup
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('keydown', handleKeyDown);
|
||||||
|
};
|
||||||
|
}, [onClose]); // Include onClose in the dependency array
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed top-0 left-0 w-full h-full flex items-center justify-center bg-black bg-opacity-50 z-50">
|
||||||
|
<div className="relative bg-white p-8 rounded-lg shadow-xl max-w-xl w-full h-auto overflow-y-auto">
|
||||||
|
<h2 className="text-xl font-semibold mb-4">График на <span title={publisher.email} className='publisher'>
|
||||||
|
<strong>{publisher.firstName} {publisher.lastName}</strong>
|
||||||
|
<span className="publisher-tooltip" onClick={common.copyToClipboard}>{publisher.email}</span>
|
||||||
|
</span> тази седмица:</h2>
|
||||||
|
|
||||||
|
{/* ... Display shifts in a calendar-like UI ... */}
|
||||||
|
<div className="grid grid-cols-6 gap-4 mb-4">
|
||||||
|
{Object.entries(dayShifts).map(([dayIndex, shiftsForDay]) => (
|
||||||
|
<div key={dayIndex} className="flex flex-col space-y-2 justify-end">
|
||||||
|
{/* Day header */}
|
||||||
|
<div className="text-center font-medium">{new Date(shiftsForDay[0].startTime).getDate()}-ти</div>
|
||||||
|
|
||||||
|
{shiftsForDay.map((shift, index) => {
|
||||||
|
const assignmentExists = hasAssignment(shift.id);
|
||||||
|
const availability = publisher.availabilities.find(avail =>
|
||||||
|
avail.startTime <= shift.startTime && avail.endTime >= shift.endTime
|
||||||
|
);
|
||||||
|
const isFromPrevMonth = availability && availability.isFromPreviousMonth;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`text-sm text-white p-2 rounded-md ${isFromPrevMonth ? 'border-l-6 border-black-500' : ''} ${shift.color} ${assignmentExists ? 'border-2 border-blue-500' : ""} h-24 flex flex-col justify-center`}
|
||||||
|
>
|
||||||
|
{common.getTimeRange(shift.startTime, shift.endTime)} {shift.id}
|
||||||
|
|
||||||
|
{!assignmentExists && shift.isAvailable && (
|
||||||
|
<button onClick={() => { addAssignment(publisher, shift.id); }}
|
||||||
|
className="mt-2 bg-green-500 text-white p-1 rounded hover:bg-green-600 active:bg-green-700 focus:outline-none"
|
||||||
|
>
|
||||||
|
добави
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{assignmentExists && (
|
||||||
|
<button onClick={() => { removeAssignment(publisher, shift.id) }} // Implement the removeAssignment function
|
||||||
|
className="mt-2 bg-red-500 text-white p-1 rounded hover:bg-red-600 active:bg-red-700 focus:outline-none"
|
||||||
|
>
|
||||||
|
махни
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Close button in the top right corner */}
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="absolute top-3 right-2 p-2 px-3 bg-red-500 text-white rounded-full hover:bg-red-600 active:bg-red-700 focus:outline-none"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* <Link href={`/cart/publishers/edit/${modalPub.id}`}
|
||||||
|
className="mt-2 bg-blue-500 text-white p-1 rounded hover:bg-blue-600 active:bg-blue-700 focus:outline-none">
|
||||||
|
<i className="fas fa-edit" />
|
||||||
|
</Link> */}
|
||||||
|
{/* Edit button in the top right corner, next to the close button */}
|
||||||
|
<Link href={`/cart/publishers/edit/${modalPub.id}`} className="absolute top-3 right-12 p-2 bg-blue-500 text-white rounded-full hover:bg-blue-600 active:bg-blue-700 focus:outline-none">
|
||||||
|
<i className="fas fa-edit" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div >
|
||||||
|
);
|
||||||
|
}
|
@ -152,15 +152,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
//bold the text after - in the notes
|
//bold the text after - in the notes
|
||||||
notes: notes,
|
notes: notes,
|
||||||
notes_bold: notes_bold,
|
notes_bold: notes_bold,
|
||||||
names: shift.assignments
|
names: shift.assignments.length > 0
|
||||||
.map((assignment) => {
|
? shift.assignments
|
||||||
return (
|
.map((assignment) => {
|
||||||
assignment.publisher.firstName +
|
return (
|
||||||
" " +
|
assignment.publisher.firstName +
|
||||||
assignment.publisher.lastName
|
" " +
|
||||||
);
|
assignment.publisher.lastName
|
||||||
})
|
);
|
||||||
.join(", "),
|
})
|
||||||
|
.join(", ")
|
||||||
|
: shift.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (shiftSchedule.names.length > 0) {
|
if (shiftSchedule.names.length > 0) {
|
||||||
|
@ -9,6 +9,7 @@ import Shift from '../../../components/calendar/ShiftComponent';
|
|||||||
import { DayOfWeek, UserRole } from '@prisma/client';
|
import { DayOfWeek, UserRole } from '@prisma/client';
|
||||||
import { env } from 'process'
|
import { env } from 'process'
|
||||||
import ShiftComponent from '../../../components/calendar/ShiftComponent';
|
import ShiftComponent from '../../../components/calendar/ShiftComponent';
|
||||||
|
import PublisherShiftsModal from '../../../components/publisher/PublisherShiftsModal';
|
||||||
//import { set } from 'date-fns';
|
//import { set } from 'date-fns';
|
||||||
const common = require('src/helpers/common');
|
const common = require('src/helpers/common');
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
@ -66,6 +67,7 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
|||||||
const [shifts, setShifts] = React.useState([]);
|
const [shifts, setShifts] = React.useState([]);
|
||||||
const [error, setError] = React.useState(null);
|
const [error, setError] = React.useState(null);
|
||||||
const [availablePubs, setAvailablePubs] = React.useState([]);
|
const [availablePubs, setAvailablePubs] = React.useState([]);
|
||||||
|
const [selectedPublisher, setSelectedPublisher] = React.useState(null);
|
||||||
|
|
||||||
const [selectedShiftId, setSelectedShiftId] = useState(null);
|
const [selectedShiftId, setSelectedShiftId] = useState(null);
|
||||||
|
|
||||||
@ -214,8 +216,8 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectedPublisher = (publisher) => {
|
const handleSelectedPublisher = (publisher) => {
|
||||||
// Do something with the selected publisher
|
|
||||||
console.log("handle pub clicked:", publisher);
|
console.log("handle pub clicked:", publisher);
|
||||||
|
setSelectedPublisher(publisher);
|
||||||
}
|
}
|
||||||
const handlePublisherModalOpen = async (publisher) => {
|
const handlePublisherModalOpen = async (publisher) => {
|
||||||
// Do something with the selected publisher
|
// Do something with the selected publisher
|
||||||
@ -365,6 +367,10 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
|||||||
isConfirmed: true
|
isConfirmed: true
|
||||||
};
|
};
|
||||||
const { data } = await axiosInstance.post("/api/data/assignments", newAssignment);
|
const { data } = await axiosInstance.post("/api/data/assignments", newAssignment);
|
||||||
|
if (selectedShiftId == shiftId) {
|
||||||
|
handleShiftSelection(shifts.find(shift => shift.id === shiftId));
|
||||||
|
}
|
||||||
|
|
||||||
// Update the 'publisher' property of the returned data with the full publisher object
|
// Update the 'publisher' property of the returned data with the full publisher object
|
||||||
data.publisher = publisher;
|
data.publisher = publisher;
|
||||||
data.shift = shifts.find(shift => shift.id === shiftId);
|
data.shift = shifts.find(shift => shift.id === shiftId);
|
||||||
@ -816,6 +822,7 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
|||||||
${bgAndBorderColorClass} ${selectedBorderClass} ${activeOpacityClass}
|
${bgAndBorderColorClass} ${selectedBorderClass} ${activeOpacityClass}
|
||||||
${pub.currentMonthAssignments >= pub.desiredShiftsPerMonth ? 'text-gray-400' : 'text-gray-800'}`}
|
${pub.currentMonthAssignments >= pub.desiredShiftsPerMonth ? 'text-gray-400' : 'text-gray-800'}`}
|
||||||
onDoubleClick={(handlePublisherModalOpen.bind(this, pub))}
|
onDoubleClick={(handlePublisherModalOpen.bind(this, pub))}
|
||||||
|
onClick={handleSelectedPublisher.bind(this, pub)}
|
||||||
>
|
>
|
||||||
<span className={`${pub.isAvailableForShift ? 'font-bold' : 'font-medium'} `}>
|
<span className={`${pub.isAvailableForShift ? 'font-bold' : 'font-medium'} `}>
|
||||||
{pub.firstName} {pub.lastName}
|
{pub.firstName} {pub.lastName}
|
||||||
@ -831,33 +838,8 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
|||||||
<button tooltip="желани участия на месец" title="желани участия" className={`badge py-1 px-2 rounded-md text-xs ${pub.desiredShiftsPerMonth ? 'bg-purple-500 text-white' : 'bg-purple-200 text-gray-400'}`}>{pub.desiredShiftsPerMonth || 0}</button>
|
<button tooltip="желани участия на месец" title="желани участия" className={`badge py-1 px-2 rounded-md text-xs ${pub.desiredShiftsPerMonth ? 'bg-purple-500 text-white' : 'bg-purple-200 text-gray-400'}`}>{pub.desiredShiftsPerMonth || 0}</button>
|
||||||
<button tooltip="push" title="push" className={`badge py-1 px-2 rounded-md text-xs bg-red-100`}
|
<button tooltip="push" title="push" className={`badge py-1 px-2 rounded-md text-xs bg-red-100`}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await fetch('/api/notify', {
|
if (selectedPublisher && selectedShiftId)
|
||||||
method: 'POST',
|
addAssignment(selectedPublisher, selectedShiftId);
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
id: pub.id,
|
|
||||||
message: "Тестово съобщение",
|
|
||||||
title: "Това е тестово съобщение от https://sofia.mwitnessing.com",
|
|
||||||
actions: [
|
|
||||||
{ action: 'OK', title: 'OK', icon: '✅' },
|
|
||||||
{ action: 'close', title: 'Затвори', icon: '❌' }
|
|
||||||
]
|
|
||||||
// actions: [
|
|
||||||
// {
|
|
||||||
// title: 'Open URL',
|
|
||||||
// action: 'open_url',
|
|
||||||
// icon: '/images/open-url.png'
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// title: 'Dismiss',
|
|
||||||
// action: 'dismiss',
|
|
||||||
// icon: '/images/dismiss.png'
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}}
|
}}
|
||||||
>+</button>
|
>+</button>
|
||||||
|
|
||||||
@ -905,133 +887,6 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
function PublisherShiftsModal({ publisher, shifts, onClose }) {
|
|
||||||
const monthInfo = common.getMonthDatesInfo(new Date(value));
|
|
||||||
const monthShifts = shifts.filter(shift => {
|
|
||||||
const shiftDate = new Date(shift.startTime);
|
|
||||||
return shiftDate > monthInfo.firstDay && shiftDate < monthInfo.lastDay;
|
|
||||||
});
|
|
||||||
const weekShifts = monthShifts.filter(shift => {
|
|
||||||
const shiftDate = new Date(shift.startTime);
|
|
||||||
return common.getStartOfWeek(value) <= shiftDate && shiftDate <= common.getEndOfWeek(value);
|
|
||||||
});
|
|
||||||
const dayShifts = weekShifts.map(shift => {
|
|
||||||
const isAvailable = publisher.availabilities?.some(avail =>
|
|
||||||
avail.startTime <= shift.startTime && avail.endTime >= shift.endTime
|
|
||||||
);
|
|
||||||
let color = isAvailable ? getColorForShift(shift) : 'bg-gray-300';
|
|
||||||
if (shift.isFromPreviousMonth) {
|
|
||||||
color += ' border-l-4 border-orange-500 ';
|
|
||||||
}
|
|
||||||
if (shift.isFromPreviousAssignment) {
|
|
||||||
color += ' border-l-4 border-red-500 ';
|
|
||||||
}
|
|
||||||
return { ...shift, isAvailable, color };
|
|
||||||
}).reduce((acc, shift) => {
|
|
||||||
const dayIndex = new Date(shift.startTime).getDay();
|
|
||||||
acc[dayIndex] = acc[dayIndex] || [];
|
|
||||||
acc[dayIndex].push(shift);
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
console.log("dayShifts:", dayShifts);
|
|
||||||
|
|
||||||
const hasAssignment = (shiftId) => {
|
|
||||||
// return publisher.assignments.some(ass => ass.shift.id == shiftId);
|
|
||||||
return publisher.assignments?.some(ass => {
|
|
||||||
//console.log(`Comparing: ${ass.shift.id} to ${shiftId}: ${ass.shift.id === shiftId}`);
|
|
||||||
return ass.shift.id === shiftId;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleKeyDown = (event) => {
|
|
||||||
if (event.key === 'Escape') {
|
|
||||||
console.log('ESC: closing modal.');
|
|
||||||
onClose(); // Call the onClose function when ESC key is pressed
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add event listener
|
|
||||||
window.addEventListener('keydown', handleKeyDown);
|
|
||||||
|
|
||||||
// Remove event listener on cleanup
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('keydown', handleKeyDown);
|
|
||||||
};
|
|
||||||
}, [onClose]); // Include onClose in the dependency array
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="fixed top-0 left-0 w-full h-full flex items-center justify-center bg-black bg-opacity-50 z-50">
|
|
||||||
<div className="relative bg-white p-8 rounded-lg shadow-xl max-w-xl w-full h-auto overflow-y-auto">
|
|
||||||
<h2 className="text-xl font-semibold mb-4">График на <span title={publisher.email} className='publisher'>
|
|
||||||
<strong>{publisher.firstName} {publisher.lastName}</strong>
|
|
||||||
<span className="publisher-tooltip" onClick={common.copyToClipboard}>{publisher.email}</span>
|
|
||||||
</span> тази седмица:</h2>
|
|
||||||
|
|
||||||
{/* ... Display shifts in a calendar-like UI ... */}
|
|
||||||
<div className="grid grid-cols-6 gap-4 mb-4">
|
|
||||||
{Object.entries(dayShifts).map(([dayIndex, shiftsForDay]) => (
|
|
||||||
<div key={dayIndex} className="flex flex-col space-y-2 justify-end">
|
|
||||||
{/* Day header */}
|
|
||||||
<div className="text-center font-medium">{new Date(shiftsForDay[0].startTime).getDate()}-ти</div>
|
|
||||||
|
|
||||||
{shiftsForDay.map((shift, index) => {
|
|
||||||
const assignmentExists = hasAssignment(shift.id);
|
|
||||||
const availability = publisher.availabilities.find(avail =>
|
|
||||||
avail.startTime <= shift.startTime && avail.endTime >= shift.endTime
|
|
||||||
);
|
|
||||||
const isFromPrevMonth = availability && availability.isFromPreviousMonth;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className={`text-sm text-white p-2 rounded-md ${isFromPrevMonth ? 'border-l-6 border-black-500' : ''} ${shift.color} ${assignmentExists ? 'border-2 border-blue-500' : ""} h-24 flex flex-col justify-center`}
|
|
||||||
>
|
|
||||||
{common.getTimeRange(shift.startTime, shift.endTime)} {shift.id}
|
|
||||||
|
|
||||||
{!assignmentExists && shift.isAvailable && (
|
|
||||||
<button onClick={() => { addAssignment(publisher, shift.id); }}
|
|
||||||
className="mt-2 bg-green-500 text-white p-1 rounded hover:bg-green-600 active:bg-green-700 focus:outline-none"
|
|
||||||
>
|
|
||||||
добави
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{assignmentExists && (
|
|
||||||
<button onClick={() => { removeAssignment(publisher, shift.id) }} // Implement the removeAssignment function
|
|
||||||
className="mt-2 bg-red-500 text-white p-1 rounded hover:bg-red-600 active:bg-red-700 focus:outline-none"
|
|
||||||
>
|
|
||||||
махни
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Close button in the top right corner */}
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="absolute top-3 right-2 p-2 px-3 bg-red-500 text-white rounded-full hover:bg-red-600 active:bg-red-700 focus:outline-none"
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* <Link href={`/cart/publishers/edit/${modalPub.id}`}
|
|
||||||
className="mt-2 bg-blue-500 text-white p-1 rounded hover:bg-blue-600 active:bg-blue-700 focus:outline-none">
|
|
||||||
<i className="fas fa-edit" />
|
|
||||||
</Link> */}
|
|
||||||
{/* Edit button in the top right corner, next to the close button */}
|
|
||||||
<Link href={`/cart/publishers/edit/${modalPub.id}`} className="absolute top-3 right-12 p-2 bg-blue-500 text-white rounded-full hover:bg-blue-600 active:bg-blue-700 focus:outline-none">
|
|
||||||
<i className="fas fa-edit" />
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div >
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getColorForShift(shift) {
|
function getColorForShift(shift) {
|
||||||
const assignedCount = shift.assignedCount || 0; // Assuming each shift has an assignedCount property
|
const assignedCount = shift.assignedCount || 0; // Assuming each shift has an assignedCount property
|
||||||
|
Reference in New Issue
Block a user