diff --git a/rsconcept/frontend/src/components/Icons.tsx b/rsconcept/frontend/src/components/Icons.tsx index eb41fa14..7a247f00 100644 --- a/rsconcept/frontend/src/components/Icons.tsx +++ b/rsconcept/frontend/src/components/Icons.tsx @@ -83,7 +83,6 @@ export { LuNewspaper as IconDefinition } from 'react-icons/lu'; export { LuDna as IconTerminology } from 'react-icons/lu'; export { FaRegHandshake as IconConvention } from 'react-icons/fa6'; export { LiaCloneSolid as IconChild } from 'react-icons/lia'; -export { RiParentLine as IconParent } from 'react-icons/ri'; export { TbTopologyRing as IconConsolidation } from 'react-icons/tb'; export { BiSpa as IconPredecessor } from 'react-icons/bi'; export { LuArchive as IconArchive } from 'react-icons/lu'; diff --git a/rsconcept/frontend/src/components/select/PickSubstitutions.tsx b/rsconcept/frontend/src/components/select/PickSubstitutions.tsx index f802febe..627e81aa 100644 --- a/rsconcept/frontend/src/components/select/PickSubstitutions.tsx +++ b/rsconcept/frontend/src/components/select/PickSubstitutions.tsx @@ -5,7 +5,7 @@ import { toast } from 'react-toastify'; import BadgeConstituenta from '@/components/info/BadgeConstituenta'; import SelectConstituenta from '@/components/select/SelectConstituenta'; -import DataTable, { createColumnHelper } from '@/components/ui/DataTable'; +import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable'; import MiniButton from '@/components/ui/MiniButton'; import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { ILibraryItem } from '@/models/library'; @@ -13,13 +13,14 @@ import { ICstSubstitute, IMultiSubstitution } from '@/models/oss'; import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform'; import { errors } from '@/utils/labels'; -import { IconPageLeft, IconPageRight, IconRemove, IconReplace } from '../Icons'; +import { IconAccept, IconPageLeft, IconPageRight, IconRemove, IconReplace } from '../Icons'; import NoData from '../ui/NoData'; import SelectLibraryItem from './SelectLibraryItem'; interface PickSubstitutionsProps { substitutions: ICstSubstitute[]; setSubstitutions: React.Dispatch>; + suggestions?: ICstSubstitute[]; prefixID: string; rows?: number; @@ -34,6 +35,7 @@ const columnHelper = createColumnHelper(); function PickSubstitutions({ substitutions, setSubstitutions, + suggestions, prefixID, rows, schemas, @@ -55,6 +57,15 @@ function PickSubstitutions({ const [deleteRight, setDeleteRight] = useState(true); const toggleDelete = () => setDeleteRight(prev => !prev); + const [ignores, setIgnores] = useState([]); + const filteredSuggestions = useMemo( + () => + suggestions?.filter( + item => !ignores.find(ignore => ignore.original === item.original && ignore.substitution === item.substitution) + ) ?? [], + [ignores, suggestions] + ); + const getSchemaByCst = useCallback( (id: ConstituentaID): IRSForm | undefined => { for (const schema of schemas) { @@ -82,14 +93,23 @@ function PickSubstitutions({ ); const substitutionData: IMultiSubstitution[] = useMemo( - () => - substitutions.map(item => ({ + () => [ + ...substitutions.map(item => ({ original_source: getSchemaByCst(item.original)!, original: getConstituenta(item.original)!, substitution: getConstituenta(item.substitution)!, - substitution_source: getSchemaByCst(item.substitution)! + substitution_source: getSchemaByCst(item.substitution)!, + is_suggestion: false })), - [getConstituenta, getSchemaByCst, substitutions] + ...filteredSuggestions.map(item => ({ + original_source: getSchemaByCst(item.original)!, + original: getConstituenta(item.original)!, + substitution: getConstituenta(item.substitution)!, + substitution_source: getSchemaByCst(item.substitution)!, + is_suggestion: true + })) + ], + [getConstituenta, getSchemaByCst, substitutions, filteredSuggestions] ); function addSubstitution() { @@ -121,19 +141,34 @@ function PickSubstitutions({ setRightCst(undefined); } - const handleDeleteRow = useCallback( - (row: number) => { + const handleDeclineSuggestion = useCallback( + (item: IMultiSubstitution) => { + setIgnores(prev => [...prev, { original: item.original.id, substitution: item.substitution.id }]); + }, + [setIgnores] + ); + + const handleAcceptSuggestion = useCallback( + (item: IMultiSubstitution) => { + setSubstitutions(prev => [...prev, { original: item.original.id, substitution: item.substitution.id }]); + }, + [setSubstitutions] + ); + + const handleDeleteSubstitution = useCallback( + (target: IMultiSubstitution) => { + handleDeclineSuggestion(target); setSubstitutions(prev => { const newItems: ICstSubstitute[] = []; - prev.forEach((item, index) => { - if (index !== row) { + prev.forEach(item => { + if (item.original !== target.original.id || item.substitution !== target.substitution.id) { newItems.push(item); } }); return newItems; }); }, - [setSubstitutions] + [setSubstitutions, handleDeclineSuggestion] ); const columns = useMemo( @@ -169,19 +204,47 @@ function PickSubstitutions({ }), columnHelper.display({ id: 'actions', - cell: props => ( -
- } - onClick={() => handleDeleteRow(props.row.index)} - /> -
- ) + cell: props => + props.row.original.is_suggestion ? ( +
+ } + onClick={() => handleAcceptSuggestion(props.row.original)} + /> + } + onClick={() => handleDeclineSuggestion(props.row.original)} + /> +
+ ) : ( +
+ } + onClick={() => handleDeleteSubstitution(props.row.original)} + /> +
+ ) }) ], - [handleDeleteRow, colors, prefixID] + [handleDeleteSubstitution, handleDeclineSuggestion, handleAcceptSuggestion, colors, prefixID] + ); + + const conditionalRowStyles = useMemo( + (): IConditionalStyle[] => [ + { + when: (item: IMultiSubstitution) => item.is_suggestion, + style: { + backgroundColor: colors.bgOrange50 + } + } + ], + [colors] ); return ( @@ -265,6 +328,7 @@ function PickSubstitutions({

Добавьте отождествление

} + conditionalRowStyles={conditionalRowStyles} /> ); diff --git a/rsconcept/frontend/src/dialogs/DlgEditOperation/DlgEditOperation.tsx b/rsconcept/frontend/src/dialogs/DlgEditOperation/DlgEditOperation.tsx index 093e0f2b..e20adc3f 100644 --- a/rsconcept/frontend/src/dialogs/DlgEditOperation/DlgEditOperation.tsx +++ b/rsconcept/frontend/src/dialogs/DlgEditOperation/DlgEditOperation.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { TabList, TabPanel, Tabs } from 'react-tabs'; import BadgeHelp from '@/components/info/BadgeHelp'; @@ -54,7 +54,10 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio () => inputOperations.map(operation => operation.result).filter(id => id !== null), [inputOperations] ); + const [substitutions, setSubstitutions] = useState(target.substitutions); + const [suggestions, setSuggestions] = useState([]); + const cache = useRSFormCache(); const schemas = useMemo( () => schemasIDs.map(id => cache.getSchema(id)).filter(item => item !== undefined), @@ -63,11 +66,11 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio const canSubmit = useMemo(() => alias !== '', [alias]); - useEffect(() => { + useLayoutEffect(() => { cache.preload(schemasIDs); }, [schemasIDs]); - useEffect(() => { + useLayoutEffect(() => { if (cache.loading || schemas.length !== schemasIDs.length) { return; } @@ -86,13 +89,14 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio ); }, [schemasIDs, schemas, cache.loading]); - useEffect(() => { + useLayoutEffect(() => { if (cache.loading || schemas.length !== schemasIDs.length) { return; } const validator = new SubstitutionValidator(schemas, substitutions); setIsCorrect(validator.validate()); setValidationText(validator.msg); + setSuggestions(validator.suggestions); }, [substitutions, cache.loading, schemas, schemasIDs.length]); const handleSubmit = useCallback(() => { @@ -151,10 +155,11 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio isCorrect={isCorrect} substitutions={substitutions} setSubstitutions={setSubstitutions} + suggestions={suggestions} /> ), - [cache.loading, cache.error, substitutions, schemas, validationText, isCorrect] + [cache.loading, cache.error, substitutions, suggestions, schemas, validationText, isCorrect] ); return ( diff --git a/rsconcept/frontend/src/dialogs/DlgEditOperation/TabSynthesis.tsx b/rsconcept/frontend/src/dialogs/DlgEditOperation/TabSynthesis.tsx index be822e37..0be98073 100644 --- a/rsconcept/frontend/src/dialogs/DlgEditOperation/TabSynthesis.tsx +++ b/rsconcept/frontend/src/dialogs/DlgEditOperation/TabSynthesis.tsx @@ -16,6 +16,7 @@ interface TabSynthesisProps { schemas: IRSForm[]; substitutions: ICstSubstitute[]; setSubstitutions: React.Dispatch>; + suggestions: ICstSubstitute[]; } function TabSynthesis({ @@ -25,7 +26,8 @@ function TabSynthesis({ validationText, isCorrect, substitutions, - setSubstitutions + setSubstitutions, + suggestions }: TabSynthesisProps) { const { colors } = useConceptOptions(); return ( @@ -36,6 +38,7 @@ function TabSynthesis({ rows={10} substitutions={substitutions} setSubstitutions={setSubstitutions} + suggestions={suggestions} />