arm-web/src/widgets/NewConsoleModal/NewConsoleModal.tsx

365 lines
12 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 React, { useEffect, useMemo, useRef, useState } from "react";
import {
Modal,
Form,
Select,
Row,
Col,
Checkbox,
Input,
Empty,
Spin,
} from "antd";
import {
fetchLegEntsByComGroup,
fetchGSKByComGroup,
fetchGroupMembers,
} from "../../services/api/directories/company_groups";
import { useAppDispatch, useAppSelector } from "../../store/store";
import { getCompanyGroupsThunk } from "../../store/slices/companyGroupsSlice/thunks";
type FormValues = {
company_group_id: string;
related_group_id: string;
year: number;
quarter: string; // example of 2nd static field
legal_entity_ids: string[];
};
type Props = {
open: boolean;
onCancel: () => void;
onOk: (values: FormValues) => void | Promise<void>;
};
export const MyModal: React.FC<Props> = ({ open, onCancel, onOk }) => {
const [form] = Form.useForm<FormValues>();
const dispatch = useAppDispatch();
const { raw_rows, loading } = useAppSelector((state) => state.company_groups);
const [subgroups, setSubgroups] = useState<any[]>([]);
const [loadingSubgroups, setLoadingSubgroups] = useState(false);
const subgroupReqRef = useRef(0);
const [legEntities, setLegEntities] = useState<any[]>([]);
const [loadingEntities, setLoadingEntities] = useState(false);
const legEntReqRef = useRef(0);
const [legEntSearch, setLegEntSearch] = useState("");
const gskEntReqRef = useRef(0);
const company_group_id = Form.useWatch("company_group_id", form);
const related_group_id = Form.useWatch("related_group_id", form);
const year = Form.useWatch("year", form);
const semester = Form.useWatch("quarter", form);
const legEntIds = Form.useWatch("legal_entity_ids", form);
const quarterOptions = [
{ value: "Q1", label: "1 квартал" },
{ value: "Q2", label: "2 квартал" },
{ value: "Q3", label: "3 квартал" },
{ value: "Q4", label: "4 квартал" },
];
useEffect(() => {
if (open && raw_rows.length === 0 && !loading) {
dispatch(getCompanyGroupsThunk());
}
}, [open, raw_rows.length, loading, dispatch]);
const isSubmitDisabled =
!company_group_id ||
!related_group_id ||
!year ||
!semester ||
!legEntIds?.length;
const yearOptions = useMemo(() => {
const y = new Date().getFullYear();
return Array.from({ length: 4 }, (_, i) => ({
value: y - i,
label: String(y - i),
}));
}, []);
const clearLegEnts = () => {
setLegEntities([]);
setLoadingEntities(false);
setLegEntSearch("");
legEntReqRef.current++;
};
// const handleGroupChange = async (value?: string) => {
// form.setFieldsValue({
// related_group_id: undefined as any,
// legal_entity_ids: [],
// });
// setSubgroups([]);
// clearLegEnts();
// if (!value) return;
// const reqId = ++subgroupReqRef.current;
// setLoadingSubgroups(true);
// try {
// const data = await fetchGSKByComGroup(value);
// if (reqId === subgroupReqRef.current) setSubgroups(data);
// } finally {
// if (reqId === subgroupReqRef.current) setLoadingSubgroups(false);
// }
// };
const handleSubGroupChange = async (value?: string) => {
form.setFieldsValue({ legal_entity_ids: [] });
if (!value) return;
const reqId = ++gskEntReqRef.current;
setLoadingEntities(true);
try {
const data = await fetchGroupMembers(value);
if (reqId !== gskEntReqRef.current) return;
const gskEntitiesIds = new Set(data.map((c) => c.legal_entity_id));
const sorted = [...legEntities].sort((a, b) => {
const a_gsk = gskEntitiesIds.has(a.id);
const b_gsk = gskEntitiesIds.has(b.id);
if (a_gsk !== b_gsk) return a_gsk ? -1 : 1;
return a.name.localeCompare(b.name);
});
setLegEntities(sorted);
form.setFieldsValue({ legal_entity_ids: [...gskEntitiesIds] });
} finally {
if (reqId === gskEntReqRef.current) setLoadingEntities(false);
}
};
const handleGroupChange = async (value?: string) => {
form.setFieldsValue({
related_group_id: undefined as any,
legal_entity_ids: [],
});
setSubgroups([]);
clearLegEnts();
if (!value) return;
const reqId = ++subgroupReqRef.current;
const leReqId = ++legEntReqRef.current;
setLoadingSubgroups(true);
setLoadingEntities(true);
try {
const dataSubgroups = await fetchGSKByComGroup(value);
const dataLegEnts = await fetchLegEntsByComGroup(value);
if (reqId === subgroupReqRef.current) setSubgroups(dataSubgroups);
if (leReqId === legEntReqRef.current) setLegEntities(dataLegEnts);
} finally {
if (reqId === subgroupReqRef.current) setLoadingSubgroups(false);
if (leReqId === legEntReqRef.current) setLoadingEntities(false);
}
};
const resetLocal = () => {
setSubgroups([]);
setLoadingSubgroups(false);
subgroupReqRef.current++;
clearLegEnts();
setLegEntSearch("");
};
const handleOk = async () => {
try {
const values = await form.validateFields();
await onOk(values);
form.resetFields();
resetLocal();
} catch {}
};
const handleCancel = () => {
form.resetFields();
resetLocal();
onCancel();
};
const filteredLegEnts = useMemo(() => {
if (!legEntSearch) return legEntities;
const q = legEntSearch.toLowerCase();
return legEntities.filter((s) => s.name.toLowerCase().includes(q));
}, [legEntities, legEntSearch]);
return (
<Modal
title="Новая консолидация"
open={open}
onCancel={handleCancel}
onOk={handleOk}
okButtonProps={{ disabled: isSubmitDisabled }}
okText="Submit"
destroyOnHidden
width={640}
>
<Form form={form} layout="vertical" preserve={false}>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="company_group_id"
label="Группа компаний"
rules={[{ required: true, message: "Укажите ГК" }]}
>
<Select
placeholder="Выберите ГК"
loading={loading}
showSearch
allowClear
filterOption={(input, option) =>
(option?.label as string)
?.toLowerCase()
.includes(input.toLowerCase())
}
options={raw_rows.map((g) => ({ value: g.id, label: g.name }))}
onChange={handleGroupChange}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="related_group_id"
label="ГСК"
rules={[{ required: true, message: "Укажите ГСК" }]}
>
<Select
placeholder={
company_group_id ? "Выберите ГСК" : "Сначала выберите ГК"
}
disabled={!company_group_id}
loading={loadingSubgroups}
showSearch
allowClear
filterOption={(input, option) =>
(option?.label as string)
?.toLowerCase()
.includes(input.toLowerCase())
}
options={subgroups.map((s) => ({ value: s.id, label: s.name }))}
onChange={handleSubGroupChange}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="year"
label="Год"
rules={[{ required: true, message: "Укажите год" }]}
>
<Select placeholder="Выберите год" options={yearOptions} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="quarter"
label="Квартал"
rules={[{ required: true, message: "Укажите квартал" }]}
>
<Select placeholder="Выберите квартал" options={quarterOptions} />
</Form.Item>
</Col>
{/* ROW 3 - full width block, only after subgroup is selected */}
{related_group_id && (
<Col span={24}>
<Form.Item
name="legal_entity_ids"
label="ЮЛ консолидации"
rules={[
{ required: true, message: "Выберите хотя бы одно ЮЛ" },
]}
>
<Checkbox.Group style={{ width: "100%" }}>
<div
style={{
border: "1px solid #d9d9d9",
borderRadius: 8,
padding: 12,
width: "100%",
}}
>
<Input.Search
placeholder="Поиск ЮЛ..."
allowClear
value={legEntSearch}
onChange={(e) => setLegEntSearch(e.target.value)}
style={{ marginBottom: 12 }}
/>
{loadingEntities ? (
<div style={{ textAlign: "center", padding: 24 }}>
<Spin />
</div>
) : filteredLegEnts.length === 0 ? (
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description={
legEntities.length === 0
? "Нет строк"
: "Нет совпадений"
}
/>
) : (
<div
style={{
maxHeight: 200,
overflowY: "auto",
display: "flex",
flexDirection: "column",
width: "100%",
gap: 8,
}}
>
{filteredLegEnts.map((s) => (
<Checkbox
key={s.id}
value={s.id}
style={{ width: "100%", display: "flex" }}
>
<div
style={{
flex: 1,
justifySelf: "flex-start",
display: "flex",
justifyContent: "space-between",
width: "100%",
gap: "30px",
}}
>
<span style={{ display: "inline-block" }}>
{s.name}
</span>
<span
style={{
display: "inline-block",
color: "rgb(102, 112, 133)",
fontSize: 11,
}}
>
{s.inn}
</span>
</div>
</Checkbox>
))}
</div>
)}
{!loadingEntities && legEntities.length > 0 && (
<div style={{ marginTop: 8, display: "flex", gap: 12 }}>
<span style={{ marginLeft: "auto", color: "#8c8c8c" }}>
Выбрано: {legEntIds?.length ?? 0} /{" "}
{legEntities.length}
</span>
</div>
)}
</div>
</Checkbox.Group>
</Form.Item>
</Col>
)}
</Row>
</Form>
</Modal>
);
};