From badc2e28b0059d01f00f5509bfba36738eff393e Mon Sep 17 00:00:00 2001 From: Kodjo Sossouvi Date: Mon, 4 Aug 2025 10:34:11 +0200 Subject: [PATCH] Dialog box at the bottom. Property layout fully operationnel --- src/components/workflows/assets/Workflows.css | 25 ++++++- .../workflows/components/WorkflowDesigner.py | 31 +------- .../components/WorkflowDesignerProperties.py | 70 ++++++++++++------- src/components/workflows/constants.py | 29 +++++++- 4 files changed, 101 insertions(+), 54 deletions(-) diff --git a/src/components/workflows/assets/Workflows.css b/src/components/workflows/assets/Workflows.css index 96360a9..2948747 100644 --- a/src/components/workflows/assets/Workflows.css +++ b/src/components/workflows/assets/Workflows.css @@ -59,6 +59,7 @@ box-sizing: border-box; font-family: Arial, sans-serif; background-color: var(--color-base-100); /* bg-base-100 */ + overflow: auto; } @@ -79,10 +80,10 @@ } .wkf-properties-properties { - display: inline-block; vertical-align: top; position: relative; box-sizing: border-box; + overflow: auto; } .wkf-properties-handle-left { @@ -105,6 +106,28 @@ background-color: transparent; } +.wkf-properties-top { + display: flex; + justify-content: center; + align-items: center; + cursor: move; + padding: 4px; +} + +.wkf-properties-handle-top { + background-image: radial-gradient(var(--color-splitter) 40%, transparent 0); + background-repeat: repeat; + background-size: 4px 4px; + cursor: move; + display: flex; + justify-content: center; + align-items: center; + height: 8px; + width: 20px; + position: relative; + top: 1px; +} + .wkf-canvas { position: relative; box-sizing: border-box; diff --git a/src/components/workflows/components/WorkflowDesigner.py b/src/components/workflows/components/WorkflowDesigner.py index a2f78a4..eb6d8ef 100644 --- a/src/components/workflows/components/WorkflowDesigner.py +++ b/src/components/workflows/components/WorkflowDesigner.py @@ -11,7 +11,8 @@ from components.workflows.assets.icons import icon_play, icon_pause, icon_stop, from components.workflows.commands import WorkflowDesignerCommandManager from components.workflows.components.WorkflowDesignerProperties import WorkflowDesignerProperties from components.workflows.components.WorkflowPlayer import WorkflowPlayer -from components.workflows.constants import WORKFLOW_DESIGNER_INSTANCE_ID, ProcessorTypes +from components.workflows.constants import WORKFLOW_DESIGNER_INSTANCE_ID, ProcessorTypes, COMPONENT_TYPES, \ + PROCESSOR_TYPES from components.workflows.db_management import WorkflowsDesignerSettings, WorkflowComponent, \ Connection, WorkflowsDesignerDbManager, ComponentState, WorkflowsDesignerState from components_helpers import apply_boundaries, mk_tooltip, mk_dialog_buttons, mk_icon @@ -23,32 +24,6 @@ from utils.DbManagementHelper import DbManagementHelper logger = logging.getLogger("WorkflowDesigner") -# Component templates -COMPONENT_TYPES = { - ProcessorTypes.Producer: { - "title": "Data Producer", - "description": "Generates or loads data", - "icon": "📊", - "color": "bg-green-100 border-green-300 text-neutral" - }, - ProcessorTypes.Filter: { - "title": "Data Filter", - "description": "Filters and transforms data", - "icon": "🔍", - "color": "bg-blue-100 border-blue-300 text-neutral" - }, - ProcessorTypes.Presenter: { - "title": "Data Presenter", - "description": "Displays or exports data", - "icon": "📋", - "color": "bg-purple-100 border-purple-300 text-neutral" - } -} - -PROCESSOR_TYPES = { - ProcessorTypes.Producer: ["Repository", "Jira"], - ProcessorTypes.Filter: ["Default"], - ProcessorTypes.Presenter: ["Default"]} class WorkflowDesigner(BaseComponent): @@ -216,7 +191,7 @@ class WorkflowDesigner(BaseComponent): undo_redo_attrs = UndoRedoAttrs(f"Select Component {component.title}", on_undo=self.refresh_state) self._db.save_state(self._key, self._state, undo_redo_attrs) - return self.refresh_properties(), self._undo_redo.refresh() + return self.properties.refresh(), self._undo_redo.refresh() def save_properties(self, component_id: str, details: dict): if component_id in self._state.components: diff --git a/src/components/workflows/components/WorkflowDesignerProperties.py b/src/components/workflows/components/WorkflowDesignerProperties.py index 542d02c..1bbb19f 100644 --- a/src/components/workflows/components/WorkflowDesignerProperties.py +++ b/src/components/workflows/components/WorkflowDesignerProperties.py @@ -2,6 +2,9 @@ from fasthtml.common import * from dataclasses import dataclass from components.BaseComponent import BaseComponent +from components.workflows.constants import COMPONENT_TYPES +from components_helpers import mk_dialog_buttons +from core.utils import merge_classes @dataclass @@ -17,12 +20,12 @@ class WorkflowDesignerProperties(BaseComponent): self._owner = owner self._boundaries = self._owner.get_boundaries() self._commands = self._owner.commands - self.layout = self.compute_layout() + self.layout = None + self._component = None + self.update_layout() + self.update_component(self._owner.get_state().selected_component_id) def update_layout(self): - self.layout = self.compute_layout() - - def compute_layout(self) -> DesignerLayout: if self._owner.get_state().properties_input_width is None: input_width = self._boundaries["width"] // 3 properties_width = self._boundaries["width"] // 3 @@ -32,13 +35,19 @@ class WorkflowDesignerProperties(BaseComponent): properties_width = self._owner.get_state().properties_properties_width output_width = self._owner.get_state().properties_output_width - return DesignerLayout( + self.layout = DesignerLayout( input_width=input_width, properties_width=properties_width, output_width=output_width ) + def update_component(self, component_id): + if component_id is None or component_id not in self._owner.get_state().components: + self._component = None + self._component = self._owner.get_state().components[component_id] + def refresh(self, oob=True): + self.update_component(self._owner.get_state().selected_component_id) return self.__ft__(oob=oob) def _mk_input(self): @@ -57,30 +66,44 @@ class WorkflowDesignerProperties(BaseComponent): cls="wkf-properties-output" ) + def _mk_header(self, cls=None): + if self._component is None: + return None + + icon = COMPONENT_TYPES[self._component.type]["icon"] + color = COMPONENT_TYPES[self._component.type]["color"] + return Div( + Div( + Span(icon), + H4(self._component.title, cls="font-semibold text-xs"), + cls=f"rounded-lg border-2 {color} flex text-center px-2" + ), + H1(self._component.id, cls="ml-4"), + cls=merge_classes("flex mb-2", cls) + ) + + def _mk_form(self, cls=None): + if self._component is None: + return None + + return Div( + cls=merge_classes(cls) + ) + def _mk_properties(self): return Div( # Drag handle (20px height) Div( - "Properties", + A(cls="wkf-properties-handle-top"), + cls="wkf-properties-top", id=f"ppt_{self._id}", - style="height: 20px; background-color: #ddd; cursor: move; text-align: center; line-height: 20px; font-weight: bold; font-size: 12px; border-bottom: 1px solid #bbb;" ), # Properties content - Div( - Input(placeholder="output name", style="width: 100%; margin-bottom: 10px;"), - Select( - Option("Repository", value="repository"), - Option("Jira", value="jira"), - style="width: 100%; margin-bottom: 10px;" - ), - Div( - Button("Save", **self._commands.on_save(), style="margin-right: 5px; padding: 5px 10px;"), - Button("Cancel", **self._commands.on_cancel(), style="padding: 5px 10px;"), - style="text-align: center;" - ), - style=f"padding: 10px; box-sizing: border-box;" - ), + + self._mk_header(cls="flex-none"), + self._mk_form(cls="flex-1 overflow-y-auto"), + mk_dialog_buttons(cls="flex-none mt-auto pb-2"), # Left resize handle Div( @@ -95,9 +118,8 @@ class WorkflowDesignerProperties(BaseComponent): ), id=f"pp_{self._id}", - style=f"width: {self.layout.properties_width}px;", - cls="wkf-properties-properties" - + style=f"width: {self.layout.properties_width}px; height: 100%;", + cls="wkf-properties-properties flex flex-col", ) def _mk_layout(self): diff --git a/src/components/workflows/constants.py b/src/components/workflows/constants.py index a0bffae..a8c1f6c 100644 --- a/src/components/workflows/constants.py +++ b/src/components/workflows/constants.py @@ -6,11 +6,39 @@ WORKFLOW_DESIGNER_DB_ENTRY = "WorkflowDesigner" WORKFLOW_DESIGNER_DB_SETTINGS_ENTRY = "Settings" WORKFLOW_DESIGNER_DB_STATE_ENTRY = "State" + class ProcessorTypes: Producer = "producer" Filter = "filter" Presenter = "presenter" + +COMPONENT_TYPES = { + ProcessorTypes.Producer: { + "title": "Data Producer", + "description": "Generates or loads data", + "icon": "📊", + "color": "bg-green-100 border-green-300 text-neutral" + }, + ProcessorTypes.Filter: { + "title": "Data Filter", + "description": "Filters and transforms data", + "icon": "🔍", + "color": "bg-blue-100 border-blue-300 text-neutral" + }, + ProcessorTypes.Presenter: { + "title": "Data Presenter", + "description": "Displays or exports data", + "icon": "📋", + "color": "bg-purple-100 border-purple-300 text-neutral" + } +} + +PROCESSOR_TYPES = { + ProcessorTypes.Producer: ["Repository", "Jira"], + ProcessorTypes.Filter: ["Default"], + ProcessorTypes.Presenter: ["Default"]} + ROUTE_ROOT = "/workflows" @@ -34,4 +62,3 @@ class Routes: PauseWorkflow = "/pause-workflow" StopWorkflow = "/stop-workflow" Refresh = "/refresh" - \ No newline at end of file