Files
crm.stream.graff.tech/client/src/components/modals/CreateBuildModal.tsx
T

146 lines
4.1 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 { 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;