146 lines
4.1 KiB
TypeScript
146 lines
4.1 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import Button from "../Button";
|
||
import api from "../../utils/api";
|
||
import IError from "../../types/IError";
|
||
import IBuild from "../../types/IBuild";
|
||
import useModalStore from "../../stores/useModalStore";
|
||
import SpinnerIcon from "../icons/SpinnerIcon";
|
||
|
||
interface Props {
|
||
companyId: string;
|
||
}
|
||
|
||
function CreateBuildModal({ companyId }: Props) {
|
||
const [builds, setBuilds] = useState<IBuild[]>([]);
|
||
const [selectedBuilds, setSelectedBuilds] = useState<IBuild[]>([]);
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||
const { setModal } = useModalStore();
|
||
|
||
function toggleBuild(build: IBuild) {
|
||
setSelectedBuilds((prev) =>
|
||
prev.some((b) => b.id === build.id)
|
||
? prev.filter((b) => b.id !== build.id)
|
||
: [...prev, build]
|
||
);
|
||
}
|
||
|
||
async function fetchBuilds() {
|
||
try {
|
||
const result: IBuild[] | IError = await api
|
||
.get(`admin/builds?availableForCompany=${companyId}`)
|
||
.json();
|
||
|
||
if ("error" in result) {
|
||
alert(result.error);
|
||
return;
|
||
}
|
||
|
||
setBuilds(result);
|
||
} catch (error) {
|
||
alert((error as Error).message);
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
}
|
||
|
||
useEffect(() => {
|
||
fetchBuilds();
|
||
}, [companyId]);
|
||
|
||
async function handleAdd() {
|
||
if (selectedBuilds.length === 0) return;
|
||
|
||
setIsSubmitting(true);
|
||
try {
|
||
for (const build of selectedBuilds) {
|
||
const result: IBuild | IError = await api
|
||
.post("admin/builds", {
|
||
json: {
|
||
companyId,
|
||
buildId: build.id,
|
||
},
|
||
})
|
||
.json();
|
||
|
||
if ("error" in result) {
|
||
alert(result.error);
|
||
return;
|
||
}
|
||
}
|
||
|
||
setModal(null);
|
||
window.location.reload();
|
||
} catch (error) {
|
||
alert((error as Error).message);
|
||
} finally {
|
||
setIsSubmitting(false);
|
||
}
|
||
}
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<div className="flex justify-center items-center p-12 bg-white rounded-lg w-[400px]">
|
||
<SpinnerIcon />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (builds.length === 0) {
|
||
return (
|
||
<div className="p-8 space-y-4 bg-white rounded-lg w-[400px]">
|
||
<p className="text-xl font-semibold">Добавить ЖК</p>
|
||
<p className="text-gray-600">
|
||
В базе данных пока нет ЖК. Создайте ЖК через базу данных вручную.
|
||
</p>
|
||
<div className="flex self-end">
|
||
<Button variant="secondary" onClick={() => setModal(null)}>
|
||
Закрыть
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="p-8 space-y-4 bg-white rounded-lg w-[400px]">
|
||
<p className="text-xl font-semibold">Выбор ЖК</p>
|
||
<p className="text-sm text-gray-600">
|
||
Выберите один или несколько ЖК для добавления в компанию
|
||
</p>
|
||
<div className="overflow-y-auto space-y-2 max-h-64">
|
||
{builds.map((build) => (
|
||
<button
|
||
key={build.id}
|
||
type="button"
|
||
onClick={() => toggleBuild(build)}
|
||
className={`w-full p-4 text-left rounded-lg border transition-colors ${
|
||
selectedBuilds.some((b) => b.id === build.id)
|
||
? "border-blue-500 bg-blue-50"
|
||
: "border-gray-200 hover:border-gray-300 hover:bg-gray-50"
|
||
}`}
|
||
>
|
||
<p className="font-medium">{build.name}</p>
|
||
<p className="text-sm text-gray-500">Сборка: {build.build}</p>
|
||
</button>
|
||
))}
|
||
</div>
|
||
<div className="flex gap-2 self-end">
|
||
<Button variant="secondary" onClick={() => setModal(null)}>
|
||
Отмена
|
||
</Button>
|
||
<Button
|
||
onClick={handleAdd}
|
||
disabled={selectedBuilds.length === 0 || isSubmitting}
|
||
>
|
||
{isSubmitting
|
||
? "Добавление…"
|
||
: `Добавить${selectedBuilds.length > 0 ? ` (${selectedBuilds.length})` : ""}`}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default CreateBuildModal;
|