-
+
{pub.currentMonthAvailabilityDaysCount || 0} | {pub.currentMonthAvailabilityHoursCount || 0}
{pub.currentWeekAssignments || 0}
@@ -809,7 +833,20 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
headers: {
'Content-Type': 'application/json'
},
- body: JSON.stringify({ broadcast: true, message: "Тестово съобщение", title: "Това е тестово съобщение от https://sofia.mwitnessing.com" })
+ body: JSON.stringify({
+ id: pub.id, message: "Тестово съобщение", title: "Това е тестово съобщение от https://sofia.mwitnessing.com", actions: [
+ {
+ action: 'open_url',
+ title: 'Open URL',
+ icon: '/images/open-url.png'
+ },
+ {
+ action: 'dismiss',
+ title: 'Dismiss',
+ icon: '/images/dismiss.png'
+ }
+ ]
+ })
})
}}
>+
diff --git a/pages/cart/publishers/stats.tsx b/pages/cart/publishers/stats.tsx
index 69e33f6..0e10c77 100644
--- a/pages/cart/publishers/stats.tsx
+++ b/pages/cart/publishers/stats.tsx
@@ -253,7 +253,7 @@ function ContactsPage({ allPublishers }) {
<>
{pub.availabilities.length > 0 ? (
-
+
{pub.currentMonthAvailabilityDaysCount} | {pub.currentMonthAvailabilityHoursCount}
) : 0}
diff --git a/pages/cart/surveys/index.tsx b/pages/cart/surveys/index.tsx
new file mode 100644
index 0000000..f8cf1e8
--- /dev/null
+++ b/pages/cart/surveys/index.tsx
@@ -0,0 +1,86 @@
+import React, { useState, useEffect } from 'react';
+import Layout from "../../../components/layout";
+import { GetServerSideProps } from 'next';
+import { Location, UserRole } from "@prisma/client";
+import axiosServer from '../../../src/axiosServer';
+const common = require('../../../src/helpers/common');
+// import * as common from '../../../src/helpers/common';
+import SurveyForm from '../../../components/survey/SurveyForm';
+import _ from 'lodash';
+import ProtectedRoute from 'components/protectedRoute';
+
+const SurveyPage = ({ serverSurveys }) => {
+ const [selectedSurvey, setSelectedSurvey] = useState(null);
+
+ return (
+
+
+
+ Анкети
+
+
+
+ Списък
+
+
+
+
+ Детайли
+
+
+
+
+
+
+
+
+ );
+};
+
+export default SurveyPage;
+
+export const getServerSideProps: GetServerSideProps = async (context) => {
+ const prisma = common.getPrismaClient();
+ let serverSurveys = await prisma.survey.findMany({
+ where: {
+ },
+ include: {
+ messages: true,
+ },
+ });
+ serverSurveys = common.convertDatesToISOStrings(serverSurveys);
+
+ context.res.setHeader("Cache-Control", "s-maxage=1, stale-while-revalidate");
+ return {
+ props: {
+ serverSurveys: serverSurveys
+ },
+ };
+};
+
diff --git a/pages/dash.tsx b/pages/dash.tsx
index 53c74bd..93b7c75 100644
--- a/pages/dash.tsx
+++ b/pages/dash.tsx
@@ -1,7 +1,8 @@
import { useSession } from "next-auth/react"
import { useRouter } from 'next/router';
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, use } from 'react';
import Layout from "../components/layout"
+import { toast } from 'react-toastify';
import AvCalendar from '../components/calendar/avcalendar';
import { getSession } from "next-auth/react";
@@ -25,9 +26,9 @@ interface IProps {
initialUserId: string;
cartEvents: any;
lastPublishedDate: Date;
+ messages: any;
}
-export default function DashboardPage({ initialItems, initialUserId, cartEvents, lastPublishedDate }: IProps) {
-
+export default function DashboardPage({ initialItems, initialUserId, cartEvents, lastPublishedDate, messages }: IProps) {
const router = useRouter();
const { newLogin } = router.query;
const { data: session } = useSession();
@@ -51,17 +52,148 @@ export default function DashboardPage({ initialItems, initialUserId, cartEvents,
}, [session]);
+
+ // MESSAGES
//const [notificationsVisible, setNotificationsVisible] = useState(false);
- useEffect(() => {
- //if (newLogin === 'true')
- {
- // alert("Мили братя, искаме само да ви напомним да ни изпратите вашите предпочитания за юни до 25-то число като използвате меню 'Възможности'. Ако имате проблем, моля пишете ни на 'specialnosvidetelstvanesofia@gmail.com'");
- const currentPath = router.pathname;
- router.replace(currentPath, undefined, { shallow: true }); // Removes the query without affecting the history
- }
- }, []);// show the message every time we load the page
+ // useEffect(() => {
+ // //if (newLogin === 'true')
+ // {
+ // // alert("Мили братя, искаме само да ви напомним да ни изпратите вашите предпочитания за юни до 25-то число като използвате меню 'Възможности'. Ако имате проблем, моля пишете ни на 'specialnosvidetelstvanesofia@gmail.com'");
+ // const currentPath = router.pathname;
+ // router.replace(currentPath, undefined, { shallow: true }); // Removes the query without affecting the history
+ // }
+ // }, []);// show the message every time we load the page
+
+ // const [processedMessages, setProcessedMessages] = useState(new Set());
+ // useEffect(() => {
+ // if (messages && messages.length > 0) {
+ // const unprocessedMessages = messages.filter(message => !processedMessages.has(message.id));
+ // if (unprocessedMessages.length > 0) {
+ // showMessageToasts(unprocessedMessages);
+ // setProcessedMessages(new Set([...processedMessages, ...unprocessedMessages.map(msg => msg.id)]));
+ // }
+ // }
+ // }, [messages, processedMessages]);
+
+ useEffect(() => {
+ if (messages && messages.length > 0) {
+ showMessageToasts(messages);
+ }
+ }, [messages]);
+
+ const showMessageToasts = (messages) => {
+ const handleOptionClick = async (messageId, option, toastId) => {
+ try {
+ await axiosInstance.put(`/api/data/messages/${messageId}`, { answer: option });
+ handleClose(toastId);
+ } catch (error) {
+ console.error("Error updating message:", error);
+ toast.error("Error updating message. Please try again.");
+ }
+ };
+
+ const handleClose = (toastId) => {
+ toast.dismiss(toastId);
+ };
+
+ messages.forEach((message, messageIndex) => {
+ const toastId = `message-${message.id}-${messageIndex}`;
+ const content = (
+
+ {message.content.message}
+
+ {message.content.options?.map((option, index) => (
+
+ ))}
+
+
+ );
+
+ toast(content, {
+ toastId,
+ autoClose: false,
+ closeButton: true,
+ onClose: () => handleClose(toastId),
+ });
+ });
+ };
+
+
+
+
+
+ // const showMessageToastNewModal = (messages, handleMessageOptionAnswer) => {
+ // let currentMessageIndex = 0;
+
+ // const showModal = () => {
+ // if (currentMessageIndex >= messages.length) {
+ // return; // All messages have been shown
+ // }
+
+ // const message = messages[currentMessageIndex];
+ // const content = (
+ //
+ //
+ //
+ //
+ //
+ // {message.content.message}
+ //
+ // {message.content.options?.map((option, index) => (
+ //
+ // ))}
+ //
+ //
+ //
+ // );
+
+ // toast(content, {
+ // autoClose: false, // Keep the toast open until manually closed
+ // closeButton: false,
+ // onClose: handleClose,
+ // //className: 'custom-toast', // Optional custom class for additional styling
+ // });
+ // };
+
+ // const handleOptionClick = async (messageId, option) => {
+ // try {
+ // await axiosInstance.put(`/api/data/messages/${messageId}`, { answer: option });
+ // toast.dismiss();
+ // currentMessageIndex++;
+ // showModal();
+ // } catch (error) {
+ // console.error("Error updating message:", error);
+ // toast.error("Error updating message. Please try again.");
+ // }
+ // };
+
+ // const handleClose = () => {
+ // toast.dismiss();
+ // };
+
+ // showModal();
+ // };
+
+ // FOR ADMINS ONLY
const handleUserSelection = async (publisher) => {
if (!publisher || publisher.id === undefined) return;
console.log("selecting publisher", publisher.id);
@@ -107,107 +239,6 @@ export default function DashboardPage({ initialItems, initialUserId, cartEvents,
}
-// async function getAvailabilities(userId) {
-// const prismaClient = common.getPrismaClient();
-// const items = await prismaClient.availability.findMany({
-// where: {
-// publisherId: userId,
-// },
-// select: {
-// id: true,
-// name: true,
-// isActive: true,
-// isFromPreviousAssignment: true,
-// isFromPreviousMonth: true,
-// dayofweek: true,
-// dayOfMonth: true,
-// startTime: true,
-// endTime: true,
-// repeatWeekly: true,
-// endDate: true,
-// publisher: {
-// select: {
-// firstName: true,
-// lastName: true,
-// id: true,
-// },
-// },
-// },
-// });
-// // Convert Date objects to ISO strings
-// const serializableItems = items.map(item => ({
-// ...item,
-// startTime: item.startTime.toISOString(),
-// endTime: item.endTime.toISOString(),
-// name: common.getTimeFormatted(item.startTime) + "-" + common.getTimeFormatted(item.endTime),
-// //endDate can be null
-// endDate: item.endDate ? item.endDate.toISOString() : null,
-// type: 'availability',
-// // Convert other Date fields similarly if they exist
-// }));
-
-// /*model Assignment {
-// id Int @id @default(autoincrement())
-// shift Shift @relation(fields: [shiftId], references: [id], onDelete: Cascade)
-// shiftId Int
-// publisher Publisher @relation(fields: [publisherId], references: [id], onDelete: Cascade)
-// publisherId String
-// isActive Boolean @default(true)
-// isConfirmed Boolean @default(false)
-// isWithTransport Boolean @default(false)
-// Report Report[]
-// }*/
-// //get assignments for this user
-// const assignments = await prismaClient.assignment.findMany({
-// where: {
-// publisherId: userId,
-// },
-// select: {
-// id: true,
-// isBySystem: true,
-// isConfirmed: true,
-// isWithTransport: true,
-// shift: {
-// select: {
-// id: true,
-// name: true,
-// startTime: true,
-// endTime: true,
-// //select all assigned publishers names as name - comma separated
-// assignments: {
-// select: {
-// publisher: {
-// select: {
-// firstName: true,
-// lastName: true,
-// }
-// }
-// }
-// }
-// }
-// }
-// }
-// });
-
-// const serializableAssignments = assignments.map(item => ({
-// ...item,
-// startTime: item.shift.startTime.toISOString(),
-// endTime: item.shift.endTime.toISOString(),
-// // name: item.shift.publishers.map(p => p.firstName + " " + p.lastName).join(", "),
-// //name: item.shift.assignments.map(a => a.publisher.firstName[0] + " " + a.publisher.lastName).join(", "),
-// name: common.getTimeFormatted(new Date(item.shift.startTime)) + "-" + common.getTimeFormatted(new Date(item.shift.endTime)),
-// type: 'assignment',
-// //delete shift object
-// shift: null,
-// publisher: { id: userId }
-// }));
-
-// serializableItems.push(...serializableAssignments);
-
-// return serializableItems;
-
-// }
-
export const getServerSideProps = async (context) => {
const auth = await serverSideAuth({
req: context.req,
@@ -270,7 +301,7 @@ export const getServerSideProps = async (context) => {
}
});
cartEvents = common.convertDatesToISOStrings(cartEvents);
- const lastPublishedDate = (await prisma.shift.findFirst({
+ let lastPublishedDate = (await prisma.shift.findFirst({
where: {
isPublished: true,
},
@@ -280,7 +311,43 @@ export const getServerSideProps = async (context) => {
orderBy: {
endTime: 'desc'
}
- })).endTime;
+ }))?.endTime || new Date();
+
+ let blockedDate = await prisma.settings.findUnique({
+ where: {
+ key: "AvailabilityBlockDate"
+ }
+ });
+
+ if (blockedDate) {
+ blockedDate.value = new Date(blockedDate.value);
+ lastPublishedDate = lastPublishedDate > blockedDate.value ? lastPublishedDate : blockedDate.value;
+ }
+
+ let messages = await prisma.message.findMany({
+ where: {
+ publisherId: userId,
+ isPublic: false,
+ answer: null,
+ },
+ include: {
+ Survey: true,
+ }
+ });
+
+ messages = messages.filter((message) => {
+ return (!message.Survey.publicFrom || message.Survey.publicFrom >= common.getStartOfDay(new Date()))
+ && (!message.Survey.publicUntil || message.Survey.publicUntil <= common.getEndOfDay(new Date()))
+ });
+ messages = common.convertDatesToISOStrings(messages);
+ messages = messages.map(message => {
+ if (message.content) {
+ message.content = JSON.parse(message.content);
+ message.content.options = message.content.options?.split(",");
+
+ }
+ return message;
+ });
return {
props: {
@@ -288,7 +355,7 @@ export const getServerSideProps = async (context) => {
userId: sessionServer?.user.id,
cartEvents: cartEvents,
lastPublishedDate: lastPublishedDate.toISOString(),
- // messages: (await import(`../content/i18n/${context.locale}.json`)).default
+ messages: messages
},
};
}
diff --git a/prisma/migrations/20240614185207_/migration.sql b/prisma/migrations/20240614185207_/migration.sql
new file mode 100644
index 0000000..fb8827e
--- /dev/null
+++ b/prisma/migrations/20240614185207_/migration.sql
@@ -0,0 +1,9 @@
+-- CreateTable
+CREATE TABLE `Settings` (
+ `id` INTEGER NOT NULL AUTO_INCREMENT,
+ `key` VARCHAR(191) NOT NULL,
+ `value` VARCHAR(191) NOT NULL,
+ `description` VARCHAR(191) NULL,
+
+ PRIMARY KEY (`id`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
diff --git a/prisma/migrations/20240614213436_modify_settings_table/migration.sql b/prisma/migrations/20240614213436_modify_settings_table/migration.sql
new file mode 100644
index 0000000..8c71f7c
--- /dev/null
+++ b/prisma/migrations/20240614213436_modify_settings_table/migration.sql
@@ -0,0 +1,10 @@
+/*
+Warnings:
+- The primary key for the `settings` table will be changed. If it partially fails, the table could be left without primary key constraint.
+- You are about to drop the column `id` on the `settings` table. All the data in the column will be lost.
+*/
+-- AlterTable
+ALTER TABLE `Settings`
+DROP PRIMARY KEY,
+DROP COLUMN `id`,
+ADD PRIMARY KEY (`key`);
\ No newline at end of file
diff --git a/prisma/migrations/20240616203120_add_survey_table/migration.sql b/prisma/migrations/20240616203120_add_survey_table/migration.sql
new file mode 100644
index 0000000..e2bc703
--- /dev/null
+++ b/prisma/migrations/20240616203120_add_survey_table/migration.sql
@@ -0,0 +1,22 @@
+-- AlterTable
+ALTER TABLE `Message`
+ADD COLUMN `answer` VARCHAR(191) NULL,
+ADD COLUMN `answerDate` DATETIME(3) NULL,
+ADD COLUMN `shownDate` DATETIME(3) NULL,
+ADD COLUMN `surveyId` INTEGER NULL;
+
+-- CreateTable
+CREATE TABLE `Survey` (
+ `id` INTEGER NOT NULL AUTO_INCREMENT,
+ `content` VARCHAR(191) NOT NULL,
+ `answers` JSON NULL,
+ `publicFrom` DATETIME(3) NULL,
+ `publicUntil` DATETIME(3) NULL,
+
+
+ PRIMARY KEY (`id`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- AddForeignKey
+ALTER TABLE `Message`
+ADD CONSTRAINT `Message_surveyId_fkey` FOREIGN KEY (`surveyId`) REFERENCES `Survey` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
\ No newline at end of file
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index c5cc4d8..35996b6 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -264,6 +264,15 @@ enum MessageType {
InApp
}
+model Survey {
+ id Int @id @default(autoincrement())
+ content String
+ answers Json?
+ messages Message[]
+ publicFrom DateTime?
+ publicUntil DateTime?
+}
+
model Message {
id Int @id @default(autoincrement())
publisher Publisher @relation(fields: [publisherId], references: [id])
@@ -274,6 +283,12 @@ model Message {
isPublic Boolean @default(false)
type MessageType @default(Email)
publicUntil DateTime?
+ shownDate DateTime?
+ answer String?
+ answerDate DateTime?
+
+ Survey Survey? @relation(fields: [surveyId], references: [id], onDelete: Cascade)
+ surveyId Int?
}
enum EventLogType {
@@ -348,3 +363,9 @@ model VerificationToken {
@@unique([identifier, token])
}
+
+model Settings {
+ key String @id
+ value String
+ description String?
+}
diff --git a/tsconfig.json b/tsconfig.json
index e903954..ed853ff 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -32,7 +32,8 @@
"components/location/LocationForm.js",
"pages/cart/locations/[id].tsx.old",
"components/publisher/ShiftsList.js",
- "src/helpers/data.js"
+ "src/helpers/data.js",
+ "components/survey/SurveyForm.js"
],
"exclude": [
"node_modules"
|