// Next.js page to show all locations in the database with a link to the location page import { useSession } from "next-auth/react"; import { useEffect, useState, useRef, use } from "react"; import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { DesktopDatePicker } from '@mui/x-date-pickers/DesktopDatePicker'; import dayjs from 'dayjs'; // import { getSession } from 'next-auth/client' // import { NextAuth } from 'next-auth/client' import { Publisher, UserRole } from "@prisma/client"; import Layout from "../../../components/layout"; import PublisherCard from "../../../components/publisher/PublisherCard"; import axiosInstance from "../../../src/axiosSecure"; import axiosServer from '../../../src/axiosServer'; const common = require("../../../src/helpers/common"); import toast from "react-hot-toast"; import { levenshteinEditDistance } from "levenshtein-edit-distance"; import ProtectedRoute from '../../../components/protectedRoute'; import ConfirmationModal from '../../../components/ConfirmationModal'; import { relative } from "path"; import { set } from "lodash"; interface IProps { initialItems: Publisher[]; } function PublishersPage({ publishers = [] }: IProps) { const [shownPubs, setShownPubs] = useState(publishers); const [filter, setFilter] = useState(""); const [showZeroShiftsOnly, setShowZeroShiftsOnly] = useState(false); const [selectedDate, setSelectedDate] = useState(null); const [filterIsImported, setFilterIsImported] = useState({ checked: false, indeterminate: true, }); // const cbRefFilterTraining = useRef(null); // const getCheckboxState = (currentState: boolean | null) => { // if (currentState === true) return 'unchecked'; // if (currentState === false) return 'checked'; // return 'indeterminate'; // }; // const cbRefFilterTraining = useRef(null); // const [cbFilterTrainingState, setcbFilterTrainingState] = useState(null); // useEffect(() => { // if (cbRefFilterTraining.current) { // cbRefFilterTraining.current.indeterminate = cbFilterTrainingState === null; // } // }, [cbFilterTrainingState]); const [flterNoTraining, setFilterNoTraining] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [isModalOpenDeleteAllVisible, setIsModalOpenDeleteAllVisible] = useState(false); const [isModalOpenDeleteAllAvaillabilities, setIsModalOpenDeleteAllAvaillabilities] = useState(false); const handleDeleteAllVisible = async () => { setIsDeleting(true); for (const publisher of shownPubs) { try { await axiosInstance.delete(`/api/data/publishers/${publisher.id}`); setShownPubs(shownPubs.filter(p => p.id !== publisher.id)); } catch (error) { console.log(JSON.stringify(error)); } } setIsDeleting(false); setIsModalOpenDeleteAllVisible(false); }; const handleDeleteAllAvailabilities = async () => { setIsDeleting(true); try { const today = new Date(); const targetDate = new Date(today.setDate(today.getDate() - 35)); await axiosInstance.post('/api/?action=deleteAllAvailabilities', { date: targetDate }); } catch (error) { console.error(JSON.stringify(error)); // Log the error } setIsDeleting(false); setIsModalOpenDeleteAllAvaillabilities(false); }; useEffect(() => { // const filteredPublishers = publishers.filter((publisher) => { // return publisher.firstName.toLowerCase().includes(filter.toLowerCase()) // || publisher.lastName.toLowerCase().includes(filter.toLowerCase()); // }); //name filter let filteredPublishersByName = publishers .filter((publisher) => { const fullName = publisher.firstName.toLowerCase() + " " + publisher.lastName.toLowerCase(); const distance = levenshteinEditDistance(fullName, filter.toLowerCase()); const lenDiff = Math.max(fullName.length, filter.length) - Math.min(fullName.length, filter.length); let similarity; if (distance === 0) { similarity = 1; // Exact match } else { similarity = 1 - (distance - lenDiff) / distance; } //console.log("distance: " + distance + "; lenDiff: " + lenDiff + " similarity: " + similarity + "; " + fullName + " =? " + filter + "") return similarity >= 0.95; }); // Email filter let filteredPublishersByEmail = publishers.filter(publisher => publisher.email.toLowerCase().includes(filter.toLowerCase()) ); // Combine name and email filters, removing duplicates let filteredPublishers = [...new Set([...filteredPublishersByName, ...filteredPublishersByEmail])]; // Zero shifts (inactive) and date filter if (showZeroShiftsOnly && selectedDate) { filteredPublishers = publishers.filter(publisher => { // If no assignments at all, include in results if (publisher.assignments.length === 0) return true; // Only include publishers who don't have any assignments after the selected date return !publisher.assignments.some(assignment => { const shiftDate = dayjs(assignment.shift.startTime); return shiftDate.isAfter(selectedDate); }); }); } else if (showZeroShiftsOnly) { // If checkbox is checked but no date selected, show publishers with no assignments filteredPublishers = publishers.filter(publisher => publisher.assignments.length === 0); } // trained filter if (flterNoTraining) { filteredPublishers = filteredPublishers.filter(p => p.isTrained === false); } setShownPubs(filteredPublishers); }, [filter, showZeroShiftsOnly, selectedDate, flterNoTraining]); // Separate effect to handle date reset when checkbox is unchecked useEffect(() => { if (!showZeroShiftsOnly) { setSelectedDate(null); } }, [showZeroShiftsOnly]); const renderPublishers = () => { if (shownPubs.length === 0) { return (
); } else { return shownPubs.map((publisher) => ( )); } }; const handleFilterChange = (event: React.ChangeEvent) => { const { name, value, type, checked } = event.target; // setFilter(event.target.value); if (type === 'text') { setFilter(value); } else if (type === 'checkbox') { if (name === 'filterIsImported') { setFilterIsImported({ checked, indeterminate: false }); } if (name === 'filterTrained') { // const nextState = cbFilterTrainingState === false ? null : cbFilterTrainingState === null ? true : false; // setcbFilterTrainingState(nextState); setFilterNoTraining(checked); } } }; const exportPublishers = async () => { try { const response = await axiosInstance.get('/api/?action=exportPublishersExcel', { responseType: 'arraybuffer' }); // Get filename from Content-Disposition header const contentDisposition = response.headers['content-disposition']; let filename = 'publishers.xlsx'; // default fallback if (contentDisposition) { const filenameMatch = contentDisposition.match(/filename=(.*?)(;|$)/); if (filenameMatch) { filename = decodeURI(filenameMatch[1]); } } const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); a.remove(); window.URL.revokeObjectURL(url); // Clean up } catch (error) { console.error(JSON.stringify(error)); toast.error("Грешка при експорт на данни"); } } return (
setIsModalOpenDeleteAllVisible(false)} onConfirm={handleDeleteAllVisible} message="Сигурни ли сте, че искате да изтриете всички показани в момента вестители?" /> setIsModalOpenDeleteAllAvaillabilities(false)} onConfirm={handleDeleteAllAvailabilities} message="Сигурни ли сте, че искате да изтриете предпочитанията на ВСИЧКИ вестители?" /> {/* export by calling excel helper .ExportPublishersToExcel() */}
{showZeroShiftsOnly && (
след: setSelectedDate(newDate)} slotProps={{ textField: { size: "small", sx: { width: '140px', '& .MuiInputBase-input': { padding: '4px 8px', fontSize: '0.875rem' } } }, mobileWrapper: { sx: { '& .MuiPickersLayout-root': { minWidth: 'unset' } } } }} format="DD.MM.YYYY" className="min-w-[120px]" />
)} {shownPubs.length} от {publishers.length} вестителя
{renderPublishers()}
); } export default PublishersPage; //import {set} from "date-fns"; export const getServerSideProps = async (context) => { // const axios = await axiosServer(context); // //ToDo: refactor all axios calls to use axiosInstance and this URL // const {data: publishers } = await axios.get('/api/data/publishers?select=id,firstName,lastName,email,isActive,isTrained,isImported,assignments.shift.startTime,availabilities.startTime&dev=fromuseefect'); //use prisma instead of axios const prisma = common.getPrismaClient(); let publishers = await prisma.publisher.findMany({ select: { id: true, firstName: true, lastName: true, email: true, isActive: true, isTrained: true, isImported: true, assignments: { select: { shift: { select: { startTime: true, }, }, }, }, availabilities: { select: { startTime: true, }, }, } }); publishers = JSON.parse(JSON.stringify(publishers)); return { props: { publishers, }, }; };