import { useCallback, useMemo, useState } from 'react'; import type { RowsChangeData } from 'react-data-grid'; import styled from '@emotion/styled'; import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton'; import { Heading } from '@/spreadsheet-import/components/Heading'; import { Table } from '@/spreadsheet-import/components/Table'; import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal'; import type { Data } from '@/spreadsheet-import/types'; import { addErrorsAndRunHooks } from '@/spreadsheet-import/utils/dataMutations'; import { Button, ButtonVariant } from '@/ui/button/components/Button'; import { useDialog } from '@/ui/dialog/hooks/useDialog'; import { IconTrash } from '@/ui/icon'; import { Toggle } from '@/ui/input/toggle/components/Toggle'; import { Modal } from '@/ui/modal/components/Modal'; import { generateColumns } from './components/columns'; import type { Meta } from './types'; const StyledToolbar = styled.div` display: flex; flex-direction: row; justify-content: space-between; margin-bottom: ${({ theme }) => theme.spacing(4)}; margin-top: ${({ theme }) => theme.spacing(8)}; `; const StyledErrorToggle = styled.div` align-items: center; display: flex; flex-direction: row; `; const StyledErrorToggleDescription = styled.span` color: ${({ theme }) => theme.font.color.primary}; font-size: ${({ theme }) => theme.font.size.sm}; font-weight: ${({ theme }) => theme.font.weight.regular}; margin-left: ${({ theme }) => theme.spacing(2)}; `; const StyledScrollContainer = styled.div` display: flex; flex-direction: column; flex-grow: 1; height: 0px; width: 100%; `; const StyledNoRowsContainer = styled.div` display: flex; grid-column: 1/-1; justify-content: center; margin-top: ${({ theme }) => theme.spacing(8)}; `; type Props = { initialData: Data[]; file: File; onSubmitStart?: () => void; }; export const ValidationStep = ({ initialData, file, onSubmitStart, }: Props) => { const { enqueueDialog } = useDialog(); const { fields, onClose, onSubmit, rowHook, tableHook } = useSpreadsheetImportInternal(); const [data, setData] = useState<(Data & Meta)[]>( useMemo( () => addErrorsAndRunHooks(initialData, fields, rowHook, tableHook), // eslint-disable-next-line react-hooks/exhaustive-deps [], ), ); const [selectedRows, setSelectedRows] = useState< ReadonlySet >(new Set()); const [filterByErrors, setFilterByErrors] = useState(false); const updateData = useCallback( (rows: typeof data) => { setData(addErrorsAndRunHooks(rows, fields, rowHook, tableHook)); }, [setData, rowHook, tableHook, fields], ); const deleteSelectedRows = () => { if (selectedRows.size) { const newData = data.filter((value) => !selectedRows.has(value.__index)); updateData(newData); setSelectedRows(new Set()); } }; const updateRow = useCallback( ( rows: typeof data, changedData?: RowsChangeData<(typeof data)[number]>, ) => { const changes = changedData?.indexes.reduce((acc, index) => { // when data is filtered val !== actual index in data const realIndex = data.findIndex( (value) => value.__index === rows[index].__index, ); acc[realIndex] = rows[index]; return acc; }, {} as Record); const newData = Object.assign([], data, changes); updateData(newData); }, [data, updateData], ); const columns = useMemo(() => generateColumns(fields), [fields]); const tableData = useMemo(() => { if (filterByErrors) { return data.filter((value) => { if (value?.__errors) { return Object.values(value.__errors)?.filter( (err) => err.level === 'error', ).length; } return false; }); } return data; }, [data, filterByErrors]); const rowKeyGetter = useCallback((row: Data & Meta) => row.__index, []); const submitData = async () => { const calculatedData = data.reduce( (acc, value) => { const { __index, __errors, ...values } = value; if (__errors) { for (const key in __errors) { if (__errors[key].level === 'error') { acc.invalidData.push(values as unknown as Data); return acc; } } } acc.validData.push(values as unknown as Data); return acc; }, { validData: [] as Data[], invalidData: [] as Data[], all: data }, ); onSubmitStart?.(); await onSubmit(calculatedData, file); onClose(); }; const onContinue = () => { const invalidData = data.find((value) => { if (value?.__errors) { return !!Object.values(value.__errors)?.filter( (err) => err.level === 'error', ).length; } return false; }); if (!invalidData) { submitData(); } else { enqueueDialog({ title: 'Finish flow with errors', message: 'There are still some rows that contain errors. Rows with errors will be ignored when submitting.', buttons: [ { title: 'Cancel' }, { title: 'Submit', variant: ButtonVariant.Primary, onClick: submitData, }, ], }); } }; return ( <> setFilterByErrors(!filterByErrors)} /> Show only rows with errors