import { ColumnType } from "antd/lib/table"; import { DoubleLeftOutlined, DoubleRightOutlined, LeftOutlined, RightOutlined, } from "@ant-design/icons"; import { DropTargetMonitor, useDrag, useDrop } from "react-dnd"; import { Field, Form } from "react-final-form"; import { FieldValidator, composeFieldValidators, required, } from "@dmitry.olyenyov/final-form-validations"; import { FormApi } from "final-form"; import { OnChange } from "react-final-form-listeners"; import { XYCoord } from "dnd-core"; import { move } from "ramda"; import { useSelector } from "react-redux"; import React, { FC, Fragment, memo, useCallback, useEffect, useMemo, useRef, useState, } from "react"; import cn from "classnames"; import { openNotification } from "../../../lib/helpers"; import { selectHasCommonTemplatesAccess, selectHasCopyCommonTemplateAccess, } from "../../../features/core/App/authSlice"; import { selectShowSystemNames, selectShowUserNames, } from "../../../features/core/App/templatesSlice"; import ColorPicker from "../../atoms/ColorPicker"; import GraphQLErrorMessage from "../../atoms/GraphQLErrorMessage"; import Search from "../../molecules/Search"; import { SummationDepth } from "../../../graphql-schema-types.generated"; import { COLOR_TYPES, ExtendedColumnType, NOTIFICATION_FROM, } from "../../../lib/types"; import { Button, DotsLoading, RadioButton, Select, TextInput, Toggle, Tooltip, WarningModal, } from "../../atoms"; import { Add2Icon, Close3Icon, Complete3Icon, CopyIcon, DraggableIcon, Edit2Icon, EditIcon, GlobalIcon, } from "../../atoms/Icons"; import { flatMapColumns, updateValues } from "./utils"; import { TemplateFragment, useEditSumDepthMutation, useGetTemplatesListQuery, useMakeTemplateActiveMutation, useMakeTemplateCommonMutation, useRemoveTamplateMutation, useRenameTemplateMutation, useSaveTemplateMutation, } from "./index.generated"; import { SOURCE_TECH_LIKE_COLUMNS, colorOptions } from "./constants"; import css from "./index.module.css"; type ListType = "used" | "available"; export type TemplateMsfoAction = { name: string; text: string; onChange: (v: boolean) => void; }; interface DragItem { source: { index: number; list: ListType; }; column: ColumnType & { hidden?: boolean }; } type Props = { columnNamesActions?: TemplateMsfoAction[]; initialColumns: Array>; onClose: () => void; tableName: string; useDescription?: boolean; enableSumDepth?: boolean; showTechBusinessColumnsSplit?: boolean; useFilterTitleInsteadOfTitle?: boolean; }; export interface FormValues { commonTemplate: string; personalTemplate: string; templateName?: string; msfoAction?: Record; techColumns?: boolean; businessColumns?: boolean; sumDepth: SummationDepth; } const TemplatesForm: FC = ({ columnNamesActions, initialColumns, onClose, tableName, useDescription, enableSumDepth, showTechBusinessColumnsSplit, useFilterTitleInsteadOfTitle, }) => { const hasCommonTemplatesAccess = useSelector(selectHasCommonTemplatesAccess); const hasCopyCommonTemplateAccess = useSelector( selectHasCopyCommonTemplateAccess ); const { data: commonData, loading: commonLoading, error: commonError, refetch: commonRefetch, } = useGetTemplatesListQuery({ variables: { table: tableName, isCommon: true, }, fetchPolicy: "network-only", }); const { data: personalData, loading: personalLoading, error: personalError, refetch: personalRefetch, } = useGetTemplatesListQuery({ variables: { table: tableName, isCommon: false, }, fetchPolicy: "network-only", }); const [color, setColor] = useState<{ [key: string]: COLOR_TYPES }>({}); const [mode, setMode] = useState<"create" | "edit" | "rename" | null>(null); const [search1, setSearch1] = useState(null); const [search2, setSearch2] = useState(null); const [{ isOver: isOverUsedList }, dropUsedList] = useDrop({ accept: "toggle", drop: (item: DragItem) => { if (item.source.list === "available") { handleMoveToUsedList(item.source.index); } }, collect: (monitor) => ({ isOver: Boolean(monitor.isOver()) }), }); const [{ isOver: isOverAvailableList }, dropAvailableList] = useDrop({ accept: "toggle", drop: (item: DragItem) => { if (item.source.list === "used") { handleMoveToAvailableList(item.source.index); } }, collect: (monitor) => ({ isOver: Boolean(monitor.isOver()) }), }); const [selectedTemplateName, setSelectedTemplateName] = useState< string | undefined >(); const [isCommonSelected, setIsCommonSelected] = useState(false); const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false); const disableEditCommon = isCommonSelected && !hasCommonTemplatesAccess; const commonTemplates = useMemo( () => commonData?.templates.list ?? [], [commonData?.templates.list] ); const commonTemplatesOptions = useMemo( () => commonTemplates.map((t) => t.name), [commonTemplates] ); const personalTemplates = useMemo( () => personalData?.templates.list ?? [], [personalData?.templates.list] ); const personalTemplatesOptions = useMemo( () => personalTemplates.map((t) => t.name), [personalTemplates] ); const allTemplates = useMemo( () => [...commonTemplates, ...personalTemplates], [commonTemplates, personalTemplates] ); const activeTemplate = useMemo( () => !commonData || !personalData || commonLoading || personalLoading ? undefined : allTemplates.find((t) => t.active) ?? (personalTemplates[0] as TemplateFragment | undefined) ?? (commonTemplates[0] as TemplateFragment | undefined), [ allTemplates, commonData, commonLoading, commonTemplates, personalData, personalLoading, personalTemplates, ] ); const selectedTemplate = useMemo( () => allTemplates.find((t) => t.name === selectedTemplateName), [allTemplates, selectedTemplateName] ); const refetch = useCallback( () => (isCommonSelected ? commonRefetch() : personalRefetch()), [commonRefetch, isCommonSelected, personalRefetch] ); const showUserNames = useSelector(selectShowUserNames); const showSystemNames = useSelector(selectShowSystemNames); // const hasDefaultTemplatesAccess = useSelector( // selectHasDefaultTemplatesAccess // ); // const userRoles = useSelector(selectPrimaryUserRoles); // const hasMultiRoles = userRoles.length > 1; // const showDefault = useMemo( // () => // ["clients", "deals", "accounts"].includes(tableName) && // (hasDefaultTemplatesAccess || hasMultiRoles), // [tableName, hasDefaultTemplatesAccess, hasMultiRoles] // ); const [columns, setColumns] = useState>>([]); const [usedColumnIndices, setUsedColumnIndices] = useState([]); const [availableColumnIndices, setAvailableColumnIndices] = useState< number[] >([]); const updateUsedAndAvailableColumns = useCallback( (columns: Array>) => { setUsedColumnIndices( columns .map((c, index) => [index, c] as const) .filter((c) => !c[1].hidden) .map((c) => c[0]) ); setAvailableColumnIndices( columns .map((c, index) => [index, c] as const) .filter((c) => c[1].hidden) .map((c) => c[0]) ); }, [] ); const toggleTechColumns = (enabled: boolean) => { const updatedColumns = columns.map((col, index) => ({ ...col, hidden: !!col.techColumn || SOURCE_TECH_LIKE_COLUMNS.includes( col.dataIndex?.toString().toLowerCase() ?? "" ) ? !enabled : availableColumnIndices.includes(index), })); updateUsedAndAvailableColumns(updatedColumns); }; const toggleBusinessColumns = (enabled: boolean) => { const updatedColumns = columns.map((col, index) => ({ ...col, hidden: !col.techColumn && !SOURCE_TECH_LIKE_COLUMNS.includes( col.dataIndex?.toString().toLowerCase() ?? "" ) ? !enabled : availableColumnIndices.includes(index), })); updateUsedAndAvailableColumns(updatedColumns); }; useEffect(() => { if (selectedTemplate == null) { return; } setSelectedTemplateName(selectedTemplate.name); const flatColumns = flatMapColumns(initialColumns); const updatedColumns = flatColumns .slice() .sort( (a, b) => selectedTemplate.columns.findIndex((c) => c.name === a.dataIndex) - selectedTemplate.columns.findIndex((c) => c.name === b.dataIndex) ) .map((c) => ({ ...c, hidden: selectedTemplate.columns.find((tc) => tc.name === c.dataIndex) ?.hidden ?? false, })); setColumns(updatedColumns); updateUsedAndAvailableColumns(updatedColumns); }, [initialColumns, selectedTemplate, updateUsedAndAvailableColumns]); useEffect(() => { updateUsedAndAvailableColumns(columns); }, [columns, updateUsedAndAvailableColumns]); useEffect(() => { if (selectedTemplateName == null && activeTemplate != null) { setSelectedTemplateName(activeTemplate.name); } }, [activeTemplate, selectedTemplateName]); const [saveTemplateMutation, { loading: saveLoading, error: saveError }] = useSaveTemplateMutation(); const [ removeTamplateMutation, { loading: removeLoading, error: removeError }, ] = useRemoveTamplateMutation(); const [ makeTemplateActiveMutation, { loading: activateLoading, error: activateError }, ] = useMakeTemplateActiveMutation(); const [ renameTemplateMutation, { loading: renameLoading, error: renameError }, ] = useRenameTemplateMutation(); const [ editSumDepthMutation, { loading: editSumDepthLoading, error: editSumDepthError }, ] = useEditSumDepthMutation(); const [ makeTemplateCommonMutation, { loading: makeCommonLoading, error: makeCommonError }, ] = useMakeTemplateCommonMutation(); // const [ // createDefaultTemplateMutation, // { loading: createDefaultLoading, error: createDefaultError }, // ] = useCreateDefaultTemplateMutation(); const handleCancelNameEdit = useCallback( (form: FormApi) => () => { form.resetFieldState("templateName"); form.change("templateName", undefined); setMode(null); }, [] ); const handleAddTemplate = useCallback( async ({ templateName }: FormValues, form: FormApi) => { if (!templateName || templateName === "") { return; } if (!allTemplates.some((t) => t.name === templateName)) { const res = await saveTemplateMutation({ variables: { table: tableName, templateName, isCommon: false, template: { isCommon: false, summationDepth: enableSumDepth ? SummationDepth.Million : undefined, columns: flatMapColumns(initialColumns).map((c) => ({ name: c.dataIndex?.toString() ?? "", hidden: (c as any)?.defaultHidden ?? false, color: COLOR_TYPES.TRANSPARENT, description: showTechBusinessColumnsSplit || useDescription ? c.description : (c.title as string), })), }, }, }); if (res.data?.templates.save) { const template = res.data.templates.save[0]; await personalRefetch(); setSelectedTemplateName(templateName); handleCancelNameEdit(form)(); updateValues(template, form); setTimeout(() => { // Грязный хак для того, чтобы useMemo personalTemplatesOptions успело пересчитаться form.change("personalTemplate", templateName); }, 1); setIsCommonSelected(false); } } }, [ allTemplates, enableSumDepth, handleCancelNameEdit, showTechBusinessColumnsSplit, initialColumns, useDescription, personalRefetch, saveTemplateMutation, tableName, ] ); // Дефолтные шаблоны пока убрали, не понятно понадобятся ли // const handleCreateDefaultTemplate = useCallback(() => { // if (!hasMultiRoles) { // createDefaultTemplateMutation({ // variables: { // table: tableName, // }, // }).then(async (res) => { // if (res.data?.templates.createDefault) { // await renameTemplateMutation({ // variables: { // table: tableName, // templateName: res.data.templates.createDefault.name, // newTemplateName: `${ // transcriptRoles[userRoles[0]] ?? "Шаблон по умолчанию" // }${getTemplateNumber(templates, true)}`, // }, // }); // const _templates = await refetch().then( // ({ data }) => data.templates.list ?? [] // ); // if (_templates.length === 1) { // setSelectedTemplateName(_templates[0].name); // } // } // }); // }setColumns // tableName, // refetch, // userRoles, // templates, // hasMultiRoles, // ]); const handleRemoveTemplate = useCallback( (form: FormApi) => async () => { if (selectedTemplateName == null) { return; } setShowDeleteConfirmation(false); const res = await removeTamplateMutation({ variables: { table: tableName, templateName: selectedTemplateName, isCommon: isCommonSelected, }, }); if (!res.data?.templates.delete) { return; } openNotification("open", { message: "Настройка шаблона", description: `Шаблон "${selectedTemplateName}" удален`, }); const res2 = await refetch(); if (res2.data.templates.list.length === 0) { setSelectedTemplateName(""); form.change( isCommonSelected ? "commonTemplate" : "personalTemplate", undefined ); return; } const template = res2.data.templates.list[0]; setSelectedTemplateName(template.name); updateValues(template, form); form.change( isCommonSelected ? "commonTemplate" : "personalTemplate", template.name ); }, [ removeTamplateMutation, tableName, selectedTemplateName, isCommonSelected, refetch, ] ); const handleSaveChanges = useCallback( async ( values: FormValues, newTemplateName?: string, isCommon?: boolean ) => { const templateName = newTemplateName ?? selectedTemplateName; if (templateName == null) { return false; } const res = await saveTemplateMutation({ variables: { table: tableName, templateName, isCommon: isCommon ?? isCommonSelected, template: { summationDepth: enableSumDepth ? values.sumDepth : undefined, isCommon: isCommon ?? isCommonSelected, columns: [ ...usedColumnIndices.map((idx) => ({ ...columns[idx], hidden: false, })), ...availableColumnIndices.map((idx) => ({ ...columns[idx], hidden: true, })), ].map((c) => { const name = c.dataIndex as string; return { name, hidden: c.hidden, color: color[name], description: showTechBusinessColumnsSplit || useDescription ? c.description : (c.title as string), width: c.width?.toString(), }; }), }, }, }); await refetch(); return res.data?.templates.save; }, [ saveTemplateMutation, tableName, selectedTemplateName, isCommonSelected, enableSumDepth, usedColumnIndices, availableColumnIndices, refetch, columns, color, showTechBusinessColumnsSplit, useDescription, ] ); const handleSaveCurrentTemplate = useCallback( (values: FormValues) => async () => { const res = await handleSaveChanges(values); if (res) { openNotification("open", { message: "Настройка шаблона", description: "Изменения сохранены", }); setMode(null); } }, [handleSaveChanges] ); const handleActivateTemplate = useCallback( (values: FormValues) => async () => { if (selectedTemplateName == null) { return; } if (mode === "edit") { await handleSaveCurrentTemplate(values)(); } makeTemplateActiveMutation({ variables: { table: tableName, templateName: selectedTemplateName, isCommon: isCommonSelected, }, }).then((res) => res.data?.templates.active && onClose()); }, [ makeTemplateActiveMutation, tableName, selectedTemplateName, isCommonSelected, onClose, mode, handleSaveCurrentTemplate, ] ); const handleEditSumDepth = useCallback( (sumDepth: SummationDepth) => { if (selectedTemplateName == null) { return; } const template = allTemplates.find( (t) => t.name === selectedTemplateName ); if (template?.summationDepth !== sumDepth) { editSumDepthMutation({ variables: { table: tableName, templateName: selectedTemplateName, isCommon: isCommonSelected, summationDepth: sumDepth, }, }).then( (res) => res.data?.templates.editSummationDepth && void refetch() ); } }, [ allTemplates, editSumDepthMutation, isCommonSelected, refetch, selectedTemplateName, tableName, ] ); const handleMakeTemplateCommon = useCallback( (form: FormApi) => async () => { if (selectedTemplateName == null) { return; } if (commonTemplates.some((t) => t.name === selectedTemplateName)) { openNotification("warning", { message: "Настройка шаблона", description: "Уже существует общин шаблон с таким именем. Переименуйте шаблон или выберите другой", duration: 12, }); } else { const res = await makeTemplateCommonMutation({ variables: { table: tableName, templateName: selectedTemplateName, }, }); if (res.data?.templates.sign) { await personalRefetch(); await commonRefetch(); setIsCommonSelected(true); setMode(null); form.change("personalTemplate", undefined); form.change("commonTemplate", selectedTemplateName); openNotification("open", { message: "Настройка шаблона", description: `Шаблон "${selectedTemplateName}" помечен общим`, }); } } }, [ commonRefetch, commonTemplates, makeTemplateCommonMutation, personalRefetch, selectedTemplateName, tableName, ] ); const handleSubmit = useCallback( async (values: FormValues) => { await handleActivateTemplate(values)(); }, [handleActivateTemplate] ); const handleRenameTemplate = useCallback( async (newTemplateName: string | undefined, form: FormApi) => { if ( !newTemplateName || newTemplateName === "" || selectedTemplateName == null ) { return; } const res = await renameTemplateMutation({ variables: { table: tableName, templateName: selectedTemplateName, newTemplateName, isCommon: isCommonSelected, }, }); if (!res.errors) { await refetch(); setSelectedTemplateName(newTemplateName); handleCancelNameEdit(form)(); form.change( isCommonSelected ? "commonTemplate" : "personalTemplate", newTemplateName ); } }, [ renameTemplateMutation, tableName, selectedTemplateName, isCommonSelected, refetch, handleCancelNameEdit, ] ); const handleCopyTemplate = useCallback( (values: FormValues, form: FormApi) => async () => { const newTemplateName = `${selectedTemplateName} - копия`; const res = await handleSaveChanges(values, newTemplateName); if (res) { await refetch(); openNotification("open", { message: "Настройка шаблона", description: `Шаблон "${selectedTemplateName}" скопирован`, }); setMode(null); setSelectedTemplateName(newTemplateName); if (isCommonSelected) { form.change("commonTemplate", newTemplateName); } else { form.change("personalTemplate", newTemplateName); } } }, [handleSaveChanges, selectedTemplateName, refetch, isCommonSelected] ); const handleCopyCommonToPersonal = useCallback( (values: FormValues, form: FormApi) => async () => { const newTemplateName = `${selectedTemplateName} - личный`; if (personalTemplates.some((t) => t.name === newTemplateName)) { openNotification("warning", { message: "Копирование шаблона", description: `Шаблон с именем "${newTemplateName}" уже существует`, duration: 12, }); } else { const res = await handleSaveChanges(values, newTemplateName, false); if (res) { await personalRefetch(); openNotification("open", { message: "Настройка шаблона", description: `Шаблон "${selectedTemplateName}" скопирован в личные`, }); setIsCommonSelected(false); setMode(null); form.change("commonTemplate", undefined); form.change("personalTemplate", newTemplateName); } } }, [ handleSaveChanges, personalRefetch, personalTemplates, selectedTemplateName, ] ); const handleCreate = useCallback( (values: FormValues, form: FormApi) => async () => { if (mode === "create") { handleAddTemplate(values, form); } if (mode === "rename") { await handleRenameTemplate(values.templateName, form); } }, [handleAddTemplate, handleRenameTemplate, mode] ); const handleTemplateNameKeyDown = useCallback( (values: FormValues, form: FormApi) => (e: React.KeyboardEvent) => { if (e.key === "Enter") { e.preventDefault(); handleCreate(values, form)(); } }, [handleCreate] ); const handleEditNameClick = useCallback( (form: FormApi) => () => { setMode("rename"); form.change("templateName", selectedTemplateName); }, [selectedTemplateName] ); useEffect(() => { setColor( activeTemplate ? Object.fromEntries( activeTemplate.columns.map((a) => [a.name, a.color as COLOR_TYPES]) ) : {} ); }, [activeTemplate]); const initialValues = useMemo( (): Partial => ({ commonTemplate: activeTemplate && selectedTemplate?.isCommon ? activeTemplate.name : undefined, personalTemplate: activeTemplate && !selectedTemplate?.isCommon ? activeTemplate.name : undefined, msfoAction: columnNamesActions ? { userNames: showUserNames, systemNames: showSystemNames, } : undefined, techColumns: flatMapColumns(initialColumns).some( ({ techColumn, dataIndex }) => (techColumn || SOURCE_TECH_LIKE_COLUMNS.includes( dataIndex?.toString().toLowerCase() ?? "" )) && activeTemplate?.columns.find((s) => s.name === dataIndex && !s.hidden) ), businessColumns: flatMapColumns(initialColumns).some( ({ techColumn, dataIndex }) => !techColumn && !SOURCE_TECH_LIKE_COLUMNS.includes( dataIndex?.toString().toLowerCase() ?? "" ) && activeTemplate?.columns.find((s) => s.name === dataIndex && !s.hidden) ), sumDepth: activeTemplate?.summationDepth ?? SummationDepth.Million, }), [ activeTemplate, selectedTemplate?.isCommon, columnNamesActions, showUserNames, showSystemNames, initialColumns, ] ); const handleMoveToUsedList = useCallback( (fromIndex: number, toIndex?: number) => { const elem = availableColumnIndices[fromIndex]; setAvailableColumnIndices((prev) => prev.filter((_, i) => i !== fromIndex) ); if (toIndex == null) { setUsedColumnIndices((prev) => [...prev, elem]); } else { setUsedColumnIndices((prev) => prev.slice(0, toIndex).concat(elem, prev.slice(toIndex)) ); } }, [availableColumnIndices] ); const handleMoveToAvailableList = useCallback( (fromIndex: number, toIndex?: number) => { const elem = usedColumnIndices[fromIndex]; const dataIndex = columns[elem].dataIndex; setColor((prev) => ({ ...prev, [dataIndex as any]: COLOR_TYPES.TRANSPARENT, })); setUsedColumnIndices((prev) => prev.filter((_, i) => i !== fromIndex)); if (toIndex == null) { setAvailableColumnIndices((prev) => [...prev, elem]); } else { setAvailableColumnIndices((prev) => prev.slice(0, toIndex).concat(elem, prev.slice(toIndex)) ); } }, [columns, usedColumnIndices] ); const handleMoveByDragging = (from: DragItem, to: DragItem) => { if (from.source.list === to.source.list) { if (from.source.list === "used") { setUsedColumnIndices((prev) => move(from.source.index, to.source.index, prev) ); } else { setAvailableColumnIndices((prev) => move(from.source.index, to.source.index, prev) ); } } else if (from.source.list === "used") { const item = usedColumnIndices[from.source.index]; setUsedColumnIndices((z) => { const res = [...z]; res.splice(from.source.index, 1); return res; }); setAvailableColumnIndices((prev) => prev.slice(0, to.source.index).concat(item, prev.slice(to.source.index)) ); const dataIndex = columns[item].dataIndex; setColor((prev) => ({ ...prev, [dataIndex as any]: COLOR_TYPES.TRANSPARENT, })); } else { const item = availableColumnIndices[from.source.index]; setAvailableColumnIndices((z) => { const res = [...z]; res.splice(from.source.index, 1); return res; }); setUsedColumnIndices((prev) => prev.slice(0, to.source.index).concat(item, prev.slice(to.source.index)) ); } }; const handleRemoveAll = useCallback(() => { setUsedColumnIndices([]); setAvailableColumnIndices((v) => [...v, ...usedColumnIndices]); }, [usedColumnIndices]); const handleAddAll = useCallback(() => { setUsedColumnIndices((v) => [...v, ...availableColumnIndices]); setAvailableColumnIndices([]); }, [availableColumnIndices]); const templateNameValidator = useMemo( () => composeFieldValidators( required("Название не может быть пустым"), (newName: any) => { if (newName.length > 100) { return "Название не может быть длиной более 100 символов"; } if (allTemplates.some(({ name }) => name === newName)) { return `Шаблон с именем ${newName} уже существует`; } return undefined; } ), [allTemplates] ); const usedRealColumns = usedColumnIndices .map((c, index) => [columns[c], index, c] as const) .filter( (c) => search1 == null || ( (useFilterTitleInsteadOfTitle ? c[0].filterTitle : c[0].title) as string ) .toLowerCase() .includes(search1.toLowerCase()) ); const availableRealColumns = availableColumnIndices .map((c, index) => [columns[c], index] as const) .filter( (c) => search2 == null || ( (useFilterTitleInsteadOfTitle ? c[0].filterTitle : c[0].title) as string ) .toLowerCase() .includes(search2.toLowerCase()) ); const hasError = commonError ?? personalError ?? saveError ?? removeError ?? activateError ?? renameError ?? editSumDepthError ?? makeCommonError; const loading = personalLoading || commonLoading; if ((!personalData || !commonData) && loading) { return (
); } return ( <> {hasError && ( )} keepDirtyOnReinitialize initialValues={initialValues} mutators={{ updateValues: ( [templateName, isCommon]: [string, boolean], state, utils ) => { const template = isCommon ? commonTemplates.find((t) => t.name === templateName) : personalTemplates.find((t) => t.name === templateName); if (template) { setColumns((prev) => prev .slice() .sort( (a, b) => template.columns.findIndex( (c) => c.name === a.dataIndex ) - template.columns.findIndex((c) => c.name === b.dataIndex) ) ); setColor( Object.fromEntries( template.columns.map((a) => [a.name, a.color as COLOR_TYPES]) ) ); if (isCommon) { utils.changeValue(state, "personalTemplate", () => undefined); } else { utils.changeValue(state, "commonTemplate", () => undefined); } utils.changeValue( state, "sumDepth", () => template.summationDepth ?? SummationDepth.Million ); if (showTechBusinessColumnsSplit) { const isTechEnabled = columns.some( ({ techColumn, dataIndex }) => (techColumn || SOURCE_TECH_LIKE_COLUMNS.includes( dataIndex?.toString().toLowerCase() ?? "" )) && template.columns.find( (c) => c.name === dataIndex && !c.hidden ) ); const isBusinessEnabled = columns.some( ({ techColumn, dataIndex }) => !techColumn && !SOURCE_TECH_LIKE_COLUMNS.includes( dataIndex?.toString().toLowerCase() ?? "" ) && template.columns.find( (c) => c.name === dataIndex && !c.hidden ) ); utils.changeValue(state, "techColumns", () => isTechEnabled); utils.changeValue( state, "businessColumns", () => isBusinessEnabled ); } } }, }} onSubmit={handleSubmit} > {({ handleSubmit, values, form, hasValidationErrors }) => (
{mode === "create" || mode === "rename" ? ( <> ) : ( <> {mode === "edit" ? ( ) : ( )} )} {hasCommonTemplatesAccess && ( )}

Общие

{mode === "rename" && isCommonSelected ? ( ) : (
{({ input, meta }) => ( a} items={personalTemplatesOptions} meta={meta} placeholder="Выберите шаблон" /> )} )}
{(value: string | undefined) => { if ( value && !(isCommonSelected && selectedTemplateName === value) ) { setSelectedTemplateName(value); form.mutators.updateValues(value, true); setIsCommonSelected(true); } }} {(value: string | undefined) => { if ( !value || (!isCommonSelected && selectedTemplateName === value) ) { return; } setSelectedTemplateName(value); form.mutators.updateValues(value, false); setIsCommonSelected(false); }}
{enableSumDepth && allTemplates.length > 0 && selectedTemplateName !== "" && (

Настройка разрядности сумм

{({ input }) => ( руб. )} {({ input }) => ( тыс. )} {({ input }) => ( млн. )}
{(e: SummationDepth) => handleEditSumDepth(e)}
)} {columnNamesActions && (

Заголовки для экспорта данных

{columnNamesActions.map((a) => ( (
{a.text}
)} type="checkbox" /> {(e) => { if (e != null) { a.onChange(e); } }}
))} {!values.msfoAction?.userNames && !values.msfoAction?.systemNames && (
Выберите хотя бы один вариант
)}
)} {showTechBusinessColumnsSplit && (

Настройка отображения

{({ input }) => (
Бизнес поля
)}
{({ input }) => (
Технические поля
)}
{(v) => { toggleTechColumns(v); }} {(v) => { toggleBusinessColumns(v); }}
)}
{/*
{showDefault && ( )}
*/} {allTemplates.length > 0 && selectedTemplateName !== "" && ( <>

Настройка отображения данных

Таблица

{mode === "edit" ? (
setSearch1( (v?.search?.trim().length ?? 0) > 0 ? v?.search ?? null : null ) } withSubmit={false} />
) : null}
{mode === "edit" ? ( ) : null} {usedRealColumns.map(([c, idx]) => ( ))}

Поле выбора данных

{mode === "edit" ? (
setSearch2( (v?.search?.trim().length ?? 0) > 0 ? v?.search ?? null : null ) } withSubmit={false} />
) : null}
{mode === "edit" ? ( ) : null} {availableRealColumns.map(([c, idx]) => ( ))}
)}
{showDeleteConfirmation && ( setShowDeleteConfirmation(false)} onComplete={handleRemoveTemplate(form)} title="Удаление" /> )} )} ); }; export default memo(TemplatesForm); type ToggleInputProps = { column: ExtendedColumnType; idx: number; onMove: (from: DragItem, to: DragItem) => void; onMoveToOtherList: (index: number, toIndex?: number) => void; useFilterTitleInsteadOfTitle: boolean | undefined; showTechBusinessColumnsSplitit: boolean | undefined; disabled: boolean; disableOrderChanges: boolean; list: ListType; setColor: React.Dispatch< React.SetStateAction<{ [key: string]: COLOR_TYPES }> >; color: { [key: string]: COLOR_TYPES }; }; const ToggleInput: FC = ({ column, idx, onMove, onMoveToOtherList, useFilterTitleInsteadOfTitle: isMSFO, showTechBusinessColumnsSplitit: hasSplit, disabled, disableOrderChanges, setColor, color, list, }) => { const ref = useRef(null); const [{ handlerId }, drop] = useDrop({ accept: `toggle`, collect(monitor) { return { handlerId: monitor.getHandlerId(), }; }, hover(item: DragItem, monitor: DropTargetMonitor) { if (!ref.current) { return; } if (disableOrderChanges) { return; } const dragIndex = item.source.index; const hoverIndex = idx; // Don't replace items with themselves if ( item.source.list === list && item.column === column && item.source.index === hoverIndex ) { return; } if (item.source.list !== list) { onMove(item, { column: item.column, source: { index: hoverIndex, list }, }); // Note: we're mutating the monitor item here! // Generally it's better to avoid mutations, // but it's good here for the sake of performance // to avoid expensive index searches. item.source.index = hoverIndex; item.source.list = list; return; } // Determine rectangle on screen const hoverBoundingRect = ref.current.getBoundingClientRect(); // Get vertical middle const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; // Determine mouse position const clientOffset = monitor.getClientOffset(); // Get pixels to the top const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top; // Only perform the move when the mouse has crossed half of the items height // When dragging downwards, only move when the cursor is below 50% // When dragging upwards, only move when the cursor is above 50% // Dragging downwards if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { return; } // Dragging upwards if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { return; } // Time to actually perform the action onMove(item, { column: item.column, source: { index: hoverIndex, list }, }); // Note: we're mutating the monitor item here! // Generally it's better to avoid mutations, // but it's good here for the sake of performance // to avoid expensive index searches. item.source.index = hoverIndex; item.source.list = list; }, }); const [{ isDragging }, drag] = useDrag({ type: `toggle`, item: (): DragItem => ({ column, source: { list, index: idx } }), collect(monitor) { return { isDragging: monitor.isDragging(), }; }, }); drag(drop(ref)); return (
{idx + 1}
{list === "used" ? ( { setColor((prev) => ({ ...prev, [column.dataIndex as any]: newValue, })); }, value: color[column.dataIndex as any], onBlur: () => {}, onFocus: () => {}, }} meta={{}} options={colorOptions} variant="vertical" /> ) : null}
{isMSFO ? column.filterTitle : column.title}
{column.translation && ( {column.translation} )} {hasSplit && ( {column.techColumn ? "Техническое" : "Бизнес"} )}
{list === "used" ? ( onMoveToOtherList(idx)} /> ) : ( onMoveToOtherList(idx)} /> )}
); }; type TemplateNameFieldProps = { onKeyDown: (e: React.KeyboardEvent) => void; validate: FieldValidator; }; const TemplateNameField = ({ onKeyDown, validate }: TemplateNameFieldProps) => ( <> {({ meta }) => meta.invalid && meta.touched && meta.modified ? (
{meta.error}
) : null }
);