Files
mwitnessing/pages/cart/publishers/index.tsx
Dobromir Popov acd776e988 renames
2024-03-26 01:08:57 +02:00

237 lines
9.0 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.

// 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 { 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';
import toast from "react-hot-toast";
import { levenshteinEditDistance } from "levenshtein-edit-distance";
import ProtectedRoute from '../../../components/protectedRoute';
import ConfirmationModal from '../../../components/ConfirmationModal';
interface IProps {
initialItems: Publisher[];
}
function PublishersPage({ publishers = [] }: IProps) {
const [shownPubs, setShownPubs] = useState(publishers);
const [filter, setFilter] = useState("");
const [filterIsImported, setFilterIsImported] = useState({
checked: false,
indeterminate: true,
});
const [showZeroShiftsOnly, setShowZeroShiftsOnly] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [isModalOpen, setIsModalOpen] = 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);
setIsModalOpen(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);
setIsModalOpen(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])];
// inactive publishers filter
filteredPublishers = showZeroShiftsOnly
? filteredPublishers.filter(p => p.assignments.length === 0)
: filteredPublishers;
setShownPubs(filteredPublishers);
}, [filter, showZeroShiftsOnly]);
const checkboxRef = useRef();
const renderPublishers = () => {
if (shownPubs.length === 0) {
return (
<div className="flex justify-center">
<a
className="btn"
href="javascript:void(0);"
onClick={() => {
setFilter("");
handleFilterChange({ target: { value: "" } });
}}
>
Clear filters
</a>
</div>
);
}
else {
return shownPubs.map((publisher) => (
<PublisherCard key={publisher.id} publisher={publisher} />
));
}
};
const handleFilterChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value, type, checked } = event.target;
// setFilter(event.target.value);
if (type === 'text') {
setFilter(value);
} else if (type === 'checkbox') {
// setFilterIsImported({ ...checkboxFilter, [name]: checked });
const { checked, indeterminate } = checkboxRef.current;
if (!checked && !indeterminate) {
// Checkbox was unchecked, set it to indeterminate state
checkboxRef.current.indeterminate = true;
setFilterIsImported({ checked: false, indeterminate: true });
} else if (!checked && indeterminate) {
// Checkbox was indeterminate, set it to checked state
checkboxRef.current.checked = true;
checkboxRef.current.indeterminate = false;
setFilterIsImported({ checked: true, indeterminate: false });
} else if (checked && !indeterminate) {
// Checkbox was checked, set it to unchecked state
checkboxRef.current.checked = false;
checkboxRef.current.indeterminate = false;
setFilterIsImported({ checked: false, indeterminate: false });
} else {
// Checkbox was checked and indeterminate (should not happen), set it to unchecked state
checkboxRef.current.checked = false;
checkboxRef.current.indeterminate = false;
setFilterIsImported({ checked: false, indeterminate: false });
}
}
};
return (
<Layout>
<ProtectedRoute allowedRoles={[UserRole.ADMIN, UserRole.POWERUSER]}>
<div className="">
<div className="flex items-center justify-center space-x-4 m-4">
<div className="flex justify-center m-4">
<a href="/cart/publishers/new" className="btn"> Добави вестител </a>
</div>
<button className="button m-2 btn btn-danger" onClick={() => setIsModalOpen(true)} disabled={isDeleting} >
{isDeleting ? "Изтриване..." : "Изтрий показаните вестители"}
</button>
<ConfirmationModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
onConfirm={handleDeleteAllVisible}
message="Сигурни ли сте, че искате да изтриете всички показани в момента вестители?"
/>
<button className="button m-2 btn btn-danger" onClick={() => setIsModalOpen(true)} disabled={isDeleting} >
{isDeleting ? "Изтриване..." : "Изтрий ВСИЧКИ предпочитания"}
</button>
<ConfirmationModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
onConfirm={handleDeleteAllAvailabilities}
message="Сигурни ли сте, че искате да изтриете предпочитанията на ВСИЧКИ вестители?"
/>
<div className="flex justify-center m-4">
<a href="/cart/publishers/import" className="btn"> Import publishers </a>
</div>
</div>
<div name="filters" className="flex items-center justify-center space-x-4 m-4 sticky top-4 z-10 bg-gray-100 p-2">
<label htmlFor="filter">Filter:</label>
<input type="text" id="filter" name="filter" value={filter} onChange={handleFilterChange}
className="border border-gray-300 rounded-md px-2 py-1"
/>
<label htmlFor="zeroShiftsOnly" className="ml-4 inline-flex items-center">
<input type="checkbox" id="zeroShiftsOnly" checked={showZeroShiftsOnly}
onChange={e => setShowZeroShiftsOnly(e.target.checked)}
className="form-checkbox text-indigo-600"
/>
<span className="ml-2">само без смени</span>
</label>
<span id="filter-info" className="ml-4">{publishers.length} от {publishers.length} вестителя</span>
</div>
<div className="grid gap-4 grid-cols-1 md:grid-cols-4 z-0">
{renderPublishers()}
</div>
</div>
</ProtectedRoute>
</Layout>
);
}
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');
return {
props: {
publishers,
},
};
};