Files
mwitnessing/pages/cart/publishers/stats.tsx
2024-05-12 12:13:56 +03:00

230 lines
14 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 { useState, useEffect, useRef } from 'react';
import Layout from "../../../components/layout";
import ProtectedRoute from '../../../components/protectedRoute';
import { Prisma, UserRole, PublisherType } 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';
import axiosInstance from '../../../src/axiosSecure';
import { setFlagsFromString } from 'v8';
import { set } from 'date-fns';
function ContactsPage({ allPublishers }) {
const currentMonth = new Date().getMonth();
const [selectedMonth, setSelectedMonth] = useState("");
const isMounted = useRef(false);
const [searchQuery, setSearchQuery] = useState('');
const [publisherType, setPublisherType] = useState('');
const [publishers, setPublishers] = useState(allPublishers);
const [pubWithAssignmentsCount, setPubWithAssignmentsCount] = useState(0);
const [filteredPublishers, setFilteredPublishers] = useState(allPublishers);
const [sortField, setSortField] = useState('name');
const [sortOrder, setSortOrder] = useState('asc');
const months = common.getMonthNames();
const subsetMonths = Array.from({ length: 9 }, (_, i) => {
const monthIndex = (currentMonth - 3 + i + 12) % 12; // Adjust for year wrap-around
return {
name: months[monthIndex],
index: monthIndex + 1
};
});
const datesOn15th = Array.from({ length: 7 }, (_, i) => new Date(new Date().getFullYear(), new Date().getMonth() - 3 + i, 15))
.map(date => date.toISOString().split('T')[0]);
function handleSort(field) {
const order = sortField === field && sortOrder === 'asc' ? 'desc' : 'asc';
setSortField(field);
setSortOrder(order);
}
useEffect(() => {
let filtered = 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()))) &&
(publisherType ? publisher.type === publisherType : true)
);
if (sortField) {
filtered.sort((a, b) => {
// Check for undefined or null values and treat them as "larger" when sorting ascending
const aValue = a[sortField] || 0; // Treat undefined, null as 0
const bValue = b[sortField] || 0; // Treat undefined, null as 0
if (aValue === 0 && bValue !== 0) return 1; // aValue is falsy, push it to end if asc
if (bValue === 0 && aValue !== 0) return -1; // bValue is falsy, push it to end if asc
if (aValue < bValue) return sortOrder === 'asc' ? -1 : 1;
if (aValue > bValue) return sortOrder === 'asc' ? 1 : -1;
return 0;
});
}
setFilteredPublishers(filtered);
setPubWithAssignmentsCount(filtered.filter(publisher => publisher.currentMonthAvailabilityHoursCount && publisher.currentMonthAvailabilityHoursCount > 0).length);
}, [searchQuery, publisherType, sortField, sortOrder, allPublishers]);
useEffect(() => {
if (isMounted.current) {
const fetchData = async () => {
const month = parseInt(selectedMonth);
const filterDate = new Date(new Date().getFullYear(), month - 1, 15);
try {
const response = await axiosInstance.get(`/api/?action=getAllPublishersWithStatistics&date=${filterDate.toISOString()}&noEndDate=false`);
setPublishers(response.data);
setFilteredPublishers(response.data);
setPubWithAssignmentsCount(response.data.filter(publisher => publisher.currentMonthAvailabilityHoursCount && publisher.currentMonthAvailabilityHoursCount > 0).length);
} catch (error) {
console.error('Failed to fetch publishers data:', error);
// Optionally, handle errors more gracefully here
}
};
fetchData();
} else {
// Set the ref to true after the initial render
isMounted.current = true;
}
}, [selectedMonth]); // Dependency array includes only selectedMonth
function renderSortArrow(field) {
return sortField === field ? sortOrder === 'asc' ? ' ↑' : ' ↓' : '';
}
return (
<Layout>
<ProtectedRoute allowedRoles={[UserRole.ADMIN, UserRole.POWERUSER]}>
<div className="container mx-auto p-4">
<h1 className="text-xl font-semibold mb-4">Статистика </h1>
<h5 className="text-lg font-semibold mb-4">{pubWithAssignmentsCount} участника с предпочитания за месеца (от {filteredPublishers.length} )</h5>
<div className="mb-4 flex justify-between items-center">
<input name="filterText"
type="text"
placeholder="Търси по име, имейл или телефон..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="border border-gray-300 rounded-md px-2 py-2 text-base md:text-sm flex-grow mr-2"
/>
{/* Month dropdown */}
<select name="filterMonth"
className="border border-gray-300 rounded-md px-2 py-2 text-base md:text-sm"
value={selectedMonth}
onChange={(e) => setSelectedMonth(e.target.value)}
>
<option value="">избери месец</option>
{subsetMonths.map((month) => (
<option key={month.index} value={month.index}>{month.name}</option>
))}
</select>
{/* Publisher type dropdown */}
<select name="filterType"
className="border border-gray-300 rounded-md px-2 py-2 text-base md:text-sm"
value={publisherType}
onChange={(e) => setPublisherType(e.target.value)}
>
<option value="">Всички типове</option>
{Object.keys(PublisherType).map((type) => (
<option key={type} value={PublisherType[type]}>{PublisherType[type]}</option>
))}
</select>
</div>
<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 cursor-pointer" onClick={() => handleSort('name')}>
Име{renderSortArrow('name')}
</th>
<th className="border-b font-medium p-4 pt-0 pb-3 cursor-pointer" onClick={() => handleSort('currentMonthAvailabilityDaysCount')}>
Възможности{renderSortArrow('currentMonthAvailabilityDaysCount')}
</th>
<th className="border-b font-medium p-4 pt-0 pb-3 cursor-pointer" onClick={() => handleSort('currentMonthAssignments')}>
Участия{renderSortArrow('currentMonthAssignments')}
</th>
<th className="border-b font-medium p-4 pt-0 pb-3 cursor-pointer" onClick={() => handleSort('lastLogin')}>
Последно влизане{renderSortArrow('lastLogin')}
</th>
</tr>
</thead>
<tbody>
{filteredPublishers.map((pub, i) => {
return (
<tr key={pub.id}>
<td className="border-b p-4 pl-8" title={pub.lastUpdate}>{i + 1}. {pub.firstName} {pub.lastName}</td>
{/* Display statistics if publisher is found */}
{pub ? (
<>
<td className="border-b p-4">
{pub.availabilities.length > 0 ? (
<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} | {pub.currentMonthAvailabilityHoursCount}
</span>
) : <span title="Няма възможности" className="badge py-1 px-2 rounded-md text-xs bg-gray-300 text-gray-500">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="участия този месец">
{pub.currentMonthAssignments}
</span>
<span className="badge py-1 px-2 rounded-md text-xs bg-gray-300 text-gray-500" title="участия миналия месец">
{pub.previousMonthAssignments}
</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;
export const getServerSideProps = async (context) => {
const allPublishers = await data.getAllPublishersWithStatistics(new Date());
//merge first and last name
allPublishers.forEach(publisher => {
publisher.name = `${publisher.firstName} ${publisher.lastName}`;
});
return {
props: {
allPublishers
},
};
};