238 lines
13 KiB
TypeScript
238 lines
13 KiB
TypeScript
import { useState } from 'react';
|
||
import Layout from "../../../components/layout";
|
||
import ProtectedRoute from '../../../components/protectedRoute';
|
||
import { Prisma, UserRole } from '@prisma/client';
|
||
import axiosServer from '../../../src/axiosServer';
|
||
import common from '../../../src/helpers/common';
|
||
// import { filterPublishers, /* other functions */ } from '../../api/index';
|
||
import data from '../../../src/helpers/data';
|
||
|
||
// const data = require('../../src/helpers/data');
|
||
|
||
function ContactsPage({ publishers, allPublishers }) {
|
||
const [searchQuery, setSearchQuery] = useState('');
|
||
|
||
const filteredPublishers = allPublishers.filter((publisher) =>
|
||
publisher.firstName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||
publisher.lastName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||
publisher.email.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||
publisher.phone?.toLowerCase().includes(searchQuery.toLowerCase())
|
||
);
|
||
|
||
return (
|
||
<Layout>
|
||
<ProtectedRoute allowedRoles={[UserRole.ADMIN, UserRole.POWERUSER, UserRole.USER]}>
|
||
<div className="container mx-auto p-4">
|
||
<h1 className="text-xl font-semibold mb-4">Статистика </h1>
|
||
<h5 className="text-lg font-semibold mb-4">{publishers.length} участника с предпочитания за месеца (от {allPublishers.length} )</h5>
|
||
<input
|
||
type="text"
|
||
placeholder="Търси по име, имейл или телефон..."
|
||
value={searchQuery}
|
||
onChange={(e) => setSearchQuery(e.target.value)}
|
||
className="border border-gray-300 rounded-md px-2 py-2 mb-4 w-full text-base md:text-sm"
|
||
/>
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full text-left border-collapse">
|
||
<thead>
|
||
<tr>
|
||
<th className="border-b font-medium p-4 pl-8 pt-0 pb-3">Име</th>
|
||
<th className="border-b font-medium p-4 pt-0 pb-3">Възможности</th>
|
||
<th className="border-b font-medium p-4 pt-0 pb-3">Участия</th>
|
||
<th className="border-b font-medium p-4 pt-0 pb-3">Последно влизане</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{filteredPublishers.map((allPub) => {
|
||
// Find the publisher in the publishers collection to access statistics
|
||
const pub = publishers.find(publisher => publisher.id === allPub.id);
|
||
|
||
return (
|
||
<tr key={allPub.id}>
|
||
<td className="border-b p-4 pl-8" title={allPub.lastUpdate}>{allPub.firstName} {allPub.lastName}</td>
|
||
{/* Display statistics if publisher is found */}
|
||
{pub ? (
|
||
<>
|
||
<td className="border-b p-4">
|
||
<span title="Възможност: часове | дни" className={`badge py-1 px-2 rounded-md text-xs ${pub.currentMonthAvailabilityHoursCount || pub.currentMonthAvailabilityDaysCount ? 'bg-teal-500 text-white' : 'bg-teal-200 text-gray-300'} hover:underline`}>
|
||
{pub.currentMonthAvailabilityDaysCount || 0} | {pub.currentMonthAvailabilityHoursCount || 0}
|
||
</span>
|
||
</td>
|
||
<td className="border-b p-4">
|
||
<div className="flex items-center justify-between">
|
||
<div className="flex items-center">
|
||
<span title="участия тази седмица" className={`badge py-1 px-2 rounded-full text-xs ${pub.currentWeekAssignments ? 'bg-yellow-500 text-white' : 'bg-yellow-200 text-gray-400'}`}>{pub.currentWeekAssignments || 0}</span>
|
||
<span title="участия този месец" className={`badge py-1 px-2 rounded-full text-xs ${pub.currentMonthAssignments ? 'bg-green-500 text-white' : 'bg-green-200 text-gray-400'}`}>{pub.currentMonthAssignments || 0}</span>
|
||
<span title="участия миналия месец" className={`badge py-1 px-2 rounded-full text-xs ${pub.previousMonthAssignments ? 'bg-blue-500 text-white' : 'bg-blue-200 text-gray-400'}`}>{pub.previousMonthAssignments || 0}</span>
|
||
<button 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>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td className="border-b p-4">{pub.lastLogin ? new Date(pub.lastLogin).toLocaleString("bg") : ""}</td>
|
||
</>
|
||
) : (
|
||
<>
|
||
<td className="border-b p-4">
|
||
|
||
</td>
|
||
<td className="border-b p-4">
|
||
<div className="flex items-center justify-between">
|
||
<div className="flex items-center">
|
||
<span className="badge py-1 px-2 rounded-md text-xs bg-gray-300 text-gray-500" title="участия този месец">
|
||
{allPub.currentMonthAssignments || 0}
|
||
</span>
|
||
<span className="badge py-1 px-2 rounded-md text-xs bg-gray-300 text-gray-500" title="участия миналия месец">
|
||
{allPub.previousMonthAssignments || 0}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td className="border-b p-4"></td> {/* Empty cell for alignment */}
|
||
</>
|
||
)}
|
||
</tr>
|
||
);
|
||
})}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</ProtectedRoute>
|
||
</Layout>
|
||
);
|
||
}
|
||
|
||
|
||
export default ContactsPage;
|
||
|
||
|
||
// Helper functions ToDo: move them to common and replace all implementations with the common ones
|
||
function countAssignments(assignments, startTime, endTime) {
|
||
return assignments.filter(assignment =>
|
||
assignment.shift.startTime >= startTime && assignment.shift.startTime <= endTime
|
||
).length;
|
||
}
|
||
|
||
function convertShiftDates(assignments) {
|
||
assignments.forEach(assignment => {
|
||
if (assignment.shift && assignment.shift.startTime) {
|
||
assignment.shift.startTime = new Date(assignment.shift.startTime).toISOString();
|
||
assignment.shift.endTime = new Date(assignment.shift.endTime).toISOString();
|
||
}
|
||
});
|
||
}
|
||
|
||
export const getServerSideProps = async (context) => {
|
||
|
||
const prisma = common.getPrismaClient();
|
||
const dateStr = new Date().toISOString().split('T')[0];
|
||
|
||
let publishers = await data.filterPublishersNew('id,firstName,lastName,email,isActive,desiredShiftsPerMonth,lastLogin', dateStr, false, true, true, true, true);
|
||
|
||
// const axios = await axiosServer(context);
|
||
// const { data: publishers } = await axios.get(`api/?action=filterPublishers&assignments=true&availabilities=true&date=${dateStr}&select=id,firstName,lastName,isActive,desiredShiftsPerMonth`);
|
||
|
||
// api/index?action=filterPublishers&assignments=true&availabilities=true&date=2024-03-14&select=id%2CfirstName%2ClastName%2CisActive%2CdesiredShiftsPerMonth
|
||
publishers.forEach(publisher => {
|
||
publisher.desiredShiftsPerMonth = publisher.desiredShiftsPerMonth || 0;
|
||
publisher.assignments = publisher.assignments || [];
|
||
publisher.availabilities = publisher.availabilities || [];
|
||
publisher.lastUpdate = publisher.availabilities.reduce((acc, curr) => curr.dateOfEntry > acc ? curr.dateOfEntry : acc, null);
|
||
if (publisher.lastUpdate) {
|
||
publisher.lastUpdate = common.getDateFormated(publisher.lastUpdate);
|
||
}
|
||
else {
|
||
publisher.lastUpdate = "Няма данни";
|
||
}
|
||
//serialize dates in publisher.assignments and publisher.availabilities
|
||
publisher.assignments.forEach(assignment => {
|
||
if (assignment.shift && assignment.shift.startTime) {
|
||
assignment.shift.startTime = assignment.shift.startTime.toISOString();
|
||
assignment.shift.endTime = assignment.shift.endTime.toISOString();
|
||
}
|
||
});
|
||
publisher.availabilities.forEach(availability => {
|
||
if (availability.startTime) {
|
||
availability.startTime = availability.startTime.toISOString();
|
||
availability.endTime = availability.endTime.toISOString();
|
||
if (availability.dateOfEntry) {
|
||
availability.dateOfEntry = availability.dateOfEntry.toISOString();
|
||
}
|
||
}
|
||
});
|
||
publisher.lastLogin = publisher.lastLogin ? publisher.lastLogin.toISOString() : null;
|
||
//remove availabilities that isFromPreviousAssignment
|
||
publisher.availabilities = publisher.availabilities.filter(availability => !availability.isFromPreviousAssignment);
|
||
|
||
|
||
});
|
||
//remove publishers without availabilities
|
||
publishers = publishers.filter(publisher => publisher.availabilities.length > 0);
|
||
|
||
let allPublishers = await prisma.publisher.findMany({
|
||
select: {
|
||
id: true,
|
||
firstName: true,
|
||
lastName: true,
|
||
email: true,
|
||
phone: true,
|
||
isActive: true,
|
||
desiredShiftsPerMonth: true,
|
||
lastLogin: true,
|
||
assignments: {
|
||
select: {
|
||
id: true,
|
||
shift: {
|
||
select: {
|
||
startTime: true,
|
||
endTime: true,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
});
|
||
|
||
|
||
|
||
|
||
let monthInfo,
|
||
currentMonthStart, currentMonthEnd,
|
||
previousMonthStart, previousMonthEnd;
|
||
|
||
monthInfo = common.getMonthDatesInfo(new Date());
|
||
currentMonthStart = monthInfo.firstMonday;
|
||
currentMonthEnd = monthInfo.lastSunday;
|
||
let prevMnt = new Date();
|
||
prevMnt.setMonth(prevMnt.getMonth() - 1);
|
||
monthInfo = common.getMonthDatesInfo(prevMnt);
|
||
previousMonthStart = monthInfo.firstMonday;
|
||
previousMonthEnd = monthInfo.lastSunday;
|
||
|
||
|
||
allPublishers.forEach(publisher => {
|
||
// Use helper functions to calculate and assign assignment counts
|
||
publisher.currentMonthAssignments = countAssignments(publisher.assignments, currentMonthStart, currentMonthEnd);
|
||
publisher.previousMonthAssignments = countAssignments(publisher.assignments, previousMonthStart, previousMonthEnd);
|
||
|
||
publisher.lastLogin = publisher.lastLogin ? publisher.lastLogin.toISOString() : null;
|
||
// Convert date formats within the same iteration
|
||
convertShiftDates(publisher.assignments);
|
||
});
|
||
|
||
// Optionally, if you need a transformed list or additional properties, map the publishers
|
||
allPublishers = allPublishers.map(publisher => ({
|
||
...publisher,
|
||
// Potentially add more computed properties or transformations here if needed
|
||
}));
|
||
allPublishers.sort((a, b) => a.firstName.localeCompare(b.firstName) || a.lastName.localeCompare(b.lastName));
|
||
|
||
|
||
return {
|
||
props: {
|
||
publishers,
|
||
allPublishers,
|
||
},
|
||
};
|
||
};
|