Files
mwitnessing/components/survey/SurveyForm.tsx
Dobromir Popov f9a41c1630 more derails on survey-details;
publisher list
2024-06-21 02:13:59 +03:00

294 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 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;