Merge branch 'main' into feature-fixStats
This commit is contained in:
@ -15,6 +15,9 @@ import { toast } from 'react-toastify';
|
||||
import ProtectedRoute from '../../../components/protectedRoute';
|
||||
import ConfirmationModal from '../../../components/ConfirmationModal';
|
||||
import LocalShippingIcon from '@mui/icons-material/LocalShipping';
|
||||
// import notify api
|
||||
import { sendPush, broadcastPush } from '../../api/notify';
|
||||
const { DateTime } = require('luxon');
|
||||
|
||||
// import { FaPlus, FaCogs, FaTrashAlt, FaSpinner } from 'react-icons/fa'; // Import FontAwesome icons
|
||||
|
||||
@ -544,7 +547,7 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
||||
var dayName = common.DaysOfWeekArray[value.getDayEuropean()];
|
||||
const cartEvent = events.find(event => event.dayofweek == dayName);
|
||||
lastShift = {
|
||||
endTime: new Date(value.setHours(9, 0, 0, 0)),
|
||||
endTime: DateTime.fromJSDate(value).setZone('Europe/Sofia', { keepLocalTime: true }).set({ hour: 9 }).toJSDate(),
|
||||
cartEventId: cartEvent.id
|
||||
};
|
||||
}
|
||||
@ -733,7 +736,19 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
||||
<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 tooltip="участия миналия месец" 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 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`}
|
||||
onClick={async () => {
|
||||
await fetch('/api/notify', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ broadcast: true, message: "Тестово съобщение", title: "Това е тестово съобщение от https://sofia.mwitnessing.com" })
|
||||
})
|
||||
}}
|
||||
>+</button>
|
||||
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
@ -4,6 +4,7 @@ import Layout from "../../../components/layout";
|
||||
import LocationCard from "../../../components/location/LocationCard";
|
||||
import axiosServer from '../../../src/axiosServer';
|
||||
import ProtectedRoute from '../../../components/protectedRoute';
|
||||
import CongregationCRUD from "../publishers/congregationCRUD";
|
||||
interface IProps {
|
||||
item: Location;
|
||||
}
|
||||
@ -32,6 +33,7 @@ function LocationsPage({ items = [] }: IProps) {
|
||||
</a>
|
||||
</div>
|
||||
</ProtectedRoute>
|
||||
<CongregationCRUD />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
103
pages/cart/publishers/congregationCRUD.tsx
Normal file
103
pages/cart/publishers/congregationCRUD.tsx
Normal file
@ -0,0 +1,103 @@
|
||||
// a simple CRUD componenet for congregations for admins
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import axiosInstance from '../../../src/axiosSecure';
|
||||
import toast from 'react-hot-toast';
|
||||
import Layout from '../../../components/layout';
|
||||
import ProtectedRoute from '../../../components/protectedRoute';
|
||||
import { UserRole } from '@prisma/client';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function CongregationCRUD() {
|
||||
const [congregations, setCongregations] = useState([]);
|
||||
const [newCongregation, setNewCongregation] = useState('');
|
||||
const router = useRouter();
|
||||
|
||||
const fetchCongregations = async () => {
|
||||
try {
|
||||
const { data: congregationsData } = await axiosInstance.get(`/api/data/congregations`);
|
||||
setCongregations(congregationsData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const addCongregation = async () => {
|
||||
try {
|
||||
await axiosInstance.post(`/api/data/congregations`, { name: newCongregation, address: "" });
|
||||
toast.success('Успешно добавен сбор');
|
||||
setNewCongregation('');
|
||||
fetchCongregations();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteCongregation = async (id) => {
|
||||
try {
|
||||
await axiosInstance.delete(`/api/data/congregations/${id}`);
|
||||
toast.success('Успешно изтрит сбор');
|
||||
fetchCongregations();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
fetchCongregations();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ProtectedRoute allowedRoles={[UserRole.ADMIN]}>
|
||||
<div className="h-5/6 grid place-items-start px-4 pt-8">
|
||||
<div className="flex flex-col w-full px-4">
|
||||
<h1 className="text-2xl font-bold text-center">Сборове</h1>
|
||||
<div className="flex gap-2 mb-4">
|
||||
<input
|
||||
type="text"
|
||||
value={newCongregation}
|
||||
onChange={(e) => setNewCongregation(e.target.value)}
|
||||
placeholder="Име на сбор"
|
||||
className="px-4 py-2 rounded-md border border-gray-300"
|
||||
/>
|
||||
<button
|
||||
onClick={addCongregation}
|
||||
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
|
||||
>
|
||||
Добави
|
||||
</button>
|
||||
</div>
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Име</th>
|
||||
<th>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{congregations.map((congregation) => (
|
||||
<tr key={congregation.id}>
|
||||
<td>{congregation.name}</td>
|
||||
<td className='right'>
|
||||
{/* <button
|
||||
onClick={() => router.push(`/cart/publishers/congregation/${congregation.id}`)}
|
||||
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
|
||||
>
|
||||
Преглед
|
||||
</button> */}
|
||||
<button
|
||||
onClick={() => deleteCongregation(congregation.id)}
|
||||
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
||||
>
|
||||
Изтрий
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</ProtectedRoute>
|
||||
);
|
||||
}
|
||||
|
@ -64,13 +64,23 @@ export const getServerSideProps = async (context) => {
|
||||
}
|
||||
});
|
||||
if (!item) {
|
||||
const user = context.req.session.user;
|
||||
const user = context.req.session?.user;
|
||||
if (!user) {
|
||||
return {
|
||||
// redirect to '/auth/signin'. assure it is not relative path
|
||||
redirect: {
|
||||
destination: process.env.NEXT_PUBLIC_PUBLIC_URL + "/auth/signin",
|
||||
permanent: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
const message = encodeURIComponent(`Този имейл (${user?.email}) не е регистриран. Моля свържете се с администратора.`);
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/message?message=Този имейл (' + user.email + ') не е регистриран. Моля свържете се с администратора.',
|
||||
destination: `/message?message=${message}`,
|
||||
permanent: false,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
// item.allShifts = item.assignments.map((a: Assignment[]) => a.shift);
|
||||
|
||||
|
@ -11,7 +11,6 @@ import * as XLSX from "xlsx";
|
||||
// import { Table } from "react-bootstrap";
|
||||
import { staticGenerationAsyncStorage } from "next/dist/client/components/static-generation-async-storage.external";
|
||||
|
||||
import moment from 'moment';
|
||||
// import { DatePicker } from '@mui/x-date-pickers'; !! CAUSERS ERROR ???
|
||||
|
||||
// import { DatePicker } from '@mui/x-date-pickers/DatePicker';
|
||||
|
@ -60,7 +60,7 @@ export default function MySchedulePage({ assignments }) {
|
||||
<div className="container ">
|
||||
<h1 className="text-xl sm:text-2xl md:text-3xl font-bold text-center my-4">Моите смени</h1>
|
||||
<div className="space-y-4">
|
||||
{assignments && assignments.map((assignment) => (
|
||||
{assignments && assignments.length > 0 ? (assignments.map((assignment) => (
|
||||
<div key={assignment.dateStr + assignments.indexOf(assignment)} className="bg-white shadow overflow-hidden rounded-lg">
|
||||
<div className="px-4 py-5 sm:px-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">{assignment.dateStr}</h3>
|
||||
@ -117,7 +117,13 @@ export default function MySchedulePage({ assignments }) {
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
))) :
|
||||
<div className="bg-white shadow overflow-hidden rounded-lg">
|
||||
<div className="px-4 py-5 sm:px-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">За сега нямате бъдещи назначени смени. Можете да проверите дали вашите възножности са актуални.</h3>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<Modal isOpen={isModalOpen}
|
||||
@ -168,10 +174,12 @@ export const getServerSideProps = async (context) => {
|
||||
}
|
||||
|
||||
const prisma = common.getPrismaClient();
|
||||
const monthInfo = common.getMonthInfo(new Date());
|
||||
//minus 1 day from the firstMonday to get the last Sunday
|
||||
const lastSunday = new Date(monthInfo.firstMonday);
|
||||
lastSunday.setDate(lastSunday.getDate() - 1);
|
||||
let today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
// const monthInfo = common.getMonthInfo(today);
|
||||
// //minus 1 day from the firstMonday to get the last Sunday
|
||||
// const lastSunday = new Date(monthInfo.firstMonday);
|
||||
// lastSunday.setDate(lastSunday.getDate() - 1);
|
||||
const publisher = await prisma.publisher.findUnique({
|
||||
where: {
|
||||
id: session.user.id,
|
||||
@ -179,7 +187,7 @@ export const getServerSideProps = async (context) => {
|
||||
some: {
|
||||
shift: {
|
||||
startTime: {
|
||||
gte: lastSunday,
|
||||
gte: today,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -208,7 +216,7 @@ export const getServerSideProps = async (context) => {
|
||||
},
|
||||
});
|
||||
|
||||
const assignments = publisher?.assignments.filter(a => a.shift.startTime >= lastSunday && a.shift.isPublished) || [];
|
||||
const assignments = publisher?.assignments.filter(a => a.shift.startTime >= today && a.shift.isPublished) || [];
|
||||
|
||||
|
||||
const transformedAssignments = assignments?.sort((a, b) => a.shift.startTime - b.shift.startTime)
|
||||
|
@ -99,7 +99,7 @@ function ContactsPage({ allPublishers }) {
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<ProtectedRoute allowedRoles={[UserRole.ADMIN, UserRole.POWERUSER, UserRole.USER]}>
|
||||
<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>
|
||||
|
@ -42,7 +42,7 @@ export default function EventLogList() {
|
||||
}, []);
|
||||
return (
|
||||
<Layout>
|
||||
<ProtectedRoute allowedRoles={[UserRole.POWERUSER, UserRole.ADMIN, UserRole.USER, UserRole.EXTERNAL]}>
|
||||
<ProtectedRoute allowedRoles={[UserRole.POWERUSER, UserRole.ADMIN]}>
|
||||
|
||||
<div className="h-5/6 grid place-items-start px-4 pt-8">
|
||||
<div className="flex flex-col w-full px-4">
|
||||
|
@ -9,7 +9,7 @@ import ProtectedRoute from '../../../components/protectedRoute';
|
||||
function NewPage(loc: Location) {
|
||||
return (
|
||||
<Layout>
|
||||
<ProtectedRoute allowedRoles={[UserRole.POWERUSER, UserRole.ADMIN, UserRole.USER, UserRole.EXTERNAL]}>
|
||||
<ProtectedRoute allowedRoles={[UserRole.POWERUSER, UserRole.ADMIN]}>
|
||||
<div className="h-5/6 grid place-items-center">
|
||||
<ExperienceForm />
|
||||
</div></ProtectedRoute>
|
||||
|
@ -83,7 +83,7 @@ export default function Reports() {
|
||||
}, []);
|
||||
return (
|
||||
<Layout>
|
||||
<ProtectedRoute allowedRoles={[UserRole.POWERUSER, UserRole.ADMIN, UserRole.USER, UserRole.EXTERNAL]}>
|
||||
<ProtectedRoute allowedRoles={[UserRole.POWERUSER, UserRole.ADMIN]}>
|
||||
|
||||
<div className="h-5/6 grid place-items-start px-4 pt-8">
|
||||
<div className="flex flex-col w-full px-4">
|
||||
|
@ -9,7 +9,7 @@ import ProtectedRoute from '../../../components/protectedRoute';
|
||||
function NewPage(loc: Location) {
|
||||
return (
|
||||
<Layout>
|
||||
<ProtectedRoute allowedRoles={[UserRole.POWERUSER, UserRole.ADMIN, UserRole.USER, UserRole.EXTERNAL]}>
|
||||
<ProtectedRoute allowedRoles={[UserRole.POWERUSER, UserRole.ADMIN]}>
|
||||
<div className="h-5/6 grid place-items-center">
|
||||
<ReportForm />
|
||||
</div></ProtectedRoute>
|
||||
|
Reference in New Issue
Block a user