From 583083c1fd3322f7a04d56cad138af3af8d2f93d Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Fri, 26 Jul 2024 00:33:22 +0300 Subject: [PATCH] F: Improve OSS UI --- .../src/components/info/TooltipOperation.tsx | 33 +++++++++++++++++++ .../src/components/select/PickSchema.tsx | 12 +++++-- .../DlgCreateOperation/DlgCreateOperation.tsx | 3 +- .../DlgCreateOperation/TabInputOperation.tsx | 18 ++++++++-- rsconcept/frontend/src/models/OssLoader.ts | 28 +++++++++++++++- rsconcept/frontend/src/models/oss.ts | 12 +++++++ .../OssPage/EditorOssCard/EditorOssCard.tsx | 5 ++- .../pages/OssPage/EditorOssCard/OssStats.tsx | 28 ++++++++++++++++ .../OssPage/EditorOssGraph/InputNode.tsx | 15 ++++++--- .../OssPage/EditorOssGraph/OperationNode.tsx | 14 +++++--- .../pages/OssPage/EditorOssGraph/OssFlow.tsx | 18 +++++++--- .../src/pages/OssPage/OssEditContext.tsx | 8 +++++ .../EditorTermGraph/EditorTermGraph.tsx | 1 - rsconcept/frontend/src/utils/constants.ts | 1 + 14 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 rsconcept/frontend/src/components/info/TooltipOperation.tsx create mode 100644 rsconcept/frontend/src/pages/OssPage/EditorOssCard/OssStats.tsx diff --git a/rsconcept/frontend/src/components/info/TooltipOperation.tsx b/rsconcept/frontend/src/components/info/TooltipOperation.tsx new file mode 100644 index 00000000..59ebfdab --- /dev/null +++ b/rsconcept/frontend/src/components/info/TooltipOperation.tsx @@ -0,0 +1,33 @@ +import Tooltip from '@/components/ui/Tooltip'; +import { IOperation } from '@/models/oss'; +import { labelOperationType } from '@/utils/labels'; + +interface TooltipOperationProps { + data: IOperation; + anchor: string; +} + +function TooltipOperation({ data, anchor }: TooltipOperationProps) { + return ( + +

Операция {data.alias}

+

+ Тип: {labelOperationType(data.operation_type)} +

+ {data.title ? ( +

+ Название: + {data.title} +

+ ) : null} + {data.comment ? ( +

+ Комментарий: + {data.comment} +

+ ) : null} +
+ ); +} + +export default TooltipOperation; diff --git a/rsconcept/frontend/src/components/select/PickSchema.tsx b/rsconcept/frontend/src/components/select/PickSchema.tsx index 24b2e521..f1d267fb 100644 --- a/rsconcept/frontend/src/components/select/PickSchema.tsx +++ b/rsconcept/frontend/src/components/select/PickSchema.tsx @@ -16,12 +16,13 @@ interface PickSchemaProps { rows?: number; value?: LibraryItemID; + baseFilter?: (target: ILibraryItem) => boolean; onSelectValue: (newValue: LibraryItemID) => void; } const columnHelper = createColumnHelper(); -function PickSchema({ id, initialFilter = '', rows = 4, value, onSelectValue }: PickSchemaProps) { +function PickSchema({ id, initialFilter = '', rows = 4, value, onSelectValue, baseFilter }: PickSchemaProps) { const intl = useIntl(); const { colors } = useConceptOptions(); @@ -38,8 +39,13 @@ function PickSchema({ id, initialFilter = '', rows = 4, value, onSelectValue }: }, [filterText]); useLayoutEffect(() => { - setItems(library.applyFilter(filter)); - }, [library, filter, filter.query]); + const filtered = library.applyFilter(filter); + if (baseFilter) { + setItems(filtered.filter(baseFilter)); + } else { + setItems(filtered); + } + }, [library, filter, filter.query, baseFilter]); const columns = useMemo( () => [ diff --git a/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx b/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx index 6ccbfa19..0c889e95 100644 --- a/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx +++ b/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx @@ -77,6 +77,7 @@ function DlgCreateOperation({ hideWindow, oss, insertPosition, positions, onCrea () => ( ), - [alias, comment, title, attachedID, syncText] + [alias, comment, title, attachedID, syncText, oss] ); const synthesisPanel = useMemo( diff --git a/rsconcept/frontend/src/dialogs/DlgCreateOperation/TabInputOperation.tsx b/rsconcept/frontend/src/dialogs/DlgCreateOperation/TabInputOperation.tsx index 75ab2071..4a3dbfe3 100644 --- a/rsconcept/frontend/src/dialogs/DlgCreateOperation/TabInputOperation.tsx +++ b/rsconcept/frontend/src/dialogs/DlgCreateOperation/TabInputOperation.tsx @@ -1,3 +1,7 @@ +'use client'; + +import { useCallback } from 'react'; + import { IconReset } from '@/components/Icons'; import PickSchema from '@/components/select/PickSchema'; import Checkbox from '@/components/ui/Checkbox'; @@ -7,10 +11,12 @@ import MiniButton from '@/components/ui/MiniButton'; import TextArea from '@/components/ui/TextArea'; import TextInput from '@/components/ui/TextInput'; import AnimateFade from '@/components/wrap/AnimateFade'; -import { LibraryItemID } from '@/models/library'; +import { ILibraryItem, LibraryItemID } from '@/models/library'; +import { IOperationSchema } from '@/models/oss'; import { limits, patterns } from '@/utils/constants'; interface TabInputOperationProps { + oss: IOperationSchema; alias: string; setAlias: React.Dispatch>; title: string; @@ -24,6 +30,7 @@ interface TabInputOperationProps { } function TabInputOperation({ + oss, alias, setAlias, title, @@ -35,6 +42,8 @@ function TabInputOperation({ syncText, setSyncText }: TabInputOperationProps) { + const baseFilter = useCallback((item: ILibraryItem) => !oss.schemas.includes(item.id), [oss]); + return ( - + ); } diff --git a/rsconcept/frontend/src/models/OssLoader.ts b/rsconcept/frontend/src/models/OssLoader.ts index bf0f8ed2..f6107dde 100644 --- a/rsconcept/frontend/src/models/OssLoader.ts +++ b/rsconcept/frontend/src/models/OssLoader.ts @@ -3,7 +3,15 @@ */ import { Graph } from './Graph'; -import { IOperation, IOperationSchema, IOperationSchemaData, OperationID } from './oss'; +import { LibraryItemID } from './library'; +import { + IOperation, + IOperationSchema, + IOperationSchemaData, + IOperationSchemaStats, + OperationID, + OperationType +} from './oss'; /** * Loads data into an {@link IOperationSchema} based on {@link IOperationSchemaData}. @@ -13,6 +21,7 @@ export class OssLoader { private oss: IOperationSchemaData; private graph: Graph = new Graph(); private operationByID: Map = new Map(); + private schemas: LibraryItemID[] = []; constructor(input: IOperationSchemaData) { this.oss = input; @@ -22,9 +31,12 @@ export class OssLoader { const result = this.oss as IOperationSchema; this.prepareLookups(); this.createGraph(); + this.extractSchemas(); result.operationByID = this.operationByID; result.graph = this.graph; + result.schemas = this.schemas; + result.stats = this.calculateStats(); return result; } @@ -38,4 +50,18 @@ export class OssLoader { private createGraph() { this.oss.arguments.forEach(argument => this.graph.addEdge(argument.argument, argument.operation)); } + + private extractSchemas() { + this.schemas = this.oss.items.map(operation => operation.result as LibraryItemID).filter(item => item !== null); + } + + private calculateStats(): IOperationSchemaStats { + const items = this.oss.items; + return { + count_operations: items.length, + count_inputs: items.filter(item => item.operation_type === OperationType.INPUT).length, + count_synthesis: items.filter(item => item.operation_type === OperationType.SYNTHESIS).length, + count_schemas: this.schemas.length + }; + } } diff --git a/rsconcept/frontend/src/models/oss.ts b/rsconcept/frontend/src/models/oss.ts index b4dadb6c..9508a4e4 100644 --- a/rsconcept/frontend/src/models/oss.ts +++ b/rsconcept/frontend/src/models/oss.ts @@ -102,6 +102,16 @@ export interface ICstSubstituteEx extends ICstSubstitute { substitution_term: string; } +/** + * Represents {@link IOperationSchema} statistics. + */ +export interface IOperationSchemaStats { + count_operations: number; + count_inputs: number; + count_synthesis: number; + count_schemas: number; +} + /** * Represents backend data for {@link IOperationSchema}. */ @@ -116,6 +126,8 @@ export interface IOperationSchemaData extends ILibraryItemData { */ export interface IOperationSchema extends IOperationSchemaData { graph: Graph; + schemas: LibraryItemID[]; + stats: IOperationSchemaStats; operationByID: Map; } diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssCard/EditorOssCard.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssCard/EditorOssCard.tsx index 1df15a76..c4e0f01b 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssCard/EditorOssCard.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssCard/EditorOssCard.tsx @@ -12,6 +12,7 @@ import { globals } from '@/utils/constants'; import { useOssEdit } from '../OssEditContext'; import FormOSS from './FormOSS'; +import OssStats from './OssStats'; interface EditorOssCardProps { isModified: boolean; @@ -50,11 +51,13 @@ function EditorOssCard({ isModified, onDestroy, setIsModified }: EditorOssCardPr onDestroy={onDestroy} controller={controller} /> - + + + ); diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssCard/OssStats.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssCard/OssStats.tsx new file mode 100644 index 00000000..ab2ed4a9 --- /dev/null +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssCard/OssStats.tsx @@ -0,0 +1,28 @@ +import Divider from '@/components/ui/Divider'; +import LabeledValue from '@/components/ui/LabeledValue'; +import { IOperationSchemaStats } from '@/models/oss'; + +interface OssStatsProps { + stats?: IOperationSchemaStats; +} + +function OssStats({ stats }: OssStatsProps) { + if (!stats) { + return null; + } + return ( +
+ + + + + + + + + +
+ ); +} + +export default OssStats; diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/InputNode.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/InputNode.tsx index 4ed211b8..17f5ee4f 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/InputNode.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/InputNode.tsx @@ -1,9 +1,11 @@ import { Handle, Position } from 'reactflow'; import { IconRSForm } from '@/components/Icons'; +import TooltipOperation from '@/components/info/TooltipOperation'; import MiniButton from '@/components/ui/MiniButton.tsx'; import Overlay from '@/components/ui/Overlay'; -import { useOSS } from '@/context/OssContext'; +import { IOperation } from '@/models/oss'; +import { prefixes } from '@/utils/constants'; import { useOssEdit } from '../OssEditContext'; @@ -11,14 +13,14 @@ interface InputNodeProps { id: string; data: { label: string; + operation: IOperation; }; } function InputNode({ id, data }: InputNodeProps) { const controller = useOssEdit(); - const model = useOSS(); - const hasFile = !!model.schema?.operationByID.get(Number(id))?.result; + const hasFile = !!data.operation.result; const handleOpenSchema = () => { controller.openOperationSchema(Number(id)); @@ -39,7 +41,12 @@ function InputNode({ id, data }: InputNodeProps) { disabled={!hasFile} /> -
{data.label}
+
+ {data.label} + {controller.showTooltip ? ( + + ) : null} +
); } diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OperationNode.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OperationNode.tsx index a4ada7e6..72e33aa9 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OperationNode.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OperationNode.tsx @@ -1,23 +1,25 @@ import { Handle, Position } from 'reactflow'; import { IconRSForm } from '@/components/Icons'; +import TooltipOperation from '@/components/info/TooltipOperation'; import MiniButton from '@/components/ui/MiniButton.tsx'; import Overlay from '@/components/ui/Overlay'; -import { useOSS } from '@/context/OssContext'; +import { IOperation } from '@/models/oss'; +import { prefixes } from '@/utils/constants'; import { useOssEdit } from '../OssEditContext'; interface OperationNodeProps { id: string; data: { label: string; + operation: IOperation; }; } function OperationNode({ id, data }: OperationNodeProps) { const controller = useOssEdit(); - const model = useOSS(); - const hasFile = !!model.schema?.operationByID.get(Number(id))?.result; + const hasFile = !!data.operation.result; const handleOpenSchema = () => { controller.openOperationSchema(Number(id)); @@ -38,7 +40,11 @@ function OperationNode({ id, data }: OperationNodeProps) { disabled={!hasFile} /> -
{data.label}
+ +
+ {data.label} + +
diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx index 45364ec2..25be92cf 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx @@ -50,7 +50,6 @@ function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowPr const onSelectionChange = useCallback( ({ nodes }: { nodes: Node[] }) => { controller.setSelected(nodes.map(node => Number(node.id))); - console.log(nodes); }, [controller] ); @@ -67,7 +66,7 @@ function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowPr setNodes( model.schema.items.map(operation => ({ id: String(operation.id), - data: { label: operation.alias }, + data: { label: operation.alias, operation: operation }, position: { x: operation.position_x, y: operation.position_y }, type: operation.operation_type.toString() })) @@ -116,7 +115,6 @@ function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowPr const handleCreateOperation = useCallback(() => { const center = flow.project({ x: window.innerWidth / 2, y: window.innerHeight / 2 }); - console.log(center); controller.promptCreateOperation(center.x, center.y, getPositions()); }, [controller, getPositions, flow]); @@ -168,8 +166,17 @@ function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowPr }); }, [colors, nodes]); + const handleContextMenu = useCallback( + (event: React.MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + controller.setShowTooltip(prev => !prev); + // setShowContextMenu(true); + }, + [controller] + ); + function handleKeyDown(event: React.KeyboardEvent) { - // Hotkeys implementation if (controller.isProcessing) { return; } @@ -217,11 +224,12 @@ function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowPr nodesConnectable={false} snapToGrid={true} snapGrid={[10, 10]} + onContextMenu={handleContextMenu} > {showGrid ? : null} ), - [nodes, edges, proOptions, handleNodesChange, onEdgesChange, OssNodeTypes, showGrid] + [nodes, edges, proOptions, handleNodesChange, handleContextMenu, onEdgesChange, OssNodeTypes, showGrid] ); return ( diff --git a/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx b/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx index a7ab337f..c78e9732 100644 --- a/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx +++ b/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx @@ -26,6 +26,9 @@ export interface IOssEditContext { isMutable: boolean; isProcessing: boolean; + showTooltip: boolean; + setShowTooltip: React.Dispatch>; + setOwner: (newOwner: UserID) => void; setAccessPolicy: (newPolicy: AccessPolicy) => void; promptEditors: () => void; @@ -71,6 +74,8 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr [accessLevel, model.schema?.read_only] ); + const [showTooltip, setShowTooltip] = useState(true); + const [showEditEditors, setShowEditEditors] = useState(false); const [showEditLocation, setShowEditLocation] = useState(false); @@ -211,6 +216,9 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr schema: model.schema, selected, + showTooltip, + setShowTooltip, + isMutable, isProcessing: model.processing, diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx index 1cc7a8f4..f4e156d0 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx @@ -180,7 +180,6 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { }, [graphRef]); function handleKeyDown(event: React.KeyboardEvent) { - // Hotkeys implementation if (controller.isProcessing) { return; } diff --git a/rsconcept/frontend/src/utils/constants.ts b/rsconcept/frontend/src/utils/constants.ts index 99956730..a92bb373 100644 --- a/rsconcept/frontend/src/utils/constants.ts +++ b/rsconcept/frontend/src/utils/constants.ts @@ -148,6 +148,7 @@ export const prefixes = { cst_source_list: 'cst_source_list_', cst_delete_list: 'cst_delete_list_', cst_dependant_list: 'cst_dependant_list_', + operation_list: 'operation_list_', csttype_list: 'csttype_', policy_list: 'policy_list_', library_filters_list: 'library_filters_list_',