318 lines
9.9 KiB
TypeScript
318 lines
9.9 KiB
TypeScript
/* eslint-disable react-hooks/exhaustive-deps */
|
||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||
import { ChangeEvent, useEffect, useState } from "react";
|
||
import api from "../../utils/api";
|
||
import Button from "../Button";
|
||
import IProject from "../../types/IProject";
|
||
import useModalStore from "../../stores/useModalStore";
|
||
|
||
interface EditProjectModalProps {
|
||
projectId: string;
|
||
}
|
||
|
||
function EditProjectModal({ projectId }: EditProjectModalProps) {
|
||
const [project, setProject] = useState<IProject>({
|
||
name: "",
|
||
company: "",
|
||
city: "",
|
||
image: "",
|
||
devices: [],
|
||
});
|
||
|
||
const [file, setFile] = useState<File>();
|
||
const [previewFile, setPreviewFile] = useState<string>();
|
||
const [setModal] = useModalStore((state) => [state.setModal]);
|
||
|
||
function handleChangeFile(e: ChangeEvent<HTMLInputElement>) {
|
||
if (!e.target.files) return;
|
||
|
||
const targetFile = e.target.files[0];
|
||
|
||
setFile(targetFile);
|
||
setPreviewFile(URL.createObjectURL(targetFile));
|
||
}
|
||
|
||
async function uploadFile() {
|
||
if (!file) return;
|
||
|
||
const formData = new FormData();
|
||
formData.append("file", file);
|
||
|
||
try {
|
||
const { file }: { file: string } = await api
|
||
.post("upload", { body: formData })
|
||
.json();
|
||
|
||
setProject((prev) => ({ ...prev, image: file }));
|
||
} catch (error) {
|
||
if (error instanceof Error) {
|
||
alert(`Error: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
async function updateProject() {
|
||
try {
|
||
await api.put(`projects/${projectId}`, { json: { ...project } });
|
||
} catch (error) {
|
||
if (error instanceof Error) {
|
||
alert(`Error: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
async function handleSubmit(e: ChangeEvent<HTMLFormElement>) {
|
||
e.preventDefault();
|
||
|
||
await updateProject();
|
||
setModal(null);
|
||
window.location.reload();
|
||
}
|
||
|
||
async function getProject() {
|
||
try {
|
||
const project: IProject = await api.get(`projects/${projectId}`).json();
|
||
|
||
setProject(project);
|
||
} catch (error) {
|
||
if (error instanceof Error) {
|
||
alert(`Error: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
useEffect(() => {
|
||
uploadFile();
|
||
}, [file]);
|
||
|
||
useEffect(() => {
|
||
getProject();
|
||
}, []);
|
||
|
||
return (
|
||
<div className="bg-white shadow-lg text-black p-8 rounded-xl flex flex-col gap-4">
|
||
<p className="text-xl pb-4 border-b border-[#ccc]">
|
||
Редактирование проекта
|
||
</p>
|
||
<form
|
||
onSubmit={handleSubmit}
|
||
className="grid grid-cols-2 gap-4 w-[512px]"
|
||
>
|
||
<div className="flex flex-col gap-1">
|
||
<label className="text-sm">Название</label>
|
||
<input
|
||
autoFocus
|
||
required
|
||
type="text"
|
||
placeholder="Название"
|
||
className="border border-neutral-500 px-3 py-2 rounded-lg outline-none"
|
||
value={project.name}
|
||
onChange={(e) =>
|
||
setProject((prev) => ({ ...prev, name: e.target.value }))
|
||
}
|
||
/>
|
||
</div>
|
||
|
||
<div className="flex flex-col gap-1">
|
||
<label className="text-sm">Компания</label>
|
||
<input
|
||
required
|
||
type="text"
|
||
placeholder="Компания"
|
||
className="border border-neutral-500 px-3 py-2 rounded-lg outline-none"
|
||
value={project.company}
|
||
onChange={(e) =>
|
||
setProject((prev) => ({ ...prev, company: e.target.value }))
|
||
}
|
||
/>
|
||
</div>
|
||
|
||
<div className="flex flex-col gap-1">
|
||
<label className="text-sm">Город</label>
|
||
<input
|
||
required
|
||
type="text"
|
||
placeholder="Город"
|
||
className="border border-neutral-500 px-3 py-2 rounded-lg outline-none"
|
||
value={project.city}
|
||
onChange={(e) =>
|
||
setProject((prev) => ({ ...prev, city: e.target.value }))
|
||
}
|
||
/>
|
||
</div>
|
||
|
||
<label className="relative border border-dashed border-neutral-500 px-3 py-2 hover:bg-opacity-10 hover:bg-black cursor-pointer rounded-lg flex flex-col gap-2">
|
||
<input
|
||
type="file"
|
||
accept="image/*"
|
||
className="absolute opacity-0"
|
||
onChange={handleChangeFile}
|
||
/>
|
||
<p>{file ? file.name : "Выберите изображение"}</p>
|
||
|
||
{project.image && <img src={project.image} alt="" />}
|
||
{previewFile && <img src={previewFile} alt="" />}
|
||
</label>
|
||
|
||
<div className="flex flex-col gap-1">
|
||
<label className="text-sm">Стадия</label>
|
||
<select
|
||
required
|
||
value={project.stage || ""}
|
||
className="border border-neutral-500 px-3 py-2 rounded-lg outline-none"
|
||
onChange={(e) =>
|
||
setProject((prev) => ({ ...prev, stage: +e.target.value }))
|
||
}
|
||
>
|
||
<option value="" disabled>
|
||
Выберите стадию
|
||
</option>
|
||
<option value={1}>1</option>
|
||
<option value={2}>2</option>
|
||
<option value={3}>3</option>
|
||
<option value={4}>4</option>
|
||
<option value={5}>5</option>
|
||
<option value={6}>6</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div className="flex flex-col gap-1">
|
||
<label className="text-sm">Год релиза</label>
|
||
<select
|
||
required
|
||
value={project.releaseYear || ""}
|
||
className="border border-neutral-500 px-3 py-2 rounded-lg outline-none"
|
||
onChange={(e) =>
|
||
setProject((prev) => ({ ...prev, releaseYear: +e.target.value }))
|
||
}
|
||
>
|
||
<option value="" disabled>
|
||
Выберите год релиза
|
||
</option>
|
||
<option value={2024}>2024</option>
|
||
<option value={2023}>2023</option>
|
||
<option value={2022}>2022</option>
|
||
<option value={2021}>2021</option>
|
||
<option value={2020}>2020</option>
|
||
<option value={2019}>2019</option>
|
||
<option value={2018}>2018</option>
|
||
<option value={2017}>2017</option>
|
||
<option value={2016}>2016</option>
|
||
<option value={2015}>2015</option>
|
||
<option value={2014}>2014</option>
|
||
<option value={2013}>2013</option>
|
||
<option value={2012}>2012</option>
|
||
<option value={2011}>2011</option>
|
||
<option value={2010}>2010</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div className="flex flex-col gap-1">
|
||
<p className="text-sm">Девайсы</p>
|
||
<div className="">
|
||
<label className="flex items-center gap-2">
|
||
<input
|
||
type="checkbox"
|
||
checked={project.devices!.includes("stream")}
|
||
onChange={(e) => {
|
||
if (e.target.checked) {
|
||
setProject((prev) => ({
|
||
...prev,
|
||
devices: [...prev.devices!, "stream"],
|
||
}));
|
||
} else {
|
||
setProject((prev) => ({
|
||
...prev,
|
||
devices: prev.devices!.filter(
|
||
(device) => device !== "stream"
|
||
),
|
||
}));
|
||
}
|
||
}}
|
||
/>
|
||
<span>Stream</span>
|
||
</label>
|
||
|
||
<label className="flex items-center gap-2">
|
||
<input
|
||
type="checkbox"
|
||
checked={project.devices!.includes("touch")}
|
||
onChange={(e) => {
|
||
if (e.target.checked) {
|
||
setProject((prev) => ({
|
||
...prev,
|
||
devices: [...prev.devices!, "touch"],
|
||
}));
|
||
} else {
|
||
setProject((prev) => ({
|
||
...prev,
|
||
devices: prev.devices!.filter(
|
||
(device) => device !== "touch"
|
||
),
|
||
}));
|
||
}
|
||
}}
|
||
/>
|
||
<span>Touch</span>
|
||
</label>
|
||
|
||
<label className="flex items-center gap-2">
|
||
<input
|
||
type="checkbox"
|
||
checked={project.devices!.includes("mobile")}
|
||
onChange={(e) => {
|
||
if (e.target.checked) {
|
||
setProject((prev) => ({
|
||
...prev,
|
||
devices: [...prev.devices!, "mobile"],
|
||
}));
|
||
} else {
|
||
setProject((prev) => ({
|
||
...prev,
|
||
devices: prev.devices!.filter(
|
||
(device) => device !== "mobile"
|
||
),
|
||
}));
|
||
}
|
||
}}
|
||
/>
|
||
<span>Mobile</span>
|
||
</label>
|
||
|
||
<label className="flex items-center gap-2">
|
||
<input
|
||
type="checkbox"
|
||
checked={project.devices!.includes("vr")}
|
||
onChange={(e) => {
|
||
if (e.target.checked) {
|
||
setProject((prev) => ({
|
||
...prev,
|
||
devices: [...prev.devices!, "vr"],
|
||
}));
|
||
} else {
|
||
setProject((prev) => ({
|
||
...prev,
|
||
devices: prev.devices!.filter(
|
||
(device) => device !== "vr"
|
||
),
|
||
}));
|
||
}
|
||
}}
|
||
/>
|
||
<span>VR</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="col-span-full flex justify-end">
|
||
<Button className="text-white outline-none">
|
||
Сохранить изменения
|
||
</Button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default EditProjectModal;
|