322 lines
12 KiB
JavaScript
322 lines
12 KiB
JavaScript
import axiosInstance from '../../src/axiosSecure';
|
|
import { useEffect, useState, useRef } from "react";
|
|
import { useRouter } from "next/router";
|
|
import toast from "react-hot-toast";
|
|
import Link from "next/link";
|
|
import DayOfWeek from "../DayOfWeek";
|
|
import TextEditor from "../TextEditor";
|
|
import FileUploadWithPreview from 'components/FileUploadWithPreview ';
|
|
|
|
import ProtectedRoute, { serverSideAuth } from "../../components/protectedRoute";
|
|
import { UserRole } from "@prisma/client";
|
|
import { Input } from '@mui/base';
|
|
|
|
const common = require('src/helpers/common');
|
|
|
|
// ------------------ LocationForm ------------------
|
|
// This component is used to create and edit locations
|
|
// location model:
|
|
// model Location {
|
|
// id Int @id @default(autoincrement())
|
|
// name String
|
|
// address String
|
|
// isActive Boolean @default(true)
|
|
// content String? @db.Text
|
|
// cartEvents CartEvent[]
|
|
// reports Report[]
|
|
|
|
// backupLocationId Int?
|
|
// backupLocation Location? @relation("BackupLocation", fields: [backupLocationId], references: [id])
|
|
// BackupForLocations Location[] @relation("BackupLocation")
|
|
// }
|
|
|
|
export default function LocationForm() {
|
|
const [uploadedImages, setUploadedImages] = useState([]);
|
|
|
|
const [isPreviewMode, setIsPreviewMode] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const fetchUploadedImages = async () => {
|
|
try {
|
|
// ToDo: we don't have this endpoint yet and we don't use the uploaded images collection
|
|
// const response = await axiosInstance.get('/uploaded-images');
|
|
// setUploadedImages(response.data.imageUrls);
|
|
} catch (error) {
|
|
console.error('Error fetching uploaded images:', error);
|
|
}
|
|
};
|
|
|
|
fetchUploadedImages();
|
|
}, []);
|
|
|
|
const quillRef = useRef(null);
|
|
const handleImageSelect = (e) => {
|
|
const imageUrl = e.target.value;
|
|
if (imageUrl && quillRef.current) {
|
|
const editor = quillRef.getQuill();
|
|
const range = editor.getSelection(true);
|
|
if (range) {
|
|
editor.insertEmbed(range.index, 'image', imageUrl);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
const [content, setContent] = useState("");
|
|
|
|
const [location, set] = useState({
|
|
name: "",
|
|
address: "",
|
|
isActive: true,
|
|
});
|
|
const [isRawHtml, setIsRawHtml] = useState(false);
|
|
|
|
// const [isEdit, setIsEdit] = useState(false);
|
|
|
|
const router = useRouter();
|
|
|
|
useEffect(() => {
|
|
const fetchLocation = async (id) => {
|
|
try {
|
|
console.log("fetching location " + router.query.id);
|
|
const { data } = await axiosInstance.get("/api/data/locations/" + id);
|
|
set(data);
|
|
setContent(data.content);
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
};
|
|
|
|
if (router.query?.id) {
|
|
fetchLocation(parseInt(router.query.id.toString()));
|
|
}
|
|
console.log("called");
|
|
}, [router.query.id]);
|
|
|
|
|
|
const [locations, setLocations] = useState([]);
|
|
useEffect(() => {
|
|
const fetchLocations = async () => {
|
|
try {
|
|
console.log("fetching locations");
|
|
const { data } = await axiosInstance.get("/api/data/locations");
|
|
setLocations(data);
|
|
console.log(data);
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
};
|
|
if (!locations.length) {
|
|
fetchLocations();
|
|
}
|
|
}, []);
|
|
|
|
|
|
const handleChange = ({ target }) => {
|
|
if (target.type === "checkbox") {
|
|
set({ ...location, [target.name]: target.checked });
|
|
} else if (target.type === "number") {
|
|
set({ ...location, [target.name]: parseInt(target.value) });
|
|
} else {
|
|
set({ ...location, [target.name]: target.value });
|
|
|
|
}
|
|
}
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault();
|
|
try {
|
|
const dataToSend = {
|
|
...location,
|
|
name: location.name.trim(),
|
|
content: content,
|
|
};
|
|
|
|
if (router.query?.id) { // UPDATE
|
|
//connect backup location
|
|
delete dataToSend.id;
|
|
dataToSend.backupLocationId = parseInt(dataToSend.backupLocationId);
|
|
// dataToSend.backupLocation = { connect: { id: location.backupLocationId } };
|
|
// delete dataToSend.backupLocationId;
|
|
await axiosInstance.put("/api/data/locations/" + router.query.id, {
|
|
...dataToSend,
|
|
});
|
|
toast.success("Task Updated", {
|
|
position: "bottom-center",
|
|
});
|
|
} else { // CREATE
|
|
await axiosInstance.post("/api/data/locations", dataToSend);
|
|
toast.success("Task Saved", {
|
|
position: "bottom-center",
|
|
});
|
|
}
|
|
|
|
router.push("/cart/locations");
|
|
} catch (error) {
|
|
//toast.error(error.response.data.message);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
<>
|
|
<ProtectedRoute allowedRoles={[UserRole.ADMIN]} deniedMessage="">
|
|
<div className="w-full max-lg">
|
|
<form className="bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4" onSubmit={handleSubmit} >
|
|
<div className="mb-4">
|
|
<label className="label" htmlFor="name">Location Name</label>
|
|
<input className="textbox" placeholder="name" id="name" name="name" onChange={handleChange} value={location.name} autoComplete="off" />
|
|
</div>
|
|
{/* Location.address */}
|
|
<div className="mb-4">
|
|
<label className="label" htmlFor="address"> Address</label>
|
|
<input className="textbox"
|
|
placeholder="address" id="address" name="address" onChange={handleChange} value={location.address} autoComplete="off" />
|
|
</div>
|
|
{/* UI for Location.isActive */}
|
|
<div className="mb-4">
|
|
<div className="form-check">
|
|
<input className="checkbox form-input" type="checkbox" id="isActive" name="isActive" onChange={handleChange} checked={location.isActive} autoComplete="off" />
|
|
<label className="label" htmlFor="isActive">Активна</label>
|
|
</div>
|
|
</div>
|
|
{/* is on main menu */}
|
|
<div className="mb-4">
|
|
<div className="form-check">
|
|
<input className="checkbox form-input" type="checkbox" id="isOnMainMenu" name="isOnMainMenu" onChange={handleChange} checked={location.isOnMainMenu} autoComplete="off" />
|
|
<label className="label" htmlFor="isOnMainMenu">Покажи в главното меню</label>
|
|
</div>
|
|
</div>
|
|
{/* backupLocation */}
|
|
<div className="mb-4">
|
|
<label className="label" htmlFor="backupLocation">При дъжд и лошо време</label>
|
|
{locations && (
|
|
<select name="backupLocationId" id="backupLocationId" onChange={handleChange} value={location.backupLocationId} placeholder="Избери локация...">
|
|
|
|
<option>Избери локация...</option>
|
|
{locations.map((loc) => (
|
|
<option key={loc.id} value={loc.id} type="number">{loc.name}</option>
|
|
))}
|
|
</select>
|
|
)}
|
|
</div>
|
|
|
|
{/* Location.content */}
|
|
<div className="mb-4">
|
|
{/* <select onChange={handleImageSelect}>
|
|
<option>Select an image</option>
|
|
{uploadedImages.map((imageUrl, index) => (
|
|
<option key={index} value={imageUrl}>{imageUrl}</option>
|
|
))}
|
|
</select> */}
|
|
{router.query.id && (
|
|
<>
|
|
<div className="flex space-x-4">
|
|
<FileUploadWithPreview
|
|
name="picture1"
|
|
label="Снимка 1"
|
|
value={location.picture1}
|
|
prefix={`location-${router.query.id}-picture1`}
|
|
onUpload={(name, imageUrl) => {
|
|
console.log('Uploaded image URL:', imageUrl);
|
|
set(location => ({ ...location, [name]: imageUrl }));
|
|
}}
|
|
/>
|
|
|
|
<FileUploadWithPreview
|
|
name="picture2"
|
|
label="Снимка 2"
|
|
value={location.picture2}
|
|
prefix={`location-${router.query.id}-picture2`}
|
|
onUpload={(name, imageUrl) => {
|
|
console.log('Uploaded image URL:', imageUrl);
|
|
set(location => ({ ...location, [name]: imageUrl }));
|
|
}}
|
|
/>
|
|
<FileUploadWithPreview
|
|
name="picture3"
|
|
label="Снимка 3"
|
|
value={location.picture3}
|
|
prefix={`location-${router.query.id}-picture3`}
|
|
onUpload={(name, imageUrl) => {
|
|
console.log('Uploaded image URL:', imageUrl);
|
|
set(location => ({ ...location, [name]: imageUrl }));
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<label className="label" htmlFor="content">Content</label>
|
|
|
|
|
|
<ProtectedRoute allowedRoles={[UserRole.ADMIN]} deniedMessage=" ">
|
|
<label className="label" htmlFor="backupLocation">Admin Edit Location HTML</label>
|
|
<div className="field mb-4">
|
|
<label className="switch">
|
|
<input
|
|
type="checkbox"
|
|
checked={isRawHtml}
|
|
onChange={(e) => {
|
|
setIsRawHtml(e.target.checked);
|
|
}}
|
|
/>
|
|
<span className="slider round"></span>
|
|
<span className="ml-2">Edit Raw HTML</span>
|
|
|
|
</label>
|
|
{isRawHtml && <>
|
|
<label className="label" htmlFor="backupLocation">Admin Edit Location HTML</label>
|
|
<textarea
|
|
className="w-full min-h-[200px] p-3 border rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 font-mono resize-y"
|
|
placeholder="HTML Content"
|
|
id="contentHTML_Raw"
|
|
name="contentHTML_Raw"
|
|
onChange={(e) => setContent(e.target.value)}
|
|
value={content}
|
|
autoComplete="off"
|
|
spellCheck="false"
|
|
/>
|
|
|
|
</>
|
|
}
|
|
</div>
|
|
</ProtectedRoute>
|
|
{!isRawHtml && <>
|
|
<TextEditor
|
|
ref={quillRef}
|
|
value={content}
|
|
onChange={setContent}
|
|
placeholder="Описание на локацията. Снимки"
|
|
prefix={`location-${router.query.id}`} />
|
|
|
|
</>}
|
|
|
|
|
|
</>)}
|
|
</div>
|
|
<div className="panel-actions pt-12">
|
|
<Link href="/cart/locations" className="action-button"> Отмени </Link>
|
|
<button className="button bg-blue-500 hover:bg-blue-700 focus:outline-none focus:shadow-outline" type="submit">
|
|
{router.query?.id ? "Обнови" : "Запази"}
|
|
</button>
|
|
</div>
|
|
|
|
</form>
|
|
</div>
|
|
<button className='button' onClick={() => setIsPreviewMode(!isPreviewMode)}>
|
|
{isPreviewMode ? 'Скрий прегледа' : 'Преглед'}
|
|
</button>
|
|
</ProtectedRoute>
|
|
<ProtectedRoute allowedRoles={[UserRole.USER]} deniedMessage=" " bypass={isPreviewMode}>
|
|
<label className="label" htmlFor="backupLocation">При дъжд и лошо време</label>
|
|
{location.name}
|
|
{location.address}
|
|
{location.backupLocationName}
|
|
<div className="border-2 border-blue-500 p-5 my-5 rounded-lg" dangerouslySetInnerHTML={{ __html: content }}></div>
|
|
</ProtectedRoute>
|
|
</>
|
|
);
|
|
}
|
|
|
|
|