kgroad-frontend2/src/widgets/forms/ReportForm/ReportForm.tsx

308 lines
9.0 KiB
TypeScript
Raw Permalink 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.

"use client";
import "./ReportForm.scss";
import "leaflet/dist/leaflet.css";
import Image from "next/image";
import {
MapContainer,
Marker,
Popup,
TileLayer,
useMapEvents,
} from "react-leaflet";
import clip from "./icons/clip.svg";
import arrow_right from "./icons/arrow-right.svg";
import pin_image from "./icons/pin-image.svg";
import { ChangeEventHandler, useState } from "react";
import { Icon } from "leaflet";
import pin_icon from "./icons/pin_icon.svg";
import axios, { AxiosError } from "axios";
import { apiInstance } from "@/shared/config/apiConfig";
import { useSession } from "next-auth/react";
import { IDisplayMap } from "@/shared/types/map-type";
import Loader from "@/shared/ui/components/Loader/Loader";
import ReportMessage from "@/features/ReportMessage/ReportMessage";
interface ILatLng {
lat: number;
lng: number;
}
const ReportForm = () => {
const session = useSession();
const [latLng, setLatLng] = useState<ILatLng[]>([]);
const [displayLatLng, setDisplayLatLng] = useState<string[]>([]);
const [images, setImages] = useState<File[]>([]);
const [showMessage, setShowMessage] = useState<boolean>(false);
const position = {
lat: 42.8746,
lng: 74.606,
};
const [loader, setLoader] = useState<boolean>(false);
const [locationWarning, setLocationWarning] = useState<string>("");
const [descriptionWarning, setDescriptionWarning] =
useState<string>("");
const [imageWarning, setImageWarning] = useState<string>("");
const [error, setError] = useState<string>("");
const customIcon = new Icon({
iconUrl: pin_icon.src,
iconSize: [32, 32],
});
const handleImages: ChangeEventHandler<HTMLInputElement> = (e) => {
if (e.target.files) {
const getArrayFromObject = Array.from(e.target.files);
setImages(getArrayFromObject);
}
};
const deleteImage = (name: string) => {
setImages((prev) => prev.filter((img) => img.name !== name));
};
const createReport: React.MouseEventHandler<
HTMLFormElement
> = async (e) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
if (displayLatLng.length === 0 || latLng.length === 0) {
setDescriptionWarning("");
setImageWarning("");
setError("");
setLocationWarning("Выберите хотя бы одну точку!");
return;
}
if (!formData.get("description")) {
setLocationWarning("");
setImageWarning("");
setError("");
setDescriptionWarning("Пожалуйста опишите свое обращение");
return;
}
if (images.length === 0) {
setLocationWarning("");
setDescriptionWarning("");
setError("");
setImageWarning("Загрузите по меньшей мере одну фотографию");
return;
}
setLocationWarning("");
setDescriptionWarning("");
setError("");
setImageWarning("");
const Authorization = `Bearer ${session.data?.access_token}`;
const config = {
headers: {
Authorization,
},
};
images.forEach((image) => {
formData.append("image", image);
});
formData.append("latitude1", latLng[0].lat.toString());
formData.append("longitude1", latLng[0].lng.toString());
if (latLng.length === 2) {
formData.append("latitude2", latLng[1].lat.toString());
formData.append("longitude2", latLng[1].lng.toString());
}
try {
setLoader(true);
const res = await apiInstance.post(
"/report/create/",
formData,
config
);
if ([200, 201].includes(res.status)) {
setShowMessage(true);
}
} catch (error: unknown) {
if (error instanceof AxiosError) {
console.log(error);
setError(error.message);
} else {
setError("Произошла непредвиденная ошибка");
}
} finally {
setLoader(false);
}
};
const MapPins = (e: any) => {
useMapEvents({
click(e) {
if (latLng.length < 2) {
setLatLng([...latLng, e.latlng]);
axios
.get(
`https://nominatim.openstreetmap.org/reverse?lat=${e.latlng.lat}&lon=${e.latlng.lng}&format=json`
)
.then((res) => {
console.log(res.data);
if (res.data.address.country_code !== "kg") {
setLocationWarning("Выберите точку в Кыргызстане");
return;
} else if (res.data.address.road === undefined) {
setLocationWarning("Выберите точку на дороге");
return;
}
setDisplayLatLng([
...displayLatLng,
res.data.display_name,
]);
})
.catch((error) => console.log(error));
}
},
});
const removeMarker = (latLng: { lat: number; lng: number }) => {
setLatLng((prev) => prev.filter((l) => l !== latLng));
axios
.get<IDisplayMap>(
`https://nominatim.openstreetmap.org/reverse?lat=${latLng.lat}&lon=${latLng.lng}&format=json`
)
.then((res) =>
setDisplayLatLng((prev) =>
prev.filter((loc) => loc !== res.data.display_name)
)
)
.catch((error) => console.log(error));
};
return latLng.map((l) => (
<Marker
eventHandlers={{
click: () => {
removeMarker(l);
},
}}
key={l.lat}
icon={customIcon}
position={l}
/>
));
};
return (
<form onSubmit={createReport} className="report-form">
<div className="report-form__input">
<label>Адрес</label>
<input
value={displayLatLng.join("; ")}
disabled
placeholder="Отметьте точки на карте"
type="text"
/>
{locationWarning ? <p>{locationWarning}</p> : null}
</div>
<div className="report-form__map">
<MapContainer
center={position}
zoom={14}
scrollWheelZoom={false}
>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<MapPins />
</MapContainer>
<div className="report-form__info">
<h4>Как отметить участок дороги? </h4>
<Image src={pin_image} alt="Pin Image" />
<p>
Поставьте булавку и начните рисовать участок дороги{" "}
<span>
(он может состоять из любого количества ломаных линий)
</span>
</p>
<p>Чтобы удалить отрезок нажмите на точки повторно.</p>
</div>
</div>
<div className="report-form__input">
<label>Добавьте описание проблемы</label>
<textarea name="description" placeholder="Введите описание" />
{descriptionWarning ? <p>{descriptionWarning}</p> : null}
</div>
<div className="report-form__add-images">
<label>Добавьте фотографии</label>
<p>
Загрузите до 5 фотографии, связанные с дорогой, которую Вы
хотите отметить. Фотографии помогут лучше понять проблему.
</p>
<label
className="report-form__upload"
htmlFor="report-form-upload"
>
<Image src={clip} alt="Paper Clip Icon" />
Прикрепить файл
<span>(до 5 МБ)</span>
</label>
{imageWarning ? (
<p className="report-form__add-images-error">
{imageWarning}
</p>
) : null}
<input
onChange={handleImages}
multiple
type="file"
id="report-form-upload"
/>
{images.length ? (
<div className="report-form__images">
{images.map((image) => (
<div key={image.name}>
<img
src={URL.createObjectURL(image)}
alt="Report Image"
/>
<button onClick={() => deleteImage(image.name)}>
удалить
</button>
</div>
))}
</div>
) : null}
</div>
<button disabled={loader} type="submit">
{loader ? (
<Loader />
) : (
<>
Отправить на модерацию
<Image src={arrow_right} alt="Arrow Right Icon" />
</>
)}
</button>
{error ? <p className="report-form__error">{error}</p> : null}
{showMessage ? (
<ReportMessage
setShowMessage={setShowMessage}
showMessage={showMessage}
/>
) : null}
</form>
);
};
export default ReportForm;