294 lines
14 KiB
TypeScript
294 lines
14 KiB
TypeScript
import axiosInstance from '../../src/axiosSecure';
|
||
import { use, useEffect, useState } from "react";
|
||
import { toast } from "react-hot-toast";
|
||
import { useRouter } from "next/router";
|
||
import Link from "next/link";
|
||
import { useSession } from "next-auth/react"
|
||
import { MessageType, Message, Survey } from "@prisma/client";
|
||
// import { content } from 'googleapis/build/src/apis/content';
|
||
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
|
||
// import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3';
|
||
// import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
|
||
import dayjs from 'dayjs';
|
||
import { set } from 'lodash';
|
||
|
||
const common = require('src/helpers/common');
|
||
|
||
|
||
// ------------------ ------------------
|
||
// This component is used to create and edit
|
||
/* location model:
|
||
|
||
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])
|
||
publisherId String
|
||
date DateTime
|
||
content String
|
||
isRead Boolean @default(false)
|
||
isPublic Boolean @default(false)
|
||
type MessageType @default(Email)
|
||
publicUntil DateTime?
|
||
shownDate DateTime?
|
||
answer String?
|
||
answerDate DateTime?
|
||
|
||
Survey Survey? @relation(fields: [surveyId], references: [id])
|
||
surveyId Int?
|
||
}
|
||
*/
|
||
|
||
interface SurveyFormProps {
|
||
existingItem: Survey | null;
|
||
}
|
||
|
||
const SurveyForm: React.FC<SurveyFormProps> = ({ existingItem }) => {
|
||
|
||
const router = useRouter();
|
||
const [editMode, setEditMode] = useState(existingItem ? true : false);
|
||
const [pubs, setPubs] = useState([]);
|
||
|
||
|
||
const [item, setItem] = useState(existingItem || {
|
||
...existingItem,
|
||
content: existingItem?.content || "Нова анкета",
|
||
answers: existingItem?.answers.split(",") || [],
|
||
publicFrom: existingItem?.publicFrom ? dayjs(existingItem.publicFrom).toISOString() : new Date().toISOString(),
|
||
publicUntil: existingItem?.publicUntil ? dayjs(existingItem.publicUntil).toISOString() : new Date().toISOString(),
|
||
});
|
||
|
||
|
||
useEffect(() => {
|
||
let transformedItem = { ...existingItem };
|
||
transformedItem.answersCount = existingItem?.answers.split(",") || [];
|
||
setEditMode(existingItem ? true : false);
|
||
setItem(transformedItem);
|
||
}, [existingItem]);
|
||
|
||
useEffect(async () => {
|
||
const pubs = await axiosInstance.get("/api/data/publishers?select=id,firstName,lastName,email");
|
||
setPubs(pubs.data);
|
||
}, []);
|
||
|
||
|
||
|
||
const handleChange = ({ target }) => {
|
||
setItem({ ...item, [target.name]: target.value });
|
||
};
|
||
|
||
const handleDateChange = (fieldName, newDate) => {
|
||
setItem((prevItem) => ({
|
||
...prevItem,
|
||
[fieldName]: newDate
|
||
}));
|
||
};
|
||
|
||
const handleSubmit = async (e) => {
|
||
e.preventDefault();
|
||
delete item.answersCount;
|
||
try {
|
||
|
||
if (editMode) {
|
||
delete item.messages;
|
||
const { data } = await axiosInstance.put(`/api/data/surveys/${existingItem.id}`, item);
|
||
toast.success("Анкетата е обновена успешно");
|
||
}
|
||
else {
|
||
//get all publisherIds and create a message for each
|
||
const messages = pubs.data.map(pub => {
|
||
return {
|
||
publisherId: pub.id,
|
||
content: JSON.stringify({ message: item.content, options: item.answers }),
|
||
date: new Date(),
|
||
isPublic: false,
|
||
type: MessageType.InApp,
|
||
publicUntil: item.publicUntil,
|
||
}
|
||
});
|
||
item.messages = { create: messages };
|
||
const { data } = await axiosInstance.post("/api/data/surveys", item);
|
||
toast.success("Анкетата е създадена успешно");
|
||
}
|
||
router.push("/cart/surveys");
|
||
} catch (error) {
|
||
toast.error("Възникна грешка при създаването на анкетата");
|
||
console.error("Error creating survey:", error);
|
||
}
|
||
}
|
||
|
||
const handleDelete = async (e) => {
|
||
e.preventDefault();
|
||
if (!editMode) return;
|
||
try {
|
||
await axiosInstance.delete(`/api/data/surveys/${existingItem.id}`);
|
||
|
||
toast.success("Записът изтрит", {
|
||
position: "bottom-center",
|
||
});
|
||
router.push("/cart/surveys");
|
||
|
||
} catch (error) {
|
||
//alert("Нещо се обърка при изтриването. Моля, опитайте отново или се свържете с нас");
|
||
console.log(JSON.stringify(error));
|
||
toast.error(error.response?.data?.message || "Нещо се обърка при изтриването. Моля, опитай отново и се свържете с нас ако проблема продължи.");
|
||
}
|
||
};
|
||
|
||
function handleDeleteAnswers(e: MouseEvent<HTMLButtonElement, MouseEvent>): void {
|
||
e.preventDefault();
|
||
if (!editMode) return;
|
||
|
||
Promise.all(existingItem.messages.map(message =>
|
||
axiosInstance.put(`/api/data/messages/${message.id}`, { answer: null })
|
||
))
|
||
.then(() => {
|
||
toast.success("Отговорите изтрити", {
|
||
position: "bottom-center",
|
||
});
|
||
})
|
||
.catch((error) => {
|
||
console.log(JSON.stringify(error));
|
||
toast.error(error.response?.data?.message || "Нещо се обърка при изтриването. Моля, опитай отново и се свържете с нас ако проблема продължи.");
|
||
});
|
||
}
|
||
|
||
|
||
const getNamesByIds = (ids) => {
|
||
return ids
|
||
.map((id) => {
|
||
const pub = pubs.find((p) => p.id === id);
|
||
return pub ? `${pub.firstName} ${pub.lastName}` : null;
|
||
})
|
||
.filter((name) => name !== null)
|
||
.join(", ");
|
||
};
|
||
|
||
const getIdsForAnswer = (answer) => {
|
||
return item.messages
|
||
.filter((message) => message.answer === answer)
|
||
.map((message) => message.publisherId);
|
||
};
|
||
|
||
const getIdsForAnswered = () => {
|
||
return item.messages
|
||
.filter((message) => message.answer)
|
||
.map((message) => message.publisherId);
|
||
};
|
||
|
||
const getIdsForUnanswered = () => {
|
||
return item.messages
|
||
.filter((message) => !message.answer)
|
||
.map((message) => message.publisherId);
|
||
};
|
||
return (
|
||
<div className="w-full max-w-md mx-auto" >
|
||
< form className="bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4" onSubmit={handleSubmit} >
|
||
|
||
<h1 className="text-2xl font-bold mb-8">Анкета {existingItem?.id}</h1>
|
||
<div className="mb-4">
|
||
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="date">
|
||
Видима от
|
||
</label>
|
||
<DatePicker className="textbox form-input px-4 py-2 rounded" name="publicFrom" onChange={(newDate) => handleDateChange('publicFrom', newDate)} value={dayjs(item?.publicFrom)} />
|
||
</div>
|
||
<div className="mb-4">
|
||
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="date">
|
||
Видима до
|
||
</label>
|
||
<DatePicker className="textbox form-input px-4 py-2 rounded" name="publicUntil" onChange={(newDate) => handleDateChange('publicUntil', newDate)} value={dayjs(item?.publicUntil)} />
|
||
</div>
|
||
<div className="mb-4">
|
||
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="content">
|
||
Съдържание
|
||
</label>
|
||
<textarea className={`textbox form-input px-4 py-2 rounded ${editMode ? 'opacity-50 cursor-not-allowed' : ''}`} id="content" name="content" onChange={handleChange} value={item?.content} autoComplete="off" disabled={editMode} />
|
||
</div>
|
||
<div className="mb-4">
|
||
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="answers">
|
||
Отговори
|
||
</label>
|
||
<input className={`textbox form-input px-4 py-2 rounded ${editMode ? 'opacity-50 cursor-not-allowed' : ''}`} id="answers" name="answers" type="text" onChange={handleChange} value={item?.answers} autoComplete="off" disabled={editMode}
|
||
/>
|
||
</div>
|
||
{/* show count of each answer and the total answered/unanswered messages */}
|
||
{item?.answersCount?.length > 0 && (
|
||
<div>
|
||
{item?.answersCount?.length > 0 && (
|
||
<div className="mb-4">
|
||
<h3 className="text-lg font-semibold mb-2">Отговори:</h3>
|
||
{item.answersCount.map((answer, index) => {
|
||
const currentCount = item.messages ? item.messages.filter((message) => message.answer === answer).length : 0;
|
||
const totalCount = item.messages ? item.messages.length : 0;
|
||
const percentage = totalCount > 0 ? (currentCount / totalCount) * 100 : 0;
|
||
const ids = getIdsForAnswer(answer);
|
||
const names = getNamesByIds(ids);
|
||
|
||
return (
|
||
<div key={index} className="mb-4">
|
||
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor={`answer-${index}`}>
|
||
{answer}
|
||
</label>
|
||
<div className="relative h-6 w-full bg-gray-200 rounded" title={names}>
|
||
<div className="absolute h-full bg-blue-600 rounded" style={{ width: `${percentage}%` }}></div>
|
||
<div className="absolute inset-0 flex items-center justify-center text-white font-bold">
|
||
{currentCount} ({percentage.toFixed(1)}%)
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
<div className="mb-4">
|
||
<label className="block text-gray-700 text-sm font-bold mb-2">Общо отговорили</label>
|
||
<div className="relative h-6 w-full bg-gray-200 rounded" title={getNamesByIds(getIdsForAnswered())}>
|
||
<div className="absolute h-full bg-green-600 rounded" style={{ width: `${item.messages ? (item.messages.filter((message) => message.answer).length / item.messages.length) * 100 : 0}%` }}></div>
|
||
<div className="absolute inset-0 flex items-center justify-center text-white font-bold">
|
||
{item.messages ? item.messages.filter((message) => message.answer).length : 0}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="mb-4">
|
||
<label className="block text-gray-700 text-sm font-bold mb-2">Общо неотговорили</label>
|
||
<div className="relative h-6 w-full bg-gray-200 rounded" title={getNamesByIds(getIdsForUnanswered())}>
|
||
<div className="absolute h-full bg-red-600 rounded" style={{ width: `${item.messages ? (item.messages.filter((message) => !message.answer).length / item.messages.length) * 100 : 0}%` }}></div>
|
||
<div className="absolute inset-0 flex items-center justify-center text-white font-bold">
|
||
{item.messages ? item.messages.filter((message) => !message.answer).length : 0}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
|
||
<div className="flex items-center justify-between">
|
||
{editMode && (<>
|
||
<button className="button btn-outline bg-red-500 hover:bg-red-700 focus:outline-none focus:shadow-outline" type="button" onClick={handleDelete}>
|
||
Изтрий
|
||
</button>
|
||
|
||
<button className="button btn-outline bg-red-500 hover:bg-red-700 focus:outline-none focus:shadow-outline" type="button" onClick={handleDeleteAnswers}>
|
||
Изтрий отговорите
|
||
</button>
|
||
|
||
</>)}
|
||
<button className="btn bg-blue-500 hover:bg-blue-600 text-white font-semibold py-2 px-4 rounded transition duration-300" type="submit">
|
||
Запази
|
||
</button>
|
||
|
||
</div>
|
||
</form >
|
||
</div >
|
||
);
|
||
}
|
||
|
||
export default SurveyForm; |