diff --git a/rsconcept/frontend/src/components/SchemaPicker.tsx b/rsconcept/frontend/src/components/SchemaPicker.tsx new file mode 100644 index 00000000..eaed76c4 --- /dev/null +++ b/rsconcept/frontend/src/components/SchemaPicker.tsx @@ -0,0 +1,117 @@ +import { useLayoutEffect, useMemo, useState } from 'react'; +import { useIntl } from 'react-intl'; + +import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/DataTable'; +import SearchBar from '@/components/ui/SearchBar'; +import { useLibrary } from '@/context/LibraryContext'; +import { useConceptTheme } from '@/context/ThemeContext'; +import { ILibraryItem, LibraryItemID } from '@/models/library'; +import { ILibraryFilter } from '@/models/miscellaneous'; + +import FlexColumn from './ui/FlexColumn'; + +interface SchemaPickerProps { + id?: string; + initialFilter?: string; + rows?: number; + + value?: LibraryItemID; + onSelectValue: (newValue: LibraryItemID) => void; +} + +const columnHelper = createColumnHelper(); + +function SchemaPicker({ id, initialFilter = '', rows = 4, value, onSelectValue }: SchemaPickerProps) { + const intl = useIntl(); + const { colors } = useConceptTheme(); + + const library = useLibrary(); + const [filterText, setFilterText] = useState(initialFilter); + const [filter, setFilter] = useState({}); + const [items, setItems] = useState([]); + + useLayoutEffect(() => { + setFilter({ + query: filterText + }); + }, [filterText]); + + useLayoutEffect(() => { + setItems(library.applyFilter(filter)); + }, [library, filter, filter.query]); + + const columns = useMemo( + () => [ + columnHelper.accessor('alias', { + id: 'alias', + header: 'Шифр', + size: 150, + minSize: 80, + maxSize: 150 + }), + columnHelper.accessor('title', { + id: 'title', + header: 'Название', + size: 1200, + minSize: 200, + maxSize: 1200, + cell: props =>
{props.getValue()}
+ }), + columnHelper.accessor('time_update', { + id: 'time_update', + header: 'Дата', + cell: props => ( +
+ {new Date(props.getValue()).toLocaleString(intl.locale, { + year: '2-digit', + month: '2-digit', + day: '2-digit' + })} +
+ ) + }) + ], + [intl] + ); + + const conditionalRowStyles = useMemo( + (): IConditionalStyle[] => [ + { + when: (item: ILibraryItem) => item.id === value, + style: { backgroundColor: colors.bgSelected } + } + ], + [value, colors] + ); + + return ( +
+ setFilterText(newValue)} + /> + +

Список схем пуст

+

Измените параметры фильтра

+ + } + onRowClicked={rowData => onSelectValue(rowData.id)} + /> +
+ ); +} + +export default SchemaPicker; diff --git a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/ConstituentsTab.tsx b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/ConstituentsTab.tsx new file mode 100644 index 00000000..b1a125d1 --- /dev/null +++ b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/ConstituentsTab.tsx @@ -0,0 +1,18 @@ +'use client'; + +import { LibraryItemID } from '@/models/library'; +import { IRSForm } from '@/models/rsform'; + +interface ConstituentsTabProps { + schema?: IRSForm; + loading?: boolean; + selected: LibraryItemID[]; + setSelected: React.Dispatch; +} + +// { schema, loading, selected, setSelected }: ConstituentsTabProps +function ConstituentsTab(props: ConstituentsTabProps) { + return <>2 - {props.loading}; +} + +export default ConstituentsTab; diff --git a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx new file mode 100644 index 00000000..db5362c5 --- /dev/null +++ b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx @@ -0,0 +1,101 @@ +'use client'; + +import clsx from 'clsx'; +import { useMemo, useState } from 'react'; +import { TabList, TabPanel, Tabs } from 'react-tabs'; + +import Modal, { ModalProps } from '@/components/ui/Modal'; +import TabLabel from '@/components/ui/TabLabel'; +import useRSFormDetails from '@/hooks/useRSFormDetails'; +import { LibraryItemID } from '@/models/library'; +import { ICstSubstituteData, IRSForm, IRSFormInlineData } from '@/models/rsform'; + +import ConstituentsTab from './ConstituentsTab'; +import SchemaTab from './SchemaTab'; +import SubstitutionsTab from './SubstitutionsTab'; + +interface DlgInlineSynthesisProps extends Pick { + receiver: IRSForm; + onInlineSynthesis: (data: IRSFormInlineData) => void; +} + +export enum TabID { + SCHEMA = 0, + SELECTIONS = 1, + SUBSTITUTIONS = 2 +} + +function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInlineSynthesisProps) { + const [activeTab, setActiveTab] = useState(TabID.SCHEMA); + + const [donorID, setDonorID] = useState(undefined); + const [selected, setSelected] = useState([]); + const [substitutions, setSubstitutions] = useState([]); + + const source = useRSFormDetails({ target: donorID ? String(donorID) : undefined }); + + const validated = useMemo(() => false, []); + + function handleSubmit() { + if (!source.schema) { + return; + } + const data: IRSFormInlineData = { + source: source.schema?.id, + receiver: receiver.id, + items: selected, + substitutions: substitutions + }; + onInlineSynthesis(data); + } + + return ( + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default DlgInlineSynthesis; diff --git a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/SchemaTab.tsx b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/SchemaTab.tsx new file mode 100644 index 00000000..be9524f4 --- /dev/null +++ b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/SchemaTab.tsx @@ -0,0 +1,36 @@ +'use client'; + +import { useMemo } from 'react'; + +import SchemaPicker from '@/components/SchemaPicker'; +import FlexColumn from '@/components/ui/FlexColumn'; +import TextInput from '@/components/ui/TextInput'; +import { useLibrary } from '@/context/LibraryContext'; +import { LibraryItemID } from '@/models/library'; + +interface SchemaTabProps { + selected?: LibraryItemID; + setSelected: React.Dispatch; +} + +function SchemaTab({ selected, setSelected }: SchemaTabProps) { + const library = useLibrary(); + const selectedInfo = useMemo(() => library.items.find(item => item.id === selected), [selected, library.items]); + + return ( + + + + + ); +} + +export default SchemaTab; diff --git a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/SubstitutionsTab.tsx b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/SubstitutionsTab.tsx new file mode 100644 index 00000000..53805fe0 --- /dev/null +++ b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/SubstitutionsTab.tsx @@ -0,0 +1,18 @@ +'use client'; + +import { ICstSubstituteData, IRSForm } from '@/models/rsform'; + +interface SubstitutionsTabProps { + receiver?: IRSForm; + source?: IRSForm; + loading?: boolean; + substitutions: ICstSubstituteData[]; + setSubstitutions: React.Dispatch; +} + +// { source, receiver, loading, substitutions, setSubstitutions }: SubstitutionsTabProps +function SubstitutionsTab(props: SubstitutionsTabProps) { + return <>3 - {props.loading}; +} + +export default SubstitutionsTab; diff --git a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/index.tsx b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/index.tsx new file mode 100644 index 00000000..5f50eadd --- /dev/null +++ b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/index.tsx @@ -0,0 +1 @@ +export { default } from './DlgInlineSynthesis'; diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx index 490d5c10..c317c8f5 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx @@ -21,14 +21,15 @@ import DlgCreateVersion from '@/dialogs/DlgCreateVersion'; import DlgDeleteCst from '@/dialogs/DlgDeleteCst'; import DlgEditVersions from '@/dialogs/DlgEditVersions'; import DlgEditWordForms from '@/dialogs/DlgEditWordForms'; +import DlgInlineSynthesis from '@/dialogs/DlgInlineSynthesis'; import DlgRenameCst from '@/dialogs/DlgRenameCst'; import DlgSubstituteCst from '@/dialogs/DlgSubstituteCst'; import DlgUploadRSForm from '@/dialogs/DlgUploadRSForm'; import { IVersionData } from '@/models/library'; import { UserAccessMode } from '@/models/miscellaneous'; import { + ConstituentaID, CstType, - EntityID, IConstituenta, IConstituentaMeta, ICstCreateData, @@ -69,6 +70,7 @@ interface IRSEditContext { download: () => void; reindex: () => void; produceStructure: () => void; + inlineSynthesis: () => void; substitute: () => void; createVersion: () => void; @@ -85,13 +87,13 @@ export const useRSEdit = () => { }; interface RSEditStateProps { - selected: EntityID[]; + selected: ConstituentaID[]; isModified: boolean; - setSelected: React.Dispatch>; + setSelected: React.Dispatch>; activeCst?: IConstituenta; onCreateCst?: (newCst: IConstituentaMeta) => void; - onDeleteCst?: (newActive?: EntityID) => void; + onDeleteCst?: (newActive?: ConstituentaID) => void; children: React.ReactNode; } @@ -127,6 +129,7 @@ export const RSEditState = ({ const [showSubstitute, setShowSubstitute] = useState(false); const [showCreateVersion, setShowCreateVersion] = useState(false); const [showEditVersions, setShowEditVersions] = useState(false); + const [showInlineSynthesis, setShowInlineSynthesis] = useState(false); const [createInitialData, setCreateInitialData] = useState(); const [showCreateCst, setShowCreateCst] = useState(false); @@ -134,7 +137,7 @@ export const RSEditState = ({ const [renameInitialData, setRenameInitialData] = useState(); const [showRenameCst, setShowRenameCst] = useState(false); - const [insertCstID, setInsertCstID] = useState(undefined); + const [insertCstID, setInsertCstID] = useState(undefined); const [showTemplates, setShowTemplates] = useState(false); useLayoutEffect( @@ -192,7 +195,7 @@ export const RSEditState = ({ ); const handleDeleteCst = useCallback( - (deleted: EntityID[]) => { + (deleted: ConstituentaID[]) => { if (!model.schema) { return; } @@ -479,6 +482,7 @@ export const RSEditState = ({ share, toggleSubscribe, reindex, + inlineSynthesis: () => setShowInlineSynthesis(true), produceStructure, substitute, @@ -549,6 +553,13 @@ export const RSEditState = ({ onUpdate={handleUpdateVersion} /> ) : null} + {showInlineSynthesis ? ( + setShowInlineSynthesis(false)} + onInlineSynthesis={() => toast('Testing')} + /> + ) : null} ) : null} @@ -586,10 +597,10 @@ function ProcessError({ } function getNextActiveOnDelete( - activeID: number | undefined, + activeID: ConstituentaID | undefined, items: IConstituenta[], - deleted: number[] -): number | undefined { + deleted: ConstituentaID[] +): ConstituentaID | undefined { if (items.length === deleted.length) { return undefined; } diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSTabsMenu.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSTabsMenu.tsx index d7ee6493..b7964090 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSTabsMenu.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSTabsMenu.tsx @@ -1,7 +1,6 @@ 'use client'; import { - BiAnalyse, BiDiamond, BiDownload, BiDuplicate, @@ -13,7 +12,16 @@ import { BiUpload } from 'react-icons/bi'; import { FiEdit } from 'react-icons/fi'; -import { LuAlertTriangle, LuArchive, LuCrown, LuGlasses, LuNetwork, LuReplace } from 'react-icons/lu'; +import { + LuAlertTriangle, + LuArchive, + LuBookCopy, + LuCrown, + LuGlasses, + LuNetwork, + LuReplace, + LuWand2 +} from 'react-icons/lu'; import { VscLibrary } from 'react-icons/vsc'; import Button from '@/components/ui/Button'; @@ -95,6 +103,11 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) { controller.produceStructure(); } + function handleInlineSynthesis() { + editMenu.hide(); + controller.inlineSynthesis(); + } + function handleChangeMode(newMode: UserAccessMode) { accessMenu.hide(); setMode(newMode); @@ -201,12 +214,19 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) { icon={} onClick={handleTemplates} /> + } + onClick={handleInlineSynthesis} + /> } + icon={} onClick={handleReindex} /> } + icon={} onClick={handleSubstituteCst} />