From bffed7f2f85a9cc3bf5e1f8a679b99e45390f5b1 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Wed, 27 Nov 2024 16:41:54 +0100 Subject: [PATCH 01/18] Enable selection of terms to import into excel --- src/Client/MainComponents/Widgets.fs | 6 ++- src/Client/OfficeInterop/OfficeInterop.fs | 18 ++++++-- .../Pages/ProtocolTemplates/TemplateFromDB.fs | 43 +++++++++++++++---- src/Client/States/OfficeInteropState.fs | 2 +- src/Client/States/SpreadsheetInterface.fs | 2 +- src/Client/Update/InterfaceUpdate.fs | 4 +- src/Client/Update/OfficeInteropUpdate.fs | 6 +-- src/Client/Update/SpreadsheetUpdate.fs | 2 +- src/Shared/ARCtrl.Helper.fs | 8 +++- 9 files changed, 67 insertions(+), 24 deletions(-) diff --git a/src/Client/MainComponents/Widgets.fs b/src/Client/MainComponents/Widgets.fs index b31dd277..abc2f23b 100644 --- a/src/Client/MainComponents/Widgets.fs +++ b/src/Client/MainComponents/Widgets.fs @@ -211,6 +211,8 @@ type Widget = [] static member Templates (model: Model, dispatch, rmv: MouseEvent -> unit) = let templates, setTemplates = React.useState(model.ProtocolState.Templates) + let selectedColumnsLength = if model.ProtocolState.TemplateSelected.IsSome then model.ProtocolState.TemplateSelected.Value.Table.Columns.Length else 0 + let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init selectedColumnsLength) let config, setConfig = React.useState(TemplateFilterConfig.init) let filteredTemplates = Protocol.Search.filterTemplates (templates, config) React.useEffectOnce(fun _ -> Messages.Protocol.GetAllProtocolsRequest |> Messages.ProtocolMsg |> dispatch) @@ -223,12 +225,12 @@ type Widget = let insertContent() = [ Html.div [ - Protocol.TemplateFromDB.addFromDBToTableButton model dispatch + Protocol.TemplateFromDB.addFromDBToTableButton model selectedColumns dispatch ] Html.div [ prop.style [style.maxHeight (length.px 350); style.overflow.auto] prop.children [ - Protocol.TemplateFromDB.displaySelectedProtocolEle model dispatch + Protocol.TemplateFromDB.displaySelectedProtocolEle model selectedColumns setSelectedColumns dispatch ] ] ] diff --git a/src/Client/OfficeInterop/OfficeInterop.fs b/src/Client/OfficeInterop/OfficeInterop.fs index a42fca99..30ce67e5 100644 --- a/src/Client/OfficeInterop/OfficeInterop.fs +++ b/src/Client/OfficeInterop/OfficeInterop.fs @@ -924,7 +924,7 @@ let addCompositeColumn (excelTable: Table) (arcTable: ArcTable) (newColumn: Comp /// Prepare the given table to be joined with the currently active annotation table /// /// -let prepareTemplateInMemory (table: Table) (tableToAdd: ArcTable) (context: RequestContext) = +let prepareTemplateInMemory (table: Table) (tableToAdd: ArcTable) (selectedColumns:bool []) (context: RequestContext) = promise { let! originTableRes = ArcTable.fromExcelTable(table, context) @@ -932,7 +932,13 @@ let prepareTemplateInMemory (table: Table) (tableToAdd: ArcTable) (context: Requ | Result.Error _ -> return failwith $"Failed to create arc table for table {table.name}" | Result.Ok originTable -> - let finalTable = Table.selectiveTablePrepare originTable tableToAdd + let selectedColumnIndices = + selectedColumns + |> Array.mapi (fun i item -> if item = false then Some i else None) + |> Array.choose (fun x -> x) + |> List.ofArray + + let finalTable = Table.selectiveTablePrepare originTable tableToAdd selectedColumnIndices let selectedRange = context.workbook.getSelectedRange() @@ -959,17 +965,21 @@ let prepareTemplateInMemory (table: Table) (tableToAdd: ArcTable) (context: Requ /// /// /// -let joinTable (tableToAdd: ArcTable, options: TableJoinOptions option) = +let joinTable (tableToAdd: ArcTable, selectedColumns:bool [], options: TableJoinOptions option) = Excel.run(fun context -> promise { + let columns = tableToAdd.Columns + + columns + //When a name is available get the annotation and arctable for easy access of indices and value adaption //Annotation table enables a easy way to adapt the table, updating existing and adding new columns let! result = AnnotationTable.tryGetActive context match result with | Some excelTable -> - let! (tableToAdd: ArcTable, index: int option) = prepareTemplateInMemory excelTable tableToAdd context + let! (tableToAdd: ArcTable, index: int option) = prepareTemplateInMemory excelTable tableToAdd selectedColumns context //Arctable enables a fast check for the existence of input- and output-columns and their indices let! arcTableRes = ArcTable.fromExcelTable(excelTable, context) diff --git a/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs b/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs index f6659a05..bbc0ea1c 100644 --- a/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs +++ b/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs @@ -6,6 +6,15 @@ open Messages open Model open Shared +type SelectedColumns = { + Columns: bool [] +} +with + static member init(length) = + { + Columns = Array.init length (fun _ -> true) + } + type TemplateFromDB = static member toProtocolSearchElement (model:Model) dispatch = @@ -16,7 +25,7 @@ type TemplateFromDB = prop.text "Browse database" ] - static member addFromDBToTableButton (model:Model) dispatch = + static member addFromDBToTableButton (model:Model) selectionInformation dispatch = Html.div [ prop.className "join flex flex-row justify-center gap-2" prop.children [ @@ -30,21 +39,22 @@ type TemplateFromDB = if model.ProtocolState.TemplateSelected.IsNone then failwith "No template selected!" - SpreadsheetInterface.AddTemplate(model.ProtocolState.TemplateSelected.Value.Table) |> InterfaceMsg |> dispatch + SpreadsheetInterface.AddTemplate(model.ProtocolState.TemplateSelected.Value.Table, selectionInformation.Columns) |> InterfaceMsg |> dispatch ) prop.text "Add template" ] if model.ProtocolState.TemplateSelected.IsSome then Daisy.button.a [ button.outline - prop.onClick (fun e -> Protocol.RemoveSelectedProtocol |> ProtocolMsg |> dispatch) + prop.onClick (fun _ -> Protocol.RemoveSelectedProtocol |> ProtocolMsg |> dispatch) button.error Html.i [prop.className "fa-solid fa-times"] |> prop.children ] ] ] - static member displaySelectedProtocolEle (model:Model) dispatch = + [] + static member displaySelectedProtocolEle (model:Model) (selectionInformation:SelectedColumns) (setSelectedColumns:SelectedColumns -> unit) dispatch = Html.div [ prop.style [style.overflowX.auto; style.marginBottom (length.rem 1)] prop.children [ @@ -52,6 +62,7 @@ type TemplateFromDB = prop.children [ Html.thead [ Html.tr [ + Html.th "Selection" Html.th "Column" Html.th "Column TAN" //Html.th "Unit" @@ -59,10 +70,24 @@ type TemplateFromDB = ] ] Html.tbody [ - for column in model.ProtocolState.TemplateSelected.Value.Table.Columns do + for i in 0..model.ProtocolState.TemplateSelected.Value.Table.Columns.Length-1 do + let column = model.ProtocolState.TemplateSelected.Value.Table.Columns.[i] //let unitOption = column.TryGetColumnUnits() yield Html.tr [ + Html.div [ + prop.style [style.display.flex; style.justifyContent.center] + prop.children [ + Daisy.checkbox [ + prop.type'.checkbox + prop.isChecked selectionInformation.Columns.[i] + prop.onChange (fun (b: bool) -> + let selectedData = selectionInformation.Columns + selectedData.[i] <- b + {selectionInformation with Columns = selectedData} |> setSelectedColumns) + ] + ] + ] Html.td (column.Header.ToString()) Html.td (if column.Header.IsTermColumn then column.Header.ToTerm().TermAccessionShort else "-") //td [] [str (if unitOption.IsSome then insertBB.UnitTerm.Value.Name else "-")] @@ -75,19 +100,21 @@ type TemplateFromDB = ] static member Main(model:Model, dispatch) = + let length = if model.ProtocolState.TemplateSelected.IsSome then model.ProtocolState.TemplateSelected.Value.Table.Columns.Length else 0 + let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init length) SidebarComponents.SidebarLayout.LogicContainer [ Html.div [ TemplateFromDB.toProtocolSearchElement model dispatch ] Html.div [ - TemplateFromDB.addFromDBToTableButton model dispatch + TemplateFromDB.addFromDBToTableButton model selectedColumns dispatch ] if model.ProtocolState.TemplateSelected.IsSome then Html.div [ - TemplateFromDB.displaySelectedProtocolEle model dispatch + TemplateFromDB.displaySelectedProtocolEle model selectedColumns setSelectedColumns dispatch ] Html.div [ - TemplateFromDB.addFromDBToTableButton model dispatch + TemplateFromDB.addFromDBToTableButton model selectedColumns dispatch ] ] diff --git a/src/Client/States/OfficeInteropState.fs b/src/Client/States/OfficeInteropState.fs index 009a3785..1f8bf8d6 100644 --- a/src/Client/States/OfficeInteropState.fs +++ b/src/Client/States/OfficeInteropState.fs @@ -31,7 +31,7 @@ type Msg = | ValidateBuildingBlock | AddAnnotationBlock of CompositeColumn | AddAnnotationBlocks of CompositeColumn [] //* OfficeInterop.Types.Xml.ValidationTypes.TableValidation option - | AddTemplate of ArcTable + | AddTemplate of ArcTable * bool[] | JoinTable of ArcTable * options: TableJoinOptions option | RemoveBuildingBlock | UpdateUnitForCells diff --git a/src/Client/States/SpreadsheetInterface.fs b/src/Client/States/SpreadsheetInterface.fs index e9e385e7..968e1aef 100644 --- a/src/Client/States/SpreadsheetInterface.fs +++ b/src/Client/States/SpreadsheetInterface.fs @@ -17,7 +17,7 @@ type Msg = | AddAnnotationBlocks of CompositeColumn [] | AddDataAnnotation of {| fragmentSelectors: string []; fileName: string; fileType: string; targetColumn: DataAnnotator.TargetColumn |} /// This function will do preprocessing on the table to join -| AddTemplate of ArcTable +| AddTemplate of ArcTable * bool[] | JoinTable of ArcTable * columnIndex: int option * options: TableJoinOptions option | UpdateArcFile of ArcFiles /// Inserts TermMinimal to selected fields of one column diff --git a/src/Client/Update/InterfaceUpdate.fs b/src/Client/Update/InterfaceUpdate.fs index 81e2b304..eba7f525 100644 --- a/src/Client/Update/InterfaceUpdate.fs +++ b/src/Client/Update/InterfaceUpdate.fs @@ -164,10 +164,10 @@ module Interface = model, cmd | _ -> failwith "not implemented" - | AddTemplate table -> + | AddTemplate (table, selectedColumns) -> match host with | Some Swatehost.Excel -> - let cmd = OfficeInterop.AddTemplate table |> OfficeInteropMsg |> Cmd.ofMsg + let cmd = OfficeInterop.AddTemplate (table, selectedColumns) |> OfficeInteropMsg |> Cmd.ofMsg model, cmd | Some Swatehost.Browser | Some Swatehost.ARCitect -> let cmd = Spreadsheet.AddTemplate table |> SpreadsheetMsg |> Cmd.ofMsg diff --git a/src/Client/Update/OfficeInteropUpdate.fs b/src/Client/Update/OfficeInteropUpdate.fs index fb82cb49..d013e664 100644 --- a/src/Client/Update/OfficeInteropUpdate.fs +++ b/src/Client/Update/OfficeInteropUpdate.fs @@ -76,11 +76,11 @@ module OfficeInterop = UpdateUtil.downloadFromString (jsonExport) state, model, Cmd.none - | AddTemplate table -> + | AddTemplate (table, selectedColumns) -> let cmd = Cmd.OfPromise.either OfficeInterop.Core.joinTable - (table, Some ARCtrl.TableJoinOptions.WithValues) + (table, selectedColumns, Some ARCtrl.TableJoinOptions.WithValues) (curry GenericInteropLogs Cmd.none >> DevMsg) (curry GenericError Cmd.none >> DevMsg) state, model, cmd @@ -89,7 +89,7 @@ module OfficeInterop = let cmd = Cmd.OfPromise.either OfficeInterop.Core.joinTable - (table, options) + (table, [||], options) (curry GenericInteropLogs Cmd.none >> DevMsg) (curry GenericError Cmd.none >> DevMsg) state, model, cmd diff --git a/src/Client/Update/SpreadsheetUpdate.fs b/src/Client/Update/SpreadsheetUpdate.fs index 300a3cfa..baecd9e5 100644 --- a/src/Client/Update/SpreadsheetUpdate.fs +++ b/src/Client/Update/SpreadsheetUpdate.fs @@ -109,7 +109,7 @@ module Spreadsheet = let options = Some ARCtrl.TableJoinOptions.WithValues // If changed to anything else we need different logic to keep input/output values let msg = fun t -> JoinTable(t, index, options) |> SpreadsheetMsg let cmd = - Table.selectiveTablePrepare state.ActiveTable table + Table.selectiveTablePrepare state.ActiveTable table [] |> msg |> Cmd.ofMsg state, model, cmd diff --git a/src/Shared/ARCtrl.Helper.fs b/src/Shared/ARCtrl.Helper.fs index 67abd0a4..83999645 100644 --- a/src/Shared/ARCtrl.Helper.fs +++ b/src/Shared/ARCtrl.Helper.fs @@ -93,15 +93,19 @@ module Table = /// /// The active/current table /// The new table, which will be added to the existing one. - let selectiveTablePrepare (activeTable: ArcTable) (toJoinTable: ArcTable) : ArcTable = + let selectiveTablePrepare (activeTable: ArcTable) (toJoinTable: ArcTable) (removeColumns:int list): ArcTable = // Remove existing columns - let mutable columnsToRemove = [] + let mutable columnsToRemove = removeColumns // find duplicate columns let tablecopy = toJoinTable.Copy() for header in activeTable.Headers do let containsAtIndex = tablecopy.Headers |> Seq.tryFindIndex (fun h -> h = header) if containsAtIndex.IsSome then columnsToRemove <- containsAtIndex.Value::columnsToRemove + + //Remove duplicates because unselected and already existing columns can overlap + let columnsToRemove = columnsToRemove |> Set.ofList |> Set.toList + tablecopy.RemoveColumns (Array.ofList columnsToRemove) tablecopy.IteriColumns(fun i c0 -> let c1 = {c0 with Cells = [||]} From 2aa0ddfed5f8ac8fca814093e2e3af141ab348c7 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Thu, 28 Nov 2024 13:00:48 +0100 Subject: [PATCH 02/18] Fix error in swate alpha --- src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs b/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs index bbc0ea1c..c63b55aa 100644 --- a/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs +++ b/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs @@ -80,11 +80,15 @@ type TemplateFromDB = prop.children [ Daisy.checkbox [ prop.type'.checkbox - prop.isChecked selectionInformation.Columns.[i] + prop.isChecked + (if selectionInformation.Columns.Length > 0 then + selectionInformation.Columns.[i] + else true) prop.onChange (fun (b: bool) -> - let selectedData = selectionInformation.Columns - selectedData.[i] <- b - {selectionInformation with Columns = selectedData} |> setSelectedColumns) + if selectionInformation.Columns.Length > 0 then + let selectedData = selectionInformation.Columns + selectedData.[i] <- b + {selectionInformation with Columns = selectedData} |> setSelectedColumns) ] ] ] From b179a4a5df9826904344c6eede69cb0d8a194eea Mon Sep 17 00:00:00 2001 From: patrick blume Date: Thu, 28 Nov 2024 13:29:17 +0100 Subject: [PATCH 03/18] Move basic modal element creation methods into separat file --- src/Client/Client.fable-temp.csproj | 154 +++++++++++++++++++ src/Client/Client.fsproj | 2 + src/Client/Modals/ModalElements.fs | 71 +++++++++ src/Client/Modals/SelectiveImportModal.fs | 81 ++-------- src/Client/Modals/SelectiveTemplateFromDB.fs | 41 +++++ 5 files changed, 285 insertions(+), 64 deletions(-) create mode 100644 src/Client/Client.fable-temp.csproj create mode 100644 src/Client/Modals/ModalElements.fs create mode 100644 src/Client/Modals/SelectiveTemplateFromDB.fs diff --git a/src/Client/Client.fable-temp.csproj b/src/Client/Client.fable-temp.csproj new file mode 100644 index 00000000..d1678662 --- /dev/null +++ b/src/Client/Client.fable-temp.csproj @@ -0,0 +1,154 @@ + + + + + net8.0 + FABLE_COMPILER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Client/Client.fsproj b/src/Client/Client.fsproj index 93fe14cc..63c15990 100644 --- a/src/Client/Client.fsproj +++ b/src/Client/Client.fsproj @@ -35,6 +35,7 @@ + @@ -52,6 +53,7 @@ + diff --git a/src/Client/Modals/ModalElements.fs b/src/Client/Modals/ModalElements.fs new file mode 100644 index 00000000..36eb9700 --- /dev/null +++ b/src/Client/Modals/ModalElements.fs @@ -0,0 +1,71 @@ +module Modals.ModalElements + +open Feliz +open Feliz.DaisyUI +open Model +open Messages +open Shared + +open ARCtrl +open JsonImport +open Components + + +type ModalElements = + + static member RadioPlugin(radioGroup: string, txt:string, isChecked, onChange: bool -> unit, ?isDisabled: bool) = + let isDisabled = defaultArg isDisabled false + Daisy.formControl [ + Daisy.label [ + prop.className [ + "cursor-pointer transition-colors" + if isDisabled then + "!cursor-not-allowed" + else + "hover:bg-base-300" + ] + prop.children [ + Daisy.radio [ + prop.disabled isDisabled + radio.xs + prop.name radioGroup + prop.isChecked isChecked + prop.onChange onChange + ] + Html.span [ + prop.className "text-sm" + prop.text txt + ] + ] + ] + ] + + static member Box(title: string, icon: string, content: ReactElement, ?className: string list) = + Html.div [ + prop.className [ + "rounded shadow p-2 flex flex-col gap-2 border" + if className.IsSome then + className.Value |> String.concat " " + ] + prop.children [ + Html.h3 [ + prop.className "font-semibold gap-2 flex flex-row items-center" + prop.children [ + Html.i [prop.className icon] + Html.span title + ] + ] + content + ] + ] + + static member ImportRadioPlugins(importType: TableJoinOptions, radioData: (TableJoinOptions * string)[], setImportType: TableJoinOptions -> unit) = + let myradio(target: TableJoinOptions, txt: string) = + let isChecked = importType = target + ModalElements.RadioPlugin("importType", txt, isChecked, fun (b: bool) -> if b then setImportType target) + ModalElements.Box ("Import Type", "fa-solid fa-cog", React.fragment [ + Html.div [ + for i in 0..radioData.Length-1 do + myradio(radioData.[i]) + ] + ]) diff --git a/src/Client/Modals/SelectiveImportModal.fs b/src/Client/Modals/SelectiveImportModal.fs index 5e333e8b..dd70ee28 100644 --- a/src/Client/Modals/SelectiveImportModal.fs +++ b/src/Client/Modals/SelectiveImportModal.fs @@ -1,4 +1,4 @@ -namespace Modals +namespace Modals.Import open Feliz open Feliz.DaisyUI @@ -10,68 +10,14 @@ open ARCtrl open JsonImport open Components -type SelectiveImportModal = +open Modals +open Modals.ModalElements - static member private Radio(radioGroup: string, txt:string, isChecked, onChange: bool -> unit, ?isDisabled: bool) = - let isDisabled = defaultArg isDisabled false - Daisy.formControl [ - Daisy.label [ - prop.className [ - "cursor-pointer transition-colors" - if isDisabled then - "!cursor-not-allowed" - else - "hover:bg-base-300" - ] - prop.children [ - Daisy.radio [ - prop.disabled isDisabled - radio.xs - prop.name radioGroup - prop.isChecked isChecked - prop.onChange onChange - ] - Html.span [ - prop.className "text-sm" - prop.text txt - ] - ] - ] - ] - static member private Box (title: string, icon: string, content: ReactElement, ?className: string list) = - Html.div [ - prop.className [ - "rounded shadow p-2 flex flex-col gap-2 border" - if className.IsSome then - className.Value |> String.concat " " - ] - prop.children [ - Html.h3 [ - prop.className "font-semibold gap-2 flex flex-row items-center" - prop.children [ - Html.i [prop.className icon] - Html.span title - ] - ] - content - ] - ] - - static member private ImportTypeRadio(importType: TableJoinOptions, setImportType: TableJoinOptions -> unit) = - let myradio(target: TableJoinOptions, txt: string) = - let isChecked = importType = target - SelectiveImportModal.Radio("importType", txt, isChecked, fun (b:bool) -> if b then setImportType target) - SelectiveImportModal.Box ("Import Type", "fa-solid fa-cog", React.fragment [ - Html.div [ - myradio(ARCtrl.TableJoinOptions.Headers, " Column Headers") - myradio(ARCtrl.TableJoinOptions.WithUnit, " ..With Units") - myradio(ARCtrl.TableJoinOptions.WithValues, " ..With Values") - ] - ]) +type SelectiveImportModal = static member private MetadataImport(isActive: bool, setActive: bool -> unit, disArcFile: ArcFilesDiscriminate) = let name = string disArcFile - SelectiveImportModal.Box (sprintf "%s Metadata" name, "fa-solid fa-lightbulb", React.fragment [ + ModalElements.Box (sprintf "%s Metadata" name, "fa-solid fa-lightbulb", React.fragment [ Daisy.formControl [ Daisy.label [ prop.className "cursor-pointer" @@ -105,19 +51,19 @@ type SelectiveImportModal = let import = state.ImportTables |> List.tryFind (fun it -> it.Index = index) let isActive = import.IsSome let isDisabled = state.ImportMetadata - SelectiveImportModal.Box (name, "fa-solid fa-table", React.fragment [ + ModalElements.Box (name, "fa-solid fa-table", React.fragment [ Html.div [ - SelectiveImportModal.Radio (radioGroup, "Import", + ModalElements.RadioPlugin (radioGroup, "Import", isActive && import.Value.FullImport, (fun (b:bool) -> addTableImport index true), isDisabled ) - SelectiveImportModal.Radio (radioGroup, "Append to active table", + ModalElements.RadioPlugin (radioGroup, "Append to active table", isActive && not import.Value.FullImport, (fun (b:bool) -> addTableImport index false), isDisabled ) - SelectiveImportModal.Radio (radioGroup, "No Import", + ModalElements.RadioPlugin (radioGroup, "No Import", not isActive, (fun (b:bool) -> rmvTableImport index), isDisabled @@ -195,7 +141,14 @@ type SelectiveImportModal = Components.DeleteButton(props=[prop.onClick rmv]) ] ] - SelectiveImportModal.ImportTypeRadio(state.ImportType, fun it -> {state with ImportType = it} |> setState) + ModalElements.ImportRadioPlugins( + state.ImportType, + [| + ARCtrl.TableJoinOptions.Headers, " Column Headers"; + ARCtrl.TableJoinOptions.WithUnit, " ..With Units"; + ARCtrl.TableJoinOptions.WithValues, " ..With Values"; + |], + fun it -> {state with ImportType = it} |> setState) SelectiveImportModal.MetadataImport(state.ImportMetadata, setMetadataImport, disArcfile) for ti in 0 .. (tables.Count-1) do let t = tables.[ti] diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Modals/SelectiveTemplateFromDB.fs new file mode 100644 index 00000000..e6a18518 --- /dev/null +++ b/src/Client/Modals/SelectiveTemplateFromDB.fs @@ -0,0 +1,41 @@ +namespace Modals.Template + +open Feliz +open Feliz.DaisyUI +open Model +open Messages +open Shared + +open ARCtrl +open JsonImport +open Components + + +type SelectiveTemplateFromDBModal = + + static member private Radio(radioGroup: string, txt:string, isChecked, onChange: bool -> unit, ?isDisabled: bool) = + let isDisabled = defaultArg isDisabled false + Daisy.formControl [ + Daisy.label [ + prop.className [ + "cursor-pointer transition-colors" + if isDisabled then + "!cursor-not-allowed" + else + "hover:bg-base-300" + ] + prop.children [ + Daisy.radio [ + prop.disabled isDisabled + radio.xs + prop.name radioGroup + prop.isChecked isChecked + prop.onChange onChange + ] + Html.span [ + prop.className "text-sm" + prop.text txt + ] + ] + ] + ] \ No newline at end of file From b46fc28aa74ff5c08020e5888da2a2a2b4dc4fd7 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Thu, 28 Nov 2024 15:54:17 +0100 Subject: [PATCH 04/18] Replace existing TemplateFromDB with Modal --- src/Client/Modals/ModalElements.fs | 13 ++ src/Client/Modals/ModalProvider.fs | 4 +- src/Client/Modals/SelectiveTemplateFromDB.fs | 143 +++++++++++++++--- .../Pages/ProtocolTemplates/ProtocolView.fs | 2 +- .../Pages/ProtocolTemplates/TemplateFromDB.fs | 8 +- .../ProtocolTemplates/TemplateFromFile.fs | 2 +- src/Client/SidebarComponents/LayoutHelper.fs | 1 + src/Client/States/ModalState.fs | 3 + 8 files changed, 146 insertions(+), 30 deletions(-) diff --git a/src/Client/Modals/ModalElements.fs b/src/Client/Modals/ModalElements.fs index 36eb9700..d275452c 100644 --- a/src/Client/Modals/ModalElements.fs +++ b/src/Client/Modals/ModalElements.fs @@ -13,6 +13,19 @@ open Components type ModalElements = + static member Button(text: string, onClickAction, buttonInput, ?isDisabled: bool) = + let isDisabled = defaultArg isDisabled false + Daisy.button.a [ + button.success + button.wide + if isDisabled then + button.error + prop.disabled isDisabled + prop.onClick (fun _ -> onClickAction buttonInput) + + prop.text text + ] + static member RadioPlugin(radioGroup: string, txt:string, isChecked, onChange: bool -> unit, ?isDisabled: bool) = let isDisabled = defaultArg isDisabled false Daisy.formControl [ diff --git a/src/Client/Modals/ModalProvider.fs b/src/Client/Modals/ModalProvider.fs index 5962d320..8f5c34d2 100644 --- a/src/Client/Modals/ModalProvider.fs +++ b/src/Client/Modals/ModalProvider.fs @@ -22,8 +22,10 @@ type ModalProvider = Modals.ResetTable.Main dispatch | TableModals.TermDetails term -> Modals.TermModal.Main (term, dispatch) + | TableModals.SelectiveTemplateImportFromDB -> + Modals.Template.SelectiveTemplateFromDBModal.Main (model, dispatch) | TableModals.SelectiveFileImport arcfile -> - Modals.SelectiveImportModal.Main (arcfile, dispatch) + Modals.Import.SelectiveImportModal.Main (arcfile, dispatch) | TableModals.BatchUpdateColumnValues (columnIndex, column) -> Modals.UpdateColumn.Main (columnIndex, column, dispatch) | TableModals.TableCellContext (mouseX, mouseY, ci, ri) -> diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Modals/SelectiveTemplateFromDB.fs index e6a18518..67023bda 100644 --- a/src/Client/Modals/SelectiveTemplateFromDB.fs +++ b/src/Client/Modals/SelectiveTemplateFromDB.fs @@ -10,32 +10,131 @@ open ARCtrl open JsonImport open Components +open Modals +open Modals.ModalElements + +type SelectedColumns = { + Columns: bool [] +} +with + static member init(length) = + { + Columns = Array.init length (fun _ -> true) + } type SelectiveTemplateFromDBModal = - static member private Radio(radioGroup: string, txt:string, isChecked, onChange: bool -> unit, ?isDisabled: bool) = - let isDisabled = defaultArg isDisabled false - Daisy.formControl [ - Daisy.label [ - prop.className [ - "cursor-pointer transition-colors" - if isDisabled then - "!cursor-not-allowed" - else - "hover:bg-base-300" - ] - prop.children [ - Daisy.radio [ - prop.disabled isDisabled - radio.xs - prop.name radioGroup - prop.isChecked isChecked - prop.onChange onChange + static member private LogicContainer (children: ReactElement list) = + Html.div [ + // prop.className "border-l-4 border-transparent px-4 py-2 shadow-md" + // prop.style [ + // let rndVal = rnd.Next(30,70) + // let colorArr = [|NFDIColors.LightBlue.Lighter10; NFDIColors.Mint.Lighter10;|] + // style.custom("borderImageSlice", "1") + // style.custom("borderImageSource", $"linear-gradient({colorArr.[if order then 0 else 1]} {100-rndVal}%%, {colorArr.[if order then 1 else 0]})") + // order <- not order + // ] + prop.className "relative flex p-4 animated-border shadow-md gap-4 flex-col" //experimental + prop.children children + ] + + [] + static member displaySelectedProtocolEle (model: Model) (selectionInformation:SelectedColumns) (setSelectedColumns:SelectedColumns -> unit) dispatch = + Html.div [ + prop.style [style.overflowX.auto; style.marginBottom (length.rem 1)] + prop.children [ + Daisy.table [ + prop.children [ + Html.thead [ + Html.tr [ + Html.th "Selection" + Html.th "Column" + Html.th "Column TAN" + ] + ] + Html.tbody [ + let length = + if model.ProtocolState.TemplateSelected.IsSome then model.ProtocolState.TemplateSelected.Value.Table.Columns.Length-1 + else 0 + for i in 0..length do + let column = model.ProtocolState.TemplateSelected.Value.Table.Columns.[i] + yield + Html.tr [ + Html.div [ + prop.style [style.display.flex; style.justifyContent.center] + prop.children [ + Daisy.checkbox [ + prop.type'.checkbox + prop.isChecked + (if selectionInformation.Columns.Length > 0 then + selectionInformation.Columns.[i] + else true) + prop.onChange (fun (b: bool) -> + if selectionInformation.Columns.Length > 0 then + let selectedData = selectionInformation.Columns + selectedData.[i] <- b + {selectionInformation with Columns = selectedData} |> setSelectedColumns) + ] + ] + ] + Html.td (column.Header.ToString()) + Html.td (if column.Header.IsTermColumn then column.Header.ToTerm().TermAccessionShort else "-") + ] + ] ] - Html.span [ - prop.className "text-sm" - prop.text txt + ] + ] + ] + + static member addFromDBToTableButton (model: Model) selectionInformation dispatch = + let addTemplate (templatePot: Template option, selectedColumns) = + if model.ProtocolState.TemplateSelected.IsNone then + failwith "No template selected!" + if templatePot.IsSome then + let table = templatePot.Value.Table + SpreadsheetInterface.AddTemplate(table, selectedColumns) |> InterfaceMsg |> dispatch + Html.div [ + prop.className "join flex flex-row justify-center gap-2" + prop.children [ + ModalElements.Button("Add template", addTemplate, (model.ProtocolState.TemplateSelected, selectionInformation.Columns), model.ProtocolState.TemplateSelected.IsNone) + if model.ProtocolState.TemplateSelected.IsSome then + Daisy.button.a [ + button.outline + prop.onClick (fun _ -> Protocol.RemoveSelectedProtocol |> ProtocolMsg |> dispatch) + button.error + Html.i [prop.className "fa-solid fa-times"] |> prop.children ] + ] + ] + + [] + static member Main(model:Model, dispatch) = + let length = + if model.ProtocolState.TemplateSelected.IsSome then + model.ProtocolState.TemplateSelected.Value.Table.Columns.Length + else 0 + let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init length) + SelectiveTemplateFromDBModal.LogicContainer [ + Html.div [ + Daisy.button.button [ + prop.onClick(fun _ -> UpdateModel {model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch} |> dispatch) + button.primary + button.block + prop.text "Browse database" ] ] - ] \ No newline at end of file + Html.div [ + SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns dispatch + ] + if model.ProtocolState.TemplateSelected.IsSome then + Html.div [ + SelectiveTemplateFromDBModal.displaySelectedProtocolEle model selectedColumns setSelectedColumns dispatch + ] + Html.div [ + SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns dispatch + ] + ] + + //static member Main(model:Model, dispatch: Messages.Msg -> unit) = + // let rmv = Util.RMV_MODAL dispatch + // SelectiveTemplateFromDBModal.Main (model, dispatch, rmv) \ No newline at end of file diff --git a/src/Client/Pages/ProtocolTemplates/ProtocolView.fs b/src/Client/Pages/ProtocolTemplates/ProtocolView.fs index 8b4fb71e..a65ee6a7 100644 --- a/src/Client/Pages/ProtocolTemplates/ProtocolView.fs +++ b/src/Client/Pages/ProtocolTemplates/ProtocolView.fs @@ -39,7 +39,7 @@ type Templates = // Box 1 SidebarComponents.SidebarLayout.Description "Add template from database." - TemplateFromDB.Main(model, dispatch) + Modals.Template.SelectiveTemplateFromDBModal.Main(model, dispatch) // Box 2 SidebarComponents.SidebarLayout.Description (Html.p [ diff --git a/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs b/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs index c63b55aa..c87ee209 100644 --- a/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs +++ b/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs @@ -17,7 +17,7 @@ with type TemplateFromDB = - static member toProtocolSearchElement (model:Model) dispatch = + static member toProtocolSearchElement (model: Model) dispatch = Daisy.button.button [ prop.onClick(fun _ -> UpdateModel {model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch} |> dispatch) button.primary @@ -25,7 +25,7 @@ type TemplateFromDB = prop.text "Browse database" ] - static member addFromDBToTableButton (model:Model) selectionInformation dispatch = + static member addFromDBToTableButton (model: Model) selectionInformation dispatch = Html.div [ prop.className "join flex flex-row justify-center gap-2" prop.children [ @@ -38,7 +38,6 @@ type TemplateFromDB = prop.onClick (fun _ -> if model.ProtocolState.TemplateSelected.IsNone then failwith "No template selected!" - SpreadsheetInterface.AddTemplate(model.ProtocolState.TemplateSelected.Value.Table, selectionInformation.Columns) |> InterfaceMsg |> dispatch ) prop.text "Add template" @@ -54,7 +53,7 @@ type TemplateFromDB = ] [] - static member displaySelectedProtocolEle (model:Model) (selectionInformation:SelectedColumns) (setSelectedColumns:SelectedColumns -> unit) dispatch = + static member displaySelectedProtocolEle (model: Model) (selectionInformation:SelectedColumns) (setSelectedColumns:SelectedColumns -> unit) dispatch = Html.div [ prop.style [style.overflowX.auto; style.marginBottom (length.rem 1)] prop.children [ @@ -110,7 +109,6 @@ type TemplateFromDB = Html.div [ TemplateFromDB.toProtocolSearchElement model dispatch ] - Html.div [ TemplateFromDB.addFromDBToTableButton model selectedColumns dispatch ] diff --git a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs index cf287e9b..8c81319d 100644 --- a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs +++ b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs @@ -113,7 +113,7 @@ type TemplateFromFile = // modal! match state.UploadedFile with | Some af -> - Modals.SelectiveImportModal.Main (af, dispatch, rmv = (fun _ -> TemplateFromFileState.init() |> setState)) + Modals.Import.SelectiveImportModal.Main (af, dispatch, rmv = (fun _ -> TemplateFromFileState.init() |> setState)) | None -> Html.none Html.div [ Daisy.join [ diff --git a/src/Client/SidebarComponents/LayoutHelper.fs b/src/Client/SidebarComponents/LayoutHelper.fs index d1578854..692f5e89 100644 --- a/src/Client/SidebarComponents/LayoutHelper.fs +++ b/src/Client/SidebarComponents/LayoutHelper.fs @@ -39,6 +39,7 @@ type SidebarLayout = prop.className "relative flex p-4 animated-border shadow-md gap-4 flex-col" //experimental prop.children children ] + static member Header(txt: string) = Html.h3 [ prop.className "text-lg font-semibold" diff --git a/src/Client/States/ModalState.fs b/src/Client/States/ModalState.fs index 1b784b4e..bcdb1a3d 100644 --- a/src/Client/States/ModalState.fs +++ b/src/Client/States/ModalState.fs @@ -5,12 +5,15 @@ open ARCtrl open Feliz +open Model + module ModalState = type TableModals = | EditColumn of columIndex: int | MoveColumn of columnIndex: int | BatchUpdateColumnValues of columIndex: int * column: CompositeColumn + | SelectiveTemplateImportFromDB | SelectiveFileImport of ArcFiles | TermDetails of OntologyAnnotation | TableCellContext of mouseX: int * mouseY: int * columnIndex: int * rowIndex: int From 04a2cb704427f06ee1f498641b2fcf873c202bcd Mon Sep 17 00:00:00 2001 From: patrick blume Date: Fri, 29 Nov 2024 11:40:31 +0100 Subject: [PATCH 05/18] Add usage of selected columns to swate alpha --- src/Client/Client.fsproj | 1 - src/Client/MainComponents/Widgets.fs | 5 +- src/Client/Modals/SelectiveTemplateFromDB.fs | 12 -- .../Pages/ProtocolTemplates/TemplateFromDB.fs | 122 ------------------ src/Client/States/Spreadsheet.fs | 2 +- src/Client/Update/InterfaceUpdate.fs | 2 +- src/Client/Update/SpreadsheetUpdate.fs | 12 +- 7 files changed, 14 insertions(+), 142 deletions(-) delete mode 100644 src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs diff --git a/src/Client/Client.fsproj b/src/Client/Client.fsproj index 63c15990..50e238c0 100644 --- a/src/Client/Client.fsproj +++ b/src/Client/Client.fsproj @@ -92,7 +92,6 @@ - diff --git a/src/Client/MainComponents/Widgets.fs b/src/Client/MainComponents/Widgets.fs index abc2f23b..6cfd3fe9 100644 --- a/src/Client/MainComponents/Widgets.fs +++ b/src/Client/MainComponents/Widgets.fs @@ -4,6 +4,7 @@ open Feliz open Feliz.DaisyUI open Browser.Types open LocalStorage.Widgets +open Modals.Template module private InitExtensions = @@ -225,12 +226,12 @@ type Widget = let insertContent() = [ Html.div [ - Protocol.TemplateFromDB.addFromDBToTableButton model selectedColumns dispatch + SelectiveTemplateFromDBModal.displaySelectedProtocolEle model selectedColumns setSelectedColumns dispatch ] Html.div [ prop.style [style.maxHeight (length.px 350); style.overflow.auto] prop.children [ - Protocol.TemplateFromDB.displaySelectedProtocolEle model selectedColumns setSelectedColumns dispatch + SelectiveTemplateFromDBModal.displaySelectedProtocolEle model selectedColumns setSelectedColumns dispatch ] ] ] diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Modals/SelectiveTemplateFromDB.fs index 67023bda..72f76dbc 100644 --- a/src/Client/Modals/SelectiveTemplateFromDB.fs +++ b/src/Client/Modals/SelectiveTemplateFromDB.fs @@ -26,14 +26,6 @@ type SelectiveTemplateFromDBModal = static member private LogicContainer (children: ReactElement list) = Html.div [ - // prop.className "border-l-4 border-transparent px-4 py-2 shadow-md" - // prop.style [ - // let rndVal = rnd.Next(30,70) - // let colorArr = [|NFDIColors.LightBlue.Lighter10; NFDIColors.Mint.Lighter10;|] - // style.custom("borderImageSlice", "1") - // style.custom("borderImageSource", $"linear-gradient({colorArr.[if order then 0 else 1]} {100-rndVal}%%, {colorArr.[if order then 1 else 0]})") - // order <- not order - // ] prop.className "relative flex p-4 animated-border shadow-md gap-4 flex-col" //experimental prop.children children ] @@ -134,7 +126,3 @@ type SelectiveTemplateFromDBModal = SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns dispatch ] ] - - //static member Main(model:Model, dispatch: Messages.Msg -> unit) = - // let rmv = Util.RMV_MODAL dispatch - // SelectiveTemplateFromDBModal.Main (model, dispatch, rmv) \ No newline at end of file diff --git a/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs b/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs deleted file mode 100644 index c87ee209..00000000 --- a/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs +++ /dev/null @@ -1,122 +0,0 @@ -namespace Protocol - -open Feliz -open Feliz.DaisyUI -open Messages -open Model -open Shared - -type SelectedColumns = { - Columns: bool [] -} -with - static member init(length) = - { - Columns = Array.init length (fun _ -> true) - } - -type TemplateFromDB = - - static member toProtocolSearchElement (model: Model) dispatch = - Daisy.button.button [ - prop.onClick(fun _ -> UpdateModel {model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch} |> dispatch) - button.primary - button.block - prop.text "Browse database" - ] - - static member addFromDBToTableButton (model: Model) selectionInformation dispatch = - Html.div [ - prop.className "join flex flex-row justify-center gap-2" - prop.children [ - Daisy.button.a [ - button.success - button.wide - if model.ProtocolState.TemplateSelected.IsNone then - button.error - prop.disabled true - prop.onClick (fun _ -> - if model.ProtocolState.TemplateSelected.IsNone then - failwith "No template selected!" - SpreadsheetInterface.AddTemplate(model.ProtocolState.TemplateSelected.Value.Table, selectionInformation.Columns) |> InterfaceMsg |> dispatch - ) - prop.text "Add template" - ] - if model.ProtocolState.TemplateSelected.IsSome then - Daisy.button.a [ - button.outline - prop.onClick (fun _ -> Protocol.RemoveSelectedProtocol |> ProtocolMsg |> dispatch) - button.error - Html.i [prop.className "fa-solid fa-times"] |> prop.children - ] - ] - ] - - [] - static member displaySelectedProtocolEle (model: Model) (selectionInformation:SelectedColumns) (setSelectedColumns:SelectedColumns -> unit) dispatch = - Html.div [ - prop.style [style.overflowX.auto; style.marginBottom (length.rem 1)] - prop.children [ - Daisy.table [ - prop.children [ - Html.thead [ - Html.tr [ - Html.th "Selection" - Html.th "Column" - Html.th "Column TAN" - //Html.th "Unit" - //Html.th "Unit TAN" - ] - ] - Html.tbody [ - for i in 0..model.ProtocolState.TemplateSelected.Value.Table.Columns.Length-1 do - let column = model.ProtocolState.TemplateSelected.Value.Table.Columns.[i] - //let unitOption = column.TryGetColumnUnits() - yield - Html.tr [ - Html.div [ - prop.style [style.display.flex; style.justifyContent.center] - prop.children [ - Daisy.checkbox [ - prop.type'.checkbox - prop.isChecked - (if selectionInformation.Columns.Length > 0 then - selectionInformation.Columns.[i] - else true) - prop.onChange (fun (b: bool) -> - if selectionInformation.Columns.Length > 0 then - let selectedData = selectionInformation.Columns - selectedData.[i] <- b - {selectionInformation with Columns = selectedData} |> setSelectedColumns) - ] - ] - ] - Html.td (column.Header.ToString()) - Html.td (if column.Header.IsTermColumn then column.Header.ToTerm().TermAccessionShort else "-") - //td [] [str (if unitOption.IsSome then insertBB.UnitTerm.Value.Name else "-")] - //td [] [str (if insertBB.HasUnit then insertBB.UnitTerm.Value.TermAccession else "-")] - ] - ] - ] - ] - ] - ] - - static member Main(model:Model, dispatch) = - let length = if model.ProtocolState.TemplateSelected.IsSome then model.ProtocolState.TemplateSelected.Value.Table.Columns.Length else 0 - let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init length) - SidebarComponents.SidebarLayout.LogicContainer [ - Html.div [ - TemplateFromDB.toProtocolSearchElement model dispatch - ] - Html.div [ - TemplateFromDB.addFromDBToTableButton model selectedColumns dispatch - ] - if model.ProtocolState.TemplateSelected.IsSome then - Html.div [ - TemplateFromDB.displaySelectedProtocolEle model selectedColumns setSelectedColumns dispatch - ] - Html.div [ - TemplateFromDB.addFromDBToTableButton model selectedColumns dispatch - ] - ] diff --git a/src/Client/States/Spreadsheet.fs b/src/Client/States/Spreadsheet.fs index 3b747069..31efc352 100644 --- a/src/Client/States/Spreadsheet.fs +++ b/src/Client/States/Spreadsheet.fs @@ -201,7 +201,7 @@ type Msg = | AddAnnotationBlock of CompositeColumn | AddAnnotationBlocks of CompositeColumn [] | AddDataAnnotation of {| fragmentSelectors: string []; fileName: string; fileType: string; targetColumn: DataAnnotator.TargetColumn |} -| AddTemplate of ArcTable +| AddTemplate of ArcTable * bool[] | JoinTable of ArcTable * index: int option * options: TableJoinOptions option | UpdateArcFile of ArcFiles | InitFromArcFile of ArcFiles diff --git a/src/Client/Update/InterfaceUpdate.fs b/src/Client/Update/InterfaceUpdate.fs index eba7f525..3008a332 100644 --- a/src/Client/Update/InterfaceUpdate.fs +++ b/src/Client/Update/InterfaceUpdate.fs @@ -170,7 +170,7 @@ module Interface = let cmd = OfficeInterop.AddTemplate (table, selectedColumns) |> OfficeInteropMsg |> Cmd.ofMsg model, cmd | Some Swatehost.Browser | Some Swatehost.ARCitect -> - let cmd = Spreadsheet.AddTemplate table |> SpreadsheetMsg |> Cmd.ofMsg + let cmd = Spreadsheet.AddTemplate (table, selectedColumns) |> SpreadsheetMsg |> Cmd.ofMsg model, cmd | _ -> failwith "not implemented" | JoinTable (table, index, options) -> diff --git a/src/Client/Update/SpreadsheetUpdate.fs b/src/Client/Update/SpreadsheetUpdate.fs index baecd9e5..d040ad92 100644 --- a/src/Client/Update/SpreadsheetUpdate.fs +++ b/src/Client/Update/SpreadsheetUpdate.fs @@ -103,13 +103,19 @@ module Spreadsheet = | IsTable -> Controller.BuildingBlocks.addDataAnnotation data state | IsMetadata -> failwith "Unable to add data annotation in metadata view" nextState, model, Cmd.none - | AddTemplate table -> + | AddTemplate (table, selectedColumns) -> let index = Some (Spreadsheet.Controller.BuildingBlocks.SidebarControllerAux.getNextColumnIndex model.SpreadsheetModel) /// Filter out existing building blocks and keep input/output values. let options = Some ARCtrl.TableJoinOptions.WithValues // If changed to anything else we need different logic to keep input/output values - let msg = fun t -> JoinTable(t, index, options) |> SpreadsheetMsg + let msg = fun table -> JoinTable(table, index, options) |> SpreadsheetMsg + let selectedColumnsIndices = + selectedColumns + |> Array.mapi (fun i item -> if item = false then Some i else None) + |> Array.choose (fun x -> x) + |> List.ofArray + let cmd = - Table.selectiveTablePrepare state.ActiveTable table [] + Table.selectiveTablePrepare state.ActiveTable table selectedColumnsIndices |> msg |> Cmd.ofMsg state, model, cmd From 12d4d78d0ae17da2ac46047d2fdc801b5cfc7329 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Mon, 2 Dec 2024 08:22:20 +0100 Subject: [PATCH 06/18] Adapt box layout --- src/Client/Modals/ModalElements.fs | 22 ++++++++++++ src/Client/Modals/SelectiveTemplateFromDB.fs | 35 +++++++++++--------- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/Client/Modals/ModalElements.fs b/src/Client/Modals/ModalElements.fs index d275452c..5bb525e9 100644 --- a/src/Client/Modals/ModalElements.fs +++ b/src/Client/Modals/ModalElements.fs @@ -72,6 +72,28 @@ type ModalElements = ] ] + static member BoxWithChildren(children: ReactElement list, boxClass: string, ?title: string, ?icon: string, ?className: string list) = + Html.div [ + prop.className [ + "rounded shadow p-2 flex flex-col gap-2 border" + if className.IsSome then + className.Value |> String.concat " " + ] + prop.children [ + Html.h3 [ + prop.className boxClass + if icon.IsSome || title.IsSome then + prop.children [ + if icon.IsSome then + Html.i [prop.className icon.Value] + if title.IsSome then + Html.span title.Value + ] + prop.children children + ] + ] + ] + static member ImportRadioPlugins(importType: TableJoinOptions, radioData: (TableJoinOptions * string)[], setImportType: TableJoinOptions -> unit) = let myradio(target: TableJoinOptions, txt: string) = let isChecked = importType = target diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Modals/SelectiveTemplateFromDB.fs index 72f76dbc..fc3bfb97 100644 --- a/src/Client/Modals/SelectiveTemplateFromDB.fs +++ b/src/Client/Modals/SelectiveTemplateFromDB.fs @@ -100,29 +100,32 @@ type SelectiveTemplateFromDBModal = ] [] - static member Main(model:Model, dispatch) = + static member Main(model: Model, dispatch) = let length = if model.ProtocolState.TemplateSelected.IsSome then model.ProtocolState.TemplateSelected.Value.Table.Columns.Length else 0 let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init length) - SelectiveTemplateFromDBModal.LogicContainer [ - Html.div [ - Daisy.button.button [ - prop.onClick(fun _ -> UpdateModel {model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch} |> dispatch) - button.primary - button.block - prop.text "Browse database" - ] - ] - Html.div [ - SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns dispatch - ] - if model.ProtocolState.TemplateSelected.IsSome then + ModalElements.BoxWithChildren( + [ Html.div [ - SelectiveTemplateFromDBModal.displaySelectedProtocolEle model selectedColumns setSelectedColumns dispatch + Daisy.button.button [ + prop.onClick(fun _ -> UpdateModel {model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch} |> dispatch) + button.primary + button.block + prop.text "Browse database" + ] ] Html.div [ SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns dispatch ] - ] + if model.ProtocolState.TemplateSelected.IsSome then + Html.div [ + SelectiveTemplateFromDBModal.displaySelectedProtocolEle model selectedColumns setSelectedColumns dispatch + ] + Html.div [ + SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns dispatch + ] + ], + "relative flex p-4 animated-border shadow-md gap-4 flex-col" + ) From 450cda896515001f745f257ae38fe4090756f717 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Tue, 3 Dec 2024 09:57:55 +0100 Subject: [PATCH 07/18] Enable column selection and import type selection for templates and json files --- src/Client/MainComponents/Widgets.fs | 5 +- src/Client/Modals/ModalElements.fs | 83 ++++++++++++- src/Client/Modals/SelectiveImportModal.fs | 61 +++++----- src/Client/Modals/SelectiveTemplateFromDB.fs | 111 ++++++------------ src/Client/OfficeInterop/OfficeInterop.fs | 67 +++++++++-- .../Pages/ProtocolTemplates/ProtocolView.fs | 1 - .../ProtocolTemplates/TemplateFromFile.fs | 34 +++--- src/Client/States/OfficeInteropState.fs | 2 +- src/Client/States/SpreadsheetInterface.fs | 5 +- src/Client/Update/InterfaceUpdate.fs | 15 ++- src/Client/Update/OfficeInteropUpdate.fs | 4 +- src/Client/Update/UpdateUtil.fs | 24 +++- src/Shared/ARCtrl.Helper.fs | 4 +- src/Shared/DTOs/SelectedColumnsModalDto.fs | 10 ++ src/Shared/Shared.fsproj | 1 + 15 files changed, 262 insertions(+), 165 deletions(-) create mode 100644 src/Shared/DTOs/SelectedColumnsModalDto.fs diff --git a/src/Client/MainComponents/Widgets.fs b/src/Client/MainComponents/Widgets.fs index 6cfd3fe9..452186df 100644 --- a/src/Client/MainComponents/Widgets.fs +++ b/src/Client/MainComponents/Widgets.fs @@ -5,6 +5,7 @@ open Feliz.DaisyUI open Browser.Types open LocalStorage.Widgets open Modals.Template +open Shared.DTOs.SelectedColumnsModalDto module private InitExtensions = @@ -226,12 +227,12 @@ type Widget = let insertContent() = [ Html.div [ - SelectiveTemplateFromDBModal.displaySelectedProtocolEle model selectedColumns setSelectedColumns dispatch + SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch) ] Html.div [ prop.style [style.maxHeight (length.px 350); style.overflow.auto] prop.children [ - SelectiveTemplateFromDBModal.displaySelectedProtocolEle model selectedColumns setSelectedColumns dispatch + SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch) ] ] ] diff --git a/src/Client/Modals/ModalElements.fs b/src/Client/Modals/ModalElements.fs index 5bb525e9..3dcde99a 100644 --- a/src/Client/Modals/ModalElements.fs +++ b/src/Client/Modals/ModalElements.fs @@ -5,11 +5,12 @@ open Feliz.DaisyUI open Model open Messages open Shared +open Shared.DTOs.SelectedColumnsModalDto open ARCtrl open JsonImport open Components - +open Fable.React.Helpers type ModalElements = @@ -72,7 +73,7 @@ type ModalElements = ] ] - static member BoxWithChildren(children: ReactElement list, boxClass: string, ?title: string, ?icon: string, ?className: string list) = + static member BoxWithChildren(children: ReactElement list, ?title: string, ?icon: string, ?className: string list) = Html.div [ prop.className [ "rounded shadow p-2 flex flex-col gap-2 border" @@ -81,7 +82,7 @@ type ModalElements = ] prop.children [ Html.h3 [ - prop.className boxClass + prop.className "font-semibold gap-2 flex flex-row items-center" if icon.IsSome || title.IsSome then prop.children [ if icon.IsSome then @@ -94,13 +95,85 @@ type ModalElements = ] ] - static member ImportRadioPlugins(importType: TableJoinOptions, radioData: (TableJoinOptions * string)[], setImportType: TableJoinOptions -> unit) = + static member SelectorButton<'a when 'a : equality> (targetselector: 'a, selector: 'a, setSelector: 'a -> unit, ?isDisabled) = + Daisy.button.button [ + join.item + if isDisabled.IsSome then + prop.disabled isDisabled.Value + prop.style [style.flexGrow 1] + if (targetselector = selector) then + button.primary + prop.onClick (fun _ -> setSelector targetselector) + prop.text (string targetselector) + ] + + static member RadioPluginsBox(boxName, icon, importType: TableJoinOptions, radioData: (TableJoinOptions * string)[], setImportType: TableJoinOptions -> unit) = let myradio(target: TableJoinOptions, txt: string) = let isChecked = importType = target ModalElements.RadioPlugin("importType", txt, isChecked, fun (b: bool) -> if b then setImportType target) - ModalElements.Box ("Import Type", "fa-solid fa-cog", React.fragment [ + ModalElements.Box (boxName, icon, React.fragment [ Html.div [ for i in 0..radioData.Length-1 do myradio(radioData.[i]) ] ]) + + static member checkBox(columns: CompositeColumn [], index, selectionInformation: SelectedColumns, setSelectedColumns: SelectedColumns -> unit) = + Html.div [ + prop.style [style.display.flex; style.justifyContent.center] + prop.children [ + Daisy.checkbox [ + prop.type'.checkbox + prop.isChecked + (if selectionInformation.Columns.Length > 0 then + selectionInformation.Columns.[index] + else true) + prop.onChange (fun (b: bool) -> + if columns.Length > 0 then + let selectedData = selectionInformation.Columns + selectedData.[index] <- b + {selectionInformation with Columns = selectedData} |> setSelectedColumns) + ] + ] + ] + + static member TableWithImportColumnCheckboxes(table: ArcTable, ?selectionInformation: SelectedColumns, ?setSelectedColumns: SelectedColumns -> unit) = + let columns = table.Columns + let displayCheckBox = + //Determine whether to display checkboxes or not + selectionInformation.IsSome && setSelectedColumns.IsSome + Daisy.table [ + prop.children [ + Html.thead [ + Html.tr [ + for i in 0..columns.Length-1 do + Html.th [ + Html.label [ + prop.className "join flex flex-row centered gap-2" + prop.children [ + if displayCheckBox then ModalElements.checkBox(columns, i, selectionInformation.Value, setSelectedColumns.Value) + Html.text (columns.[i].Header.ToString()) + Html.div [ + prop.onClick (fun e -> + if columns.Length > 0 && selectionInformation.IsSome then + let selectedData = selectionInformation.Value.Columns + selectedData.[i] <- not selectedData.[i] + {selectionInformation.Value with Columns = selectedData} |> setSelectedColumns.Value) + ] + ] + ] + ] + ] + ] + + Html.tbody [ + for ri in 0 .. (table.RowCount-1) do + let row = table.GetRow(ri, true) + Html.tr [ + for c in row do + Html.td (c.ToString()) + ] + ] + ] + ] + diff --git a/src/Client/Modals/SelectiveImportModal.fs b/src/Client/Modals/SelectiveImportModal.fs index dd70ee28..4b2582cf 100644 --- a/src/Client/Modals/SelectiveImportModal.fs +++ b/src/Client/Modals/SelectiveImportModal.fs @@ -5,6 +5,7 @@ open Feliz.DaisyUI open Model open Messages open Shared +open Shared.DTOs.SelectedColumnsModalDto open ARCtrl open JsonImport @@ -45,7 +46,7 @@ type SelectiveImportModal = ) [] - static member private TableImport(index: int, table0: ArcTable, state: SelectiveImportModalState, addTableImport: int -> bool -> unit, rmvTableImport: int -> unit) = + static member private TableImport(index: int, table0: ArcTable, state: SelectiveImportModalState, addTableImport: int -> bool -> unit, rmvTableImport: int -> unit, selectedColumns, setSelectedColumns) = let name = table0.Name let radioGroup = "radioGroup_" + name let import = state.ImportTables |> List.tryFind (fun it -> it.Index = index) @@ -72,36 +73,21 @@ type SelectiveImportModal = Daisy.collapse [ Html.input [prop.type'.checkbox; prop.className "min-h-0 h-5"] Daisy.collapseTitle [ - prop.className "p-1 min-h-0 h-5 text-sm" - prop.text "Preview Table" + prop.className "p-1 min-h-0 h-5 text-sm" + prop.text (if isActive then "Select Columns" else "Preview Table") ] Daisy.collapseContent [ prop.className "overflow-x-auto" prop.children [ - Daisy.table [ - table.xs - prop.children [ - Html.thead [ - Html.tr [ - for c in table0.Headers do - Html.th (c.ToString()) - ] - ] - Html.tbody [ - for ri in 0 .. (table0.RowCount-1) do - let row = table0.GetRow(ri, true) - Html.tr [ - for c in row do - Html.td (c.ToString()) - ] - ] - ] - ] + if isActive then + ModalElements.TableWithImportColumnCheckboxes(table0, selectedColumns, setSelectedColumns) + else + ModalElements.TableWithImportColumnCheckboxes(table0) ] ] - ]], - className = [if isActive then "!bg-primary !text-primary-content"] - ) + ] + ], + className = [if isActive then "!bg-primary !text-primary-content"]) [] static member Main(import: ArcFiles, dispatch, rmv) = @@ -116,17 +102,21 @@ type SelectiveImportModal = if b then { state with - ImportMetadata = true; - ImportTables = [ for ti in 0 .. tables.Count-1 do {ImportTable.Index = ti; ImportTable.FullImport = true}] + ImportMetadata = true; + ImportTables = [for ti in 0 .. tables.Count-1 do {ImportTable.Index = ti; ImportTable.FullImport = true}] } |> setState else SelectiveImportModalState.init() |> setState - let addTableImport = fun (i:int) (fullImport: bool) -> + let addTableImport = fun (i: int) (fullImport: bool) -> let newImportTable: ImportTable = {Index = i; FullImport = fullImport} let newImportTables = newImportTable::state.ImportTables |> List.distinct {state with ImportTables = newImportTables} |> setState let rmvTableImport = fun i -> {state with ImportTables = state.ImportTables |> List.filter (fun it -> it.Index <> i)} |> setState + let selectedColumns = + tables + |> Array.ofSeq + |> Array.map (fun t -> React.useState(SelectedColumns.init t.Columns.Length)) Daisy.modal.div [ modal.active prop.children [ @@ -141,25 +131,28 @@ type SelectiveImportModal = Components.DeleteButton(props=[prop.onClick rmv]) ] ] - ModalElements.ImportRadioPlugins( + ModalElements.RadioPluginsBox( + "Import Type", + "fa-solid fa-cog", state.ImportType, [| - ARCtrl.TableJoinOptions.Headers, " Column Headers"; - ARCtrl.TableJoinOptions.WithUnit, " ..With Units"; + ARCtrl.TableJoinOptions.Headers, " Column Headers"; + ARCtrl.TableJoinOptions.WithUnit, " ..With Units"; ARCtrl.TableJoinOptions.WithValues, " ..With Values"; |], - fun it -> {state with ImportType = it} |> setState) + fun importType -> {state with ImportType = importType} |> setState) SelectiveImportModal.MetadataImport(state.ImportMetadata, setMetadataImport, disArcfile) for ti in 0 .. (tables.Count-1) do let t = tables.[ti] - SelectiveImportModal.TableImport(ti, t, state, addTableImport, rmvTableImport) + let selectedColumns, setSelectedColumns = fst selectedColumns.[ti], snd selectedColumns.[ti] + SelectiveImportModal.TableImport(ti, t, state, addTableImport, rmvTableImport, selectedColumns, setSelectedColumns) Daisy.cardActions [ Daisy.button.button [ button.info prop.style [style.marginLeft length.auto] prop.text "Submit" prop.onClick(fun e -> - {| importState = state; importedFile = import|} |> SpreadsheetInterface.ImportJson |> InterfaceMsg |> dispatch + {| importState = state; importedFile = import; selectedColumns = selectedColumns |} |> SpreadsheetInterface.ImportJson |> InterfaceMsg |> dispatch rmv e ) ] diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Modals/SelectiveTemplateFromDB.fs index fc3bfb97..9a7c003e 100644 --- a/src/Client/Modals/SelectiveTemplateFromDB.fs +++ b/src/Client/Modals/SelectiveTemplateFromDB.fs @@ -5,6 +5,7 @@ open Feliz.DaisyUI open Model open Messages open Shared +open Shared.DTOs.SelectedColumnsModalDto open ARCtrl open JsonImport @@ -13,16 +14,7 @@ open Components open Modals open Modals.ModalElements -type SelectedColumns = { - Columns: bool [] -} -with - static member init(length) = - { - Columns = Array.init length (fun _ -> true) - } - -type SelectiveTemplateFromDBModal = +type SelectiveTemplateFromDBModal = static member private LogicContainer (children: ReactElement list) = Html.div [ @@ -31,60 +23,27 @@ type SelectiveTemplateFromDBModal = ] [] - static member displaySelectedProtocolEle (model: Model) (selectionInformation:SelectedColumns) (setSelectedColumns:SelectedColumns -> unit) dispatch = + static member displaySelectedProtocolElements (model: Model, selectionInformation: SelectedColumns, setSelectedColumns: SelectedColumns -> unit, dispatch, ?hasIcon: bool) = + let hasIcon = defaultArg hasIcon true Html.div [ prop.style [style.overflowX.auto; style.marginBottom (length.rem 1)] prop.children [ - Daisy.table [ - prop.children [ - Html.thead [ - Html.tr [ - Html.th "Selection" - Html.th "Column" - Html.th "Column TAN" - ] - ] - Html.tbody [ - let length = - if model.ProtocolState.TemplateSelected.IsSome then model.ProtocolState.TemplateSelected.Value.Table.Columns.Length-1 - else 0 - for i in 0..length do - let column = model.ProtocolState.TemplateSelected.Value.Table.Columns.[i] - yield - Html.tr [ - Html.div [ - prop.style [style.display.flex; style.justifyContent.center] - prop.children [ - Daisy.checkbox [ - prop.type'.checkbox - prop.isChecked - (if selectionInformation.Columns.Length > 0 then - selectionInformation.Columns.[i] - else true) - prop.onChange (fun (b: bool) -> - if selectionInformation.Columns.Length > 0 then - let selectedData = selectionInformation.Columns - selectedData.[i] <- b - {selectionInformation with Columns = selectedData} |> setSelectedColumns) - ] - ] - ] - Html.td (column.Header.ToString()) - Html.td (if column.Header.IsTermColumn then column.Header.ToTerm().TermAccessionShort else "-") - ] - ] - ] - ] + if model.ProtocolState.TemplateSelected.IsSome then + if hasIcon then + Html.i [prop.className "fa-solid fa-cog"] + Html.span $"Template: {model.ProtocolState.TemplateSelected.Value.Name}" + if model.ProtocolState.TemplateSelected.IsSome then + ModalElements.TableWithImportColumnCheckboxes(model.ProtocolState.TemplateSelected.Value.Table, selectionInformation, setSelectedColumns) ] ] - static member addFromDBToTableButton (model: Model) selectionInformation dispatch = + static member addFromDBToTableButton (model: Model) selectionInformation importType dispatch = let addTemplate (templatePot: Template option, selectedColumns) = if model.ProtocolState.TemplateSelected.IsNone then failwith "No template selected!" if templatePot.IsSome then let table = templatePot.Value.Table - SpreadsheetInterface.AddTemplate(table, selectedColumns) |> InterfaceMsg |> dispatch + SpreadsheetInterface.AddTemplate(table, selectedColumns, importType) |> InterfaceMsg |> dispatch Html.div [ prop.className "join flex flex-row justify-center gap-2" prop.children [ @@ -106,26 +65,32 @@ type SelectiveTemplateFromDBModal = model.ProtocolState.TemplateSelected.Value.Table.Columns.Length else 0 let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init length) - ModalElements.BoxWithChildren( - [ - Html.div [ - Daisy.button.button [ - prop.onClick(fun _ -> UpdateModel {model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch} |> dispatch) - button.primary - button.block - prop.text "Browse database" - ] - ] - Html.div [ - SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns dispatch + let importTypeState, setImportTypeState = React.useState(SelectiveImportModalState.init) + Html.div [ + Html.div [ + Daisy.button.button [ + prop.onClick(fun _ -> UpdateModel { model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch } |> dispatch) + button.primary + button.block + prop.text "Browse database" ] if model.ProtocolState.TemplateSelected.IsSome then - Html.div [ - SelectiveTemplateFromDBModal.displaySelectedProtocolEle model selectedColumns setSelectedColumns dispatch - ] - Html.div [ - SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns dispatch + SelectiveTemplateFromDBModal.LogicContainer [ + Html.div [ + SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch, false) + ModalElements.RadioPluginsBox( + "Import Type", + "fa-solid fa-cog", + importTypeState.ImportType, + [| + ARCtrl.TableJoinOptions.Headers, " Column Headers"; + ARCtrl.TableJoinOptions.WithUnit, " ..With Units"; + ARCtrl.TableJoinOptions.WithValues, " ..With Values"; + |], + fun importType -> {importTypeState with ImportType = importType} |> setImportTypeState + ) + ] ] - ], - "relative flex p-4 animated-border shadow-md gap-4 flex-col" - ) + SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns importTypeState dispatch + ] + ] diff --git a/src/Client/OfficeInterop/OfficeInterop.fs b/src/Client/OfficeInterop/OfficeInterop.fs index 30ce67e5..e7af6c86 100644 --- a/src/Client/OfficeInterop/OfficeInterop.fs +++ b/src/Client/OfficeInterop/OfficeInterop.fs @@ -687,6 +687,16 @@ module AnnotationTable = |> Array.ofSeq |> Array.map (fun header -> header.ToString()) + /// + /// Get headers from range + /// + /// + let getBody (range: ResizeArray>) = + range + |> Array.ofSeq + |> (fun items -> items.[1..]) + |> ResizeArray + /// /// Try find annotation table in active worksheet and parse to ArcTable /// @@ -920,6 +930,39 @@ let addCompositeColumn (excelTable: Table) (arcTable: ArcTable) (newColumn: Comp loggingList +let selectiveTablePrepare (activeTable: ArcTable) (toJoinTable: ArcTable) (removeColumns: int list): ArcTable = + // Remove existing columns + let mutable columnsToRemove = removeColumns + // find duplicate columns + let tablecopy = toJoinTable.Copy() + for header in activeTable.Headers do + let containsAtIndex = tablecopy.Headers |> Seq.tryFindIndex (fun h -> h = header) + if containsAtIndex.IsSome then + columnsToRemove <- containsAtIndex.Value::columnsToRemove + + //Remove duplicates because unselected and already existing columns can overlap + let columnsToRemove = columnsToRemove |> Set.ofList |> Set.toList + + tablecopy.RemoveColumns (Array.ofList columnsToRemove) + tablecopy.IteriColumns(fun i c0 -> + let c1 = {c0 with Cells = tablecopy.Columns.[i].Cells} + let c2 = + if c1.Header.isInput then + match activeTable.TryGetInputColumn() with + | Some ic -> + {c1 with Cells = ic.Cells} + | _ -> c1 + elif c1.Header.isOutput then + match activeTable.TryGetOutputColumn() with + | Some oc -> + {c1 with Cells = oc.Cells} + | _ -> c1 + else + c1 + tablecopy.UpdateColumn(i, c2.Header, c2.Cells) + ) + tablecopy + /// /// Prepare the given table to be joined with the currently active annotation table /// @@ -965,21 +1008,16 @@ let prepareTemplateInMemory (table: Table) (tableToAdd: ArcTable) (selectedColum /// /// /// -let joinTable (tableToAdd: ArcTable, selectedColumns:bool [], options: TableJoinOptions option) = +let joinTable (tableToAdd: ArcTable, selectedColumns: bool [], options: TableJoinOptions option) = Excel.run(fun context -> promise { - - let columns = tableToAdd.Columns - - columns - //When a name is available get the annotation and arctable for easy access of indices and value adaption //Annotation table enables a easy way to adapt the table, updating existing and adding new columns let! result = AnnotationTable.tryGetActive context match result with | Some excelTable -> - let! (tableToAdd: ArcTable, index: int option) = prepareTemplateInMemory excelTable tableToAdd selectedColumns context + let! (refinedTableToAdd: ArcTable, index: int option) = prepareTemplateInMemory excelTable tableToAdd selectedColumns context //Arctable enables a fast check for the existence of input- and output-columns and their indices let! arcTableRes = ArcTable.fromExcelTable(excelTable, context) @@ -987,11 +1025,11 @@ let joinTable (tableToAdd: ArcTable, selectedColumns:bool [], options: TableJoin //When both tables could be accessed succesfully then check what kind of column shall be added an whether it is already there or not match arcTableRes with | Result.Ok arcTable -> - arcTable.Join(tableToAdd, ?index=index, ?joinOptions=options, forceReplace=true) + arcTable.Join(refinedTableToAdd, ?index=index, ?joinOptions=options, forceReplace=true) let newTableRange = excelTable.getRange() - let _ = newTableRange.load(propertyNames = U2.Case2 (ResizeArray["rowCount";])) + let _ = newTableRange.load(propertyNames = U2.Case2 (ResizeArray["rowCount"])) do! context.sync().``then``(fun _ -> excelTable.delete() @@ -999,14 +1037,16 @@ let joinTable (tableToAdd: ArcTable, selectedColumns:bool [], options: TableJoin let! (newTable, _) = AnnotationTable.createAtRange(false, false, newTableRange, context) - let _ = newTable.load(propertyNames = U2.Case2 (ResizeArray["name"; "values"; "columns";])) + let _ = newTable.load(propertyNames = U2.Case2 (ResizeArray["name"; "values"; "columns"])) + + let tableSeqs = arcTable.ToStringSeqs() do! context.sync().``then``(fun _ -> newTable.name <- excelTable.name let headerNames = - let names = AnnotationTable.getHeaders (arcTable.ToStringSeqs()) + let names = AnnotationTable.getHeaders tableSeqs names |> Array.map (fun name -> extendName names name) @@ -1035,9 +1075,12 @@ let joinTable (tableToAdd: ArcTable, selectedColumns:bool [], options: TableJoin do! context.sync() + let bodyValues = AnnotationTable.getBody tableSeqs + newBodyRange.values <- bodyValues + do! AnnotationTable.format(newTable, context, true) - return [InteropLogging.Msg.create InteropLogging.Warning $"Joined template {tableToAdd.Name} to table {excelTable.name}!"] + return [InteropLogging.Msg.create InteropLogging.Warning $"Joined template {refinedTableToAdd.Name} to table {excelTable.name}!"] | Result.Error _ -> return [InteropLogging.Msg.create InteropLogging.Error "No arc table could be created! This should not happen at this stage! Please report this as a bug to the developers.!"] | None -> return [InteropLogging.NoActiveTableMsg] diff --git a/src/Client/Pages/ProtocolTemplates/ProtocolView.fs b/src/Client/Pages/ProtocolTemplates/ProtocolView.fs index a65ee6a7..bf28532e 100644 --- a/src/Client/Pages/ProtocolTemplates/ProtocolView.fs +++ b/src/Client/Pages/ProtocolTemplates/ProtocolView.fs @@ -27,7 +27,6 @@ type Templates = SidebarComponents.SidebarLayout.Container [ SidebarComponents.SidebarLayout.Header "Templates" - SidebarComponents.SidebarLayout.Description (Html.p [ Html.b "Search the database for templates." Html.text " The building blocks from these templates can be inserted into the Swate table. " diff --git a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs index 8c81319d..524cbf24 100644 --- a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs +++ b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs @@ -19,6 +19,8 @@ open Shared open ARCtrl open Fable.Core.JsInterop +open Modals.ModalElements + type private TemplateFromFileState = { /// User select type to upload FileType: ArcFilesDiscriminate @@ -80,18 +82,6 @@ type TemplateFromFile = ) ] - static member private SelectorButton<'a when 'a : equality> (targetselector: 'a, selector: 'a, setSelector: 'a -> unit, ?isDisabled) = - Daisy.button.button [ - join.item - if isDisabled.IsSome then - prop.disabled isDisabled.Value - prop.style [style.flexGrow 1] - if (targetselector = selector) then - button.primary - prop.onClick (fun _ -> setSelector targetselector) - prop.text (string targetselector) - ] - [] static member Main(model: Model, dispatch) = let state, setState = React.useState(TemplateFromFileState.init) @@ -116,13 +106,17 @@ type TemplateFromFile = Modals.Import.SelectiveImportModal.Main (af, dispatch, rmv = (fun _ -> TemplateFromFileState.init() |> setState)) | None -> Html.none Html.div [ + SidebarComponents.SidebarLayout.Description (Html.p [ + Html.b "Import JSON files." + Html.text " You can use \"Json Export\" to create these files from existing Swate tables. " + ]) Daisy.join [ prop.className "w-full" prop.children [ - JsonExportFormat.ROCrate |> fun jef -> TemplateFromFile.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef) - JsonExportFormat.ISA |> fun jef -> TemplateFromFile.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef) - JsonExportFormat.ARCtrl |> fun jef -> TemplateFromFile.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef) - JsonExportFormat.ARCtrlCompressed |> fun jef -> TemplateFromFile.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef) + JsonExportFormat.ROCrate |> fun jef -> ModalElements.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef) + JsonExportFormat.ISA |> fun jef -> ModalElements.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef) + JsonExportFormat.ARCtrl |> fun jef -> ModalElements.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef) + JsonExportFormat.ARCtrlCompressed |> fun jef -> ModalElements.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef) ] ] ] @@ -131,10 +125,10 @@ type TemplateFromFile = Daisy.join [ prop.className "w-full" prop.children [ - ArcFilesDiscriminate.Assay |> fun ft -> TemplateFromFile.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft) - ArcFilesDiscriminate.Study |> fun ft -> TemplateFromFile.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft) - ArcFilesDiscriminate.Investigation |> fun ft -> TemplateFromFile.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft) - ArcFilesDiscriminate.Template |> fun ft -> TemplateFromFile.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft) + ArcFilesDiscriminate.Assay |> fun ft -> ModalElements.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft) + ArcFilesDiscriminate.Study |> fun ft -> ModalElements.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft) + ArcFilesDiscriminate.Investigation |> fun ft -> ModalElements.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft) + ArcFilesDiscriminate.Template |> fun ft -> ModalElements.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft) ] ] ] diff --git a/src/Client/States/OfficeInteropState.fs b/src/Client/States/OfficeInteropState.fs index 1f8bf8d6..e509900a 100644 --- a/src/Client/States/OfficeInteropState.fs +++ b/src/Client/States/OfficeInteropState.fs @@ -31,7 +31,7 @@ type Msg = | ValidateBuildingBlock | AddAnnotationBlock of CompositeColumn | AddAnnotationBlocks of CompositeColumn [] //* OfficeInterop.Types.Xml.ValidationTypes.TableValidation option - | AddTemplate of ArcTable * bool[] + | AddTemplate of ArcTable * bool[] * SelectiveImportModalState | JoinTable of ArcTable * options: TableJoinOptions option | RemoveBuildingBlock | UpdateUnitForCells diff --git a/src/Client/States/SpreadsheetInterface.fs b/src/Client/States/SpreadsheetInterface.fs index 968e1aef..24186201 100644 --- a/src/Client/States/SpreadsheetInterface.fs +++ b/src/Client/States/SpreadsheetInterface.fs @@ -1,6 +1,7 @@ namespace SpreadsheetInterface open Shared +open Shared.DTOs.SelectedColumnsModalDto open ARCtrl open JsonImport @@ -17,14 +18,14 @@ type Msg = | AddAnnotationBlocks of CompositeColumn [] | AddDataAnnotation of {| fragmentSelectors: string []; fileName: string; fileType: string; targetColumn: DataAnnotator.TargetColumn |} /// This function will do preprocessing on the table to join -| AddTemplate of ArcTable * bool[] +| AddTemplate of ArcTable * bool[] * SelectiveImportModalState | JoinTable of ArcTable * columnIndex: int option * options: TableJoinOptions option | UpdateArcFile of ArcFiles /// Inserts TermMinimal to selected fields of one column | InsertOntologyAnnotation of OntologyAnnotation | InsertFileNames of string list | ImportXlsx of byte [] -| ImportJson of {|importState: SelectiveImportModalState; importedFile: ArcFiles|} +| ImportJson of {|importState: SelectiveImportModalState; importedFile: ArcFiles; selectedColumns: (SelectedColumns * (SelectedColumns -> unit))[]|} /// Starts chain to export active table to isa json | ExportJson of ArcFiles * JsonExportFormat | UpdateUnitForCells diff --git a/src/Client/Update/InterfaceUpdate.fs b/src/Client/Update/InterfaceUpdate.fs index 3008a332..e2966e57 100644 --- a/src/Client/Update/InterfaceUpdate.fs +++ b/src/Client/Update/InterfaceUpdate.fs @@ -164,10 +164,10 @@ module Interface = model, cmd | _ -> failwith "not implemented" - | AddTemplate (table, selectedColumns) -> + | AddTemplate (table, selectedColumns, importType) -> match host with | Some Swatehost.Excel -> - let cmd = OfficeInterop.AddTemplate (table, selectedColumns) |> OfficeInteropMsg |> Cmd.ofMsg + let cmd = OfficeInterop.AddTemplate (table, selectedColumns, importType) |> OfficeInteropMsg |> Cmd.ofMsg model, cmd | Some Swatehost.Browser | Some Swatehost.ARCitect -> let cmd = Spreadsheet.AddTemplate (table, selectedColumns) |> SpreadsheetMsg |> Cmd.ofMsg @@ -201,6 +201,9 @@ module Interface = model, cmd | _ -> failwith "not implemented" | ImportJson data -> + let selectedColumns = + data.selectedColumns + |> Array.map (fun (sc, _) -> sc) match host with | Some Swatehost.Excel -> /// In Excel we must get the current information from worksheets and update them with the imported information @@ -208,7 +211,7 @@ module Interface = promise { match data.importState.ImportMetadata with | true -> // full import, does not require additional information - return UpdateUtil.JsonImportHelper.updateWithMetadata data.importedFile data.importState + return UpdateUtil.JsonImportHelper.updateWithMetadata data.importedFile data.importState selectedColumns | false -> // partial import, requires additional information let! arcfile = OfficeInterop.Core.Main.tryParseToArcFile() let arcfileOpt = arcfile |> Result.toOption @@ -217,9 +220,9 @@ module Interface = ) let activeTableIndex = match arcfileOpt, activeTable with - | Some arcfile, Ok activeTable -> arcfile.Tables() |> Seq.tryFindIndex (fun x -> x = activeTable) + | Some arcfile, Ok activeTable -> arcfile.Tables() |> Seq.tryFindIndex (fun table -> table = activeTable) | _ -> None - return UpdateUtil.JsonImportHelper.updateTables data.importedFile data.importState activeTableIndex arcfileOpt + return UpdateUtil.JsonImportHelper.updateTables data.importedFile data.importState (*selectedColumns*) activeTableIndex arcfileOpt } let updateArcFile (arcFile: ArcFiles) = SpreadsheetInterface.UpdateArcFile arcFile |> InterfaceMsg let cmd = @@ -232,7 +235,7 @@ module Interface = | Some Swatehost.Browser | Some Swatehost.ARCitect -> let cmd = match data.importState.ImportMetadata with - | true -> UpdateUtil.JsonImportHelper.updateWithMetadata data.importedFile data.importState + | true -> UpdateUtil.JsonImportHelper.updateWithMetadata data.importedFile data.importState selectedColumns | false -> UpdateUtil.JsonImportHelper.updateTables data.importedFile data.importState model.SpreadsheetModel.ActiveView.TryTableIndex model.SpreadsheetModel.ArcFile |> SpreadsheetInterface.UpdateArcFile |> InterfaceMsg |> Cmd.ofMsg model, cmd diff --git a/src/Client/Update/OfficeInteropUpdate.fs b/src/Client/Update/OfficeInteropUpdate.fs index d013e664..71be2cd5 100644 --- a/src/Client/Update/OfficeInteropUpdate.fs +++ b/src/Client/Update/OfficeInteropUpdate.fs @@ -76,11 +76,11 @@ module OfficeInterop = UpdateUtil.downloadFromString (jsonExport) state, model, Cmd.none - | AddTemplate (table, selectedColumns) -> + | AddTemplate (table, selectedColumns, importType) -> let cmd = Cmd.OfPromise.either OfficeInterop.Core.joinTable - (table, selectedColumns, Some ARCtrl.TableJoinOptions.WithValues) + (table, selectedColumns, Some importType.ImportType) (curry GenericInteropLogs Cmd.none >> DevMsg) (curry GenericError Cmd.none >> DevMsg) state, model, cmd diff --git a/src/Client/Update/UpdateUtil.fs b/src/Client/Update/UpdateUtil.fs index 6295fa63..e2efdeba 100644 --- a/src/Client/Update/UpdateUtil.fs +++ b/src/Client/Update/UpdateUtil.fs @@ -2,6 +2,7 @@ module Update.UpdateUtil open ARCtrl open Shared +open Shared.DTOs.SelectedColumnsModalDto open Fable.Remoting.Client let download(filename, bytes:byte []) = bytes.SaveFileAs(filename) @@ -15,21 +16,34 @@ module JsonImportHelper = open ARCtrl open JsonImport - let updateWithMetadata (uploadedFile: ArcFiles) (state: SelectiveImportModalState) = + let updateWithMetadata (uploadedFile: ArcFiles) (state: SelectiveImportModalState) (selectedColumns: SelectedColumns []) = if not state.ImportMetadata then failwith "Metadata must be imported" /// This updates the existing tables based on import config (joinOptions) let createUpdatedTables (arcTables: ResizeArray) = [ - for it in state.ImportTables do - let sourceTable = arcTables.[it.Index] + for importTable in state.ImportTables do + + let selectedColumn = selectedColumns.[importTable.Index] + let selectedColumnIndices = + selectedColumn.Columns + |> Array.mapi (fun i item -> if item = false then Some i else None) + |> Array.choose (fun x -> x) + |> List.ofArray + + let sourceTable = arcTables.[importTable.Index] let appliedTable = ArcTable.init(sourceTable.Name) - appliedTable.Join(sourceTable, joinOptions=state.ImportType) + + let finalTable = Table.selectiveTablePrepare appliedTable sourceTable selectedColumnIndices + let values = + sourceTable.Columns + |> Array.map (fun item -> item.Cells |> Array.map (fun itemi -> itemi.ToString())) + appliedTable.Join(finalTable, joinOptions=state.ImportType) appliedTable ] |> ResizeArray let arcFile = match uploadedFile with - | Assay a as arcFile-> + | Assay a as arcFile -> let tables = createUpdatedTables a.Tables a.Tables <- tables arcFile diff --git a/src/Shared/ARCtrl.Helper.fs b/src/Shared/ARCtrl.Helper.fs index 83999645..c04bddf4 100644 --- a/src/Shared/ARCtrl.Helper.fs +++ b/src/Shared/ARCtrl.Helper.fs @@ -93,7 +93,7 @@ module Table = /// /// The active/current table /// The new table, which will be added to the existing one. - let selectiveTablePrepare (activeTable: ArcTable) (toJoinTable: ArcTable) (removeColumns:int list): ArcTable = + let selectiveTablePrepare (activeTable: ArcTable) (toJoinTable: ArcTable) (removeColumns: int list): ArcTable = // Remove existing columns let mutable columnsToRemove = removeColumns // find duplicate columns @@ -108,7 +108,7 @@ module Table = tablecopy.RemoveColumns (Array.ofList columnsToRemove) tablecopy.IteriColumns(fun i c0 -> - let c1 = {c0 with Cells = [||]} + let c1 = {c0 with Cells = tablecopy.Columns.[i].Cells} let c2 = if c1.Header.isInput then match activeTable.TryGetInputColumn() with diff --git a/src/Shared/DTOs/SelectedColumnsModalDto.fs b/src/Shared/DTOs/SelectedColumnsModalDto.fs new file mode 100644 index 00000000..e12a11bb --- /dev/null +++ b/src/Shared/DTOs/SelectedColumnsModalDto.fs @@ -0,0 +1,10 @@ +module Shared.DTOs.SelectedColumnsModalDto + +type SelectedColumns = { + Columns: bool [] +} +with + static member init(length) = + { + Columns = Array.init length (fun _ -> true) + } \ No newline at end of file diff --git a/src/Shared/Shared.fsproj b/src/Shared/Shared.fsproj index a426ba67..9d397f96 100644 --- a/src/Shared/Shared.fsproj +++ b/src/Shared/Shared.fsproj @@ -5,6 +5,7 @@ + From c2064a5a78b023302cca0ae64a64c3cd7ae1cf99 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Tue, 3 Dec 2024 10:57:22 +0100 Subject: [PATCH 08/18] update layout --- src/Client/Modals/SelectiveTemplateFromDB.fs | 13 +++++++++---- .../Pages/ProtocolTemplates/TemplateFromFile.fs | 4 ---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Modals/SelectiveTemplateFromDB.fs index 9a7c003e..f1fbc11e 100644 --- a/src/Client/Modals/SelectiveTemplateFromDB.fs +++ b/src/Client/Modals/SelectiveTemplateFromDB.fs @@ -66,7 +66,7 @@ type SelectiveTemplateFromDBModal = else 0 let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init length) let importTypeState, setImportTypeState = React.useState(SelectiveImportModalState.init) - Html.div [ + SelectiveTemplateFromDBModal.LogicContainer [ Html.div [ Daisy.button.button [ prop.onClick(fun _ -> UpdateModel { model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch } |> dispatch) @@ -75,9 +75,8 @@ type SelectiveTemplateFromDBModal = prop.text "Browse database" ] if model.ProtocolState.TemplateSelected.IsSome then - SelectiveTemplateFromDBModal.LogicContainer [ + Html.div [ Html.div [ - SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch, false) ModalElements.RadioPluginsBox( "Import Type", "fa-solid fa-cog", @@ -89,8 +88,14 @@ type SelectiveTemplateFromDBModal = |], fun importType -> {importTypeState with ImportType = importType} |> setImportTypeState ) + ModalElements.Box( + model.ProtocolState.TemplateSelected.Value.Name, + "", + SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch, false)) ] ] - SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns importTypeState dispatch + Html.div [ + SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns importTypeState dispatch + ] ] ] diff --git a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs index 524cbf24..efd3dd3f 100644 --- a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs +++ b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs @@ -106,10 +106,6 @@ type TemplateFromFile = Modals.Import.SelectiveImportModal.Main (af, dispatch, rmv = (fun _ -> TemplateFromFileState.init() |> setState)) | None -> Html.none Html.div [ - SidebarComponents.SidebarLayout.Description (Html.p [ - Html.b "Import JSON files." - Html.text " You can use \"Json Export\" to create these files from existing Swate tables. " - ]) Daisy.join [ prop.className "w-full" prop.children [ From 4d11c9a2d5118da92980dddf926b6e72e9f55601 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Tue, 3 Dec 2024 11:07:40 +0100 Subject: [PATCH 09/18] Improve layout by adding gaps --- src/Client/Modals/SelectiveTemplateFromDB.fs | 55 +++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Modals/SelectiveTemplateFromDB.fs index f1fbc11e..c40ce431 100644 --- a/src/Client/Modals/SelectiveTemplateFromDB.fs +++ b/src/Client/Modals/SelectiveTemplateFromDB.fs @@ -22,6 +22,14 @@ type SelectiveTemplateFromDBModal = prop.children children ] + static member toProtocolSearchElement (model: Model) dispatch = + Daisy.button.button [ + prop.onClick(fun _ -> UpdateModel {model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch} |> dispatch) + button.primary + button.block + prop.text "Browse database" + ] + [] static member displaySelectedProtocolElements (model: Model, selectionInformation: SelectedColumns, setSelectedColumns: SelectedColumns -> unit, dispatch, ?hasIcon: bool) = let hasIcon = defaultArg hasIcon true @@ -68,34 +76,29 @@ type SelectiveTemplateFromDBModal = let importTypeState, setImportTypeState = React.useState(SelectiveImportModalState.init) SelectiveTemplateFromDBModal.LogicContainer [ Html.div [ - Daisy.button.button [ - prop.onClick(fun _ -> UpdateModel { model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch } |> dispatch) - button.primary - button.block - prop.text "Browse database" + SelectiveTemplateFromDBModal.toProtocolSearchElement model dispatch + ] + if model.ProtocolState.TemplateSelected.IsSome then + Html.div [ + ModalElements.RadioPluginsBox( + "Import Type", + "fa-solid fa-cog", + importTypeState.ImportType, + [| + ARCtrl.TableJoinOptions.Headers, " Column Headers"; + ARCtrl.TableJoinOptions.WithUnit, " ..With Units"; + ARCtrl.TableJoinOptions.WithValues, " ..With Values"; + |], + fun importType -> {importTypeState with ImportType = importType} |> setImportTypeState + ) ] - if model.ProtocolState.TemplateSelected.IsSome then - Html.div [ - Html.div [ - ModalElements.RadioPluginsBox( - "Import Type", - "fa-solid fa-cog", - importTypeState.ImportType, - [| - ARCtrl.TableJoinOptions.Headers, " Column Headers"; - ARCtrl.TableJoinOptions.WithUnit, " ..With Units"; - ARCtrl.TableJoinOptions.WithValues, " ..With Values"; - |], - fun importType -> {importTypeState with ImportType = importType} |> setImportTypeState - ) - ModalElements.Box( - model.ProtocolState.TemplateSelected.Value.Name, - "", - SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch, false)) - ] - ] Html.div [ - SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns importTypeState dispatch + ModalElements.Box( + model.ProtocolState.TemplateSelected.Value.Name, + "", + SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch, false)) ] + Html.div [ + SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns importTypeState dispatch ] ] From 2cf77988c569a84f8c6217d0cc0f7f07cf13c7c1 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Tue, 3 Dec 2024 11:56:07 +0100 Subject: [PATCH 10/18] Adapted Cehckbox height --- src/Client/Modals/ModalElements.fs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Client/Modals/ModalElements.fs b/src/Client/Modals/ModalElements.fs index 3dcde99a..2977473b 100644 --- a/src/Client/Modals/ModalElements.fs +++ b/src/Client/Modals/ModalElements.fs @@ -124,6 +124,9 @@ type ModalElements = prop.children [ Daisy.checkbox [ prop.type'.checkbox + prop.style [ + style.height(length.perc 100) + ] prop.isChecked (if selectionInformation.Columns.Length > 0 then selectionInformation.Columns.[index] @@ -151,7 +154,8 @@ type ModalElements = Html.label [ prop.className "join flex flex-row centered gap-2" prop.children [ - if displayCheckBox then ModalElements.checkBox(columns, i, selectionInformation.Value, setSelectedColumns.Value) + if displayCheckBox then + ModalElements.checkBox(columns, i, selectionInformation.Value, setSelectedColumns.Value) Html.text (columns.[i].Header.ToString()) Html.div [ prop.onClick (fun e -> From ace120f792b4ebd76c65ab0bde0c90aeb85a5f37 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Tue, 3 Dec 2024 15:05:05 +0100 Subject: [PATCH 11/18] Apply review changes --- src/Client/Client.fable-temp.csproj | 154 ------------------ src/Client/Modals/ModalElements.fs | 16 +- src/Client/Modals/SelectiveImportModal.fs | 7 +- src/Client/Modals/SelectiveTemplateFromDB.fs | 23 +-- .../Pages/BuildingBlock/BuildingBlockView.fs | 5 +- src/Client/SidebarComponents/LayoutHelper.fs | 15 +- 6 files changed, 28 insertions(+), 192 deletions(-) delete mode 100644 src/Client/Client.fable-temp.csproj diff --git a/src/Client/Client.fable-temp.csproj b/src/Client/Client.fable-temp.csproj deleted file mode 100644 index d1678662..00000000 --- a/src/Client/Client.fable-temp.csproj +++ /dev/null @@ -1,154 +0,0 @@ - - - - - net8.0 - FABLE_COMPILER - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Client/Modals/ModalElements.fs b/src/Client/Modals/ModalElements.fs index 2977473b..86622897 100644 --- a/src/Client/Modals/ModalElements.fs +++ b/src/Client/Modals/ModalElements.fs @@ -1,4 +1,4 @@ -module Modals.ModalElements +module Modals open Feliz open Feliz.DaisyUI @@ -14,6 +14,20 @@ open Fable.React.Helpers type ModalElements = + static member LogicContainer (children: ReactElement list) = + Html.div [ + // prop.className "border-l-4 border-transparent px-4 py-2 shadow-md" + // prop.style [ + // let rndVal = rnd.Next(30,70) + // let colorArr = [|NFDIColors.LightBlue.Lighter10; NFDIColors.Mint.Lighter10;|] + // style.custom("borderImageSlice", "1") + // style.custom("borderImageSource", $"linear-gradient({colorArr.[if order then 0 else 1]} {100-rndVal}%%, {colorArr.[if order then 1 else 0]})") + // order <- not order + // ] + prop.className "relative flex p-4 animated-border shadow-md gap-4 flex-col" //experimental + prop.children children + ] + static member Button(text: string, onClickAction, buttonInput, ?isDisabled: bool) = let isDisabled = defaultArg isDisabled false Daisy.button.a [ diff --git a/src/Client/Modals/SelectiveImportModal.fs b/src/Client/Modals/SelectiveImportModal.fs index 4b2582cf..c0166d94 100644 --- a/src/Client/Modals/SelectiveImportModal.fs +++ b/src/Client/Modals/SelectiveImportModal.fs @@ -1,4 +1,4 @@ -namespace Modals.Import +namespace Modals open Feliz open Feliz.DaisyUI @@ -11,9 +11,6 @@ open ARCtrl open JsonImport open Components -open Modals -open Modals.ModalElements - type SelectiveImportModal = static member private MetadataImport(isActive: bool, setActive: bool -> unit, disArcFile: ArcFilesDiscriminate) = @@ -90,7 +87,7 @@ type SelectiveImportModal = className = [if isActive then "!bg-primary !text-primary-content"]) [] - static member Main(import: ArcFiles, dispatch, rmv) = + static member Main (import: ArcFiles, dispatch, rmv) = let state, setState = React.useState(SelectiveImportModalState.init) let tables, disArcfile = match import with diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Modals/SelectiveTemplateFromDB.fs index c40ce431..13a4aeb3 100644 --- a/src/Client/Modals/SelectiveTemplateFromDB.fs +++ b/src/Client/Modals/SelectiveTemplateFromDB.fs @@ -1,4 +1,4 @@ -namespace Modals.Template +namespace Modals open Feliz open Feliz.DaisyUI @@ -11,18 +11,9 @@ open ARCtrl open JsonImport open Components -open Modals -open Modals.ModalElements - type SelectiveTemplateFromDBModal = - static member private LogicContainer (children: ReactElement list) = - Html.div [ - prop.className "relative flex p-4 animated-border shadow-md gap-4 flex-col" //experimental - prop.children children - ] - - static member toProtocolSearchElement (model: Model) dispatch = + static member ToProtocolSearchElement (model: Model) dispatch = Daisy.button.button [ prop.onClick(fun _ -> UpdateModel {model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch} |> dispatch) button.primary @@ -45,7 +36,7 @@ type SelectiveTemplateFromDBModal = ] ] - static member addFromDBToTableButton (model: Model) selectionInformation importType dispatch = + static member AddFromDBToTableButton (model: Model) selectionInformation importType dispatch = let addTemplate (templatePot: Template option, selectedColumns) = if model.ProtocolState.TemplateSelected.IsNone then failwith "No template selected!" @@ -67,16 +58,16 @@ type SelectiveTemplateFromDBModal = ] [] - static member Main(model: Model, dispatch) = + static member Main (model: Model, dispatch) = let length = if model.ProtocolState.TemplateSelected.IsSome then model.ProtocolState.TemplateSelected.Value.Table.Columns.Length else 0 let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init length) let importTypeState, setImportTypeState = React.useState(SelectiveImportModalState.init) - SelectiveTemplateFromDBModal.LogicContainer [ + ModalElements.LogicContainer [ Html.div [ - SelectiveTemplateFromDBModal.toProtocolSearchElement model dispatch + SelectiveTemplateFromDBModal.ToProtocolSearchElement model dispatch ] if model.ProtocolState.TemplateSelected.IsSome then Html.div [ @@ -99,6 +90,6 @@ type SelectiveTemplateFromDBModal = SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch, false)) ] Html.div [ - SelectiveTemplateFromDBModal.addFromDBToTableButton model selectedColumns importTypeState dispatch + SelectiveTemplateFromDBModal.AddFromDBToTableButton model selectedColumns importTypeState dispatch ] ] diff --git a/src/Client/Pages/BuildingBlock/BuildingBlockView.fs b/src/Client/Pages/BuildingBlock/BuildingBlockView.fs index bb4b0e15..f1caa011 100644 --- a/src/Client/Pages/BuildingBlock/BuildingBlockView.fs +++ b/src/Client/Pages/BuildingBlock/BuildingBlockView.fs @@ -4,6 +4,7 @@ open Model open Messages open Messages.BuildingBlock open Shared +open Modals open Elmish @@ -81,13 +82,13 @@ let addBuildingBlockComponent (model:Model) (dispatch:Messages.Msg -> unit) = // Input forms, etc related to add building block. SidebarComponents.SidebarLayout.Description "Add annotation building blocks (columns) to the annotation table." - SidebarComponents.SidebarLayout.LogicContainer [ + ModalElements.LogicContainer [ SearchComponent.Main model dispatch ] match model.PersistentStorageState.Host with | Some Swatehost.Excel -> Html.p "Convert existing Building Block." - SidebarComponents.SidebarLayout.LogicContainer [ + ModalElements.LogicContainer [ CellConvertComponent.Main () ] | _ -> Html.none diff --git a/src/Client/SidebarComponents/LayoutHelper.fs b/src/Client/SidebarComponents/LayoutHelper.fs index 692f5e89..c8fda28e 100644 --- a/src/Client/SidebarComponents/LayoutHelper.fs +++ b/src/Client/SidebarComponents/LayoutHelper.fs @@ -20,26 +20,13 @@ module private LayoutHelperAux = if v > 5 then false else true type SidebarLayout = + static member Container (children: ReactElement list) = Html.div [ prop.className "flex flex-col gap-2 py-4" prop.children children ] - static member LogicContainer (children: ReactElement list) = - Html.div [ - // prop.className "border-l-4 border-transparent px-4 py-2 shadow-md" - // prop.style [ - // let rndVal = rnd.Next(30,70) - // let colorArr = [|NFDIColors.LightBlue.Lighter10; NFDIColors.Mint.Lighter10;|] - // style.custom("borderImageSlice", "1") - // style.custom("borderImageSource", $"linear-gradient({colorArr.[if order then 0 else 1]} {100-rndVal}%%, {colorArr.[if order then 1 else 0]})") - // order <- not order - // ] - prop.className "relative flex p-4 animated-border shadow-md gap-4 flex-col" //experimental - prop.children children - ] - static member Header(txt: string) = Html.h3 [ prop.className "text-lg font-semibold" From 849b155c5d206e651e61339a49986b14b7405655 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Tue, 3 Dec 2024 15:13:19 +0100 Subject: [PATCH 12/18] Implemente review changes --- src/Client/Client.fsproj | 2 +- src/Client/Modals/ModalElements.fs | 75 ------------------- src/Client/Modals/SelectiveImportModal.fs | 4 +- src/Client/Modals/SelectiveTemplateFromDB.fs | 76 +++++++++++++++++++- 4 files changed, 79 insertions(+), 78 deletions(-) diff --git a/src/Client/Client.fsproj b/src/Client/Client.fsproj index 50e238c0..6daac2d3 100644 --- a/src/Client/Client.fsproj +++ b/src/Client/Client.fsproj @@ -52,8 +52,8 @@ - + diff --git a/src/Client/Modals/ModalElements.fs b/src/Client/Modals/ModalElements.fs index 86622897..217b9f00 100644 --- a/src/Client/Modals/ModalElements.fs +++ b/src/Client/Modals/ModalElements.fs @@ -120,78 +120,3 @@ type ModalElements = prop.onClick (fun _ -> setSelector targetselector) prop.text (string targetselector) ] - - static member RadioPluginsBox(boxName, icon, importType: TableJoinOptions, radioData: (TableJoinOptions * string)[], setImportType: TableJoinOptions -> unit) = - let myradio(target: TableJoinOptions, txt: string) = - let isChecked = importType = target - ModalElements.RadioPlugin("importType", txt, isChecked, fun (b: bool) -> if b then setImportType target) - ModalElements.Box (boxName, icon, React.fragment [ - Html.div [ - for i in 0..radioData.Length-1 do - myradio(radioData.[i]) - ] - ]) - - static member checkBox(columns: CompositeColumn [], index, selectionInformation: SelectedColumns, setSelectedColumns: SelectedColumns -> unit) = - Html.div [ - prop.style [style.display.flex; style.justifyContent.center] - prop.children [ - Daisy.checkbox [ - prop.type'.checkbox - prop.style [ - style.height(length.perc 100) - ] - prop.isChecked - (if selectionInformation.Columns.Length > 0 then - selectionInformation.Columns.[index] - else true) - prop.onChange (fun (b: bool) -> - if columns.Length > 0 then - let selectedData = selectionInformation.Columns - selectedData.[index] <- b - {selectionInformation with Columns = selectedData} |> setSelectedColumns) - ] - ] - ] - - static member TableWithImportColumnCheckboxes(table: ArcTable, ?selectionInformation: SelectedColumns, ?setSelectedColumns: SelectedColumns -> unit) = - let columns = table.Columns - let displayCheckBox = - //Determine whether to display checkboxes or not - selectionInformation.IsSome && setSelectedColumns.IsSome - Daisy.table [ - prop.children [ - Html.thead [ - Html.tr [ - for i in 0..columns.Length-1 do - Html.th [ - Html.label [ - prop.className "join flex flex-row centered gap-2" - prop.children [ - if displayCheckBox then - ModalElements.checkBox(columns, i, selectionInformation.Value, setSelectedColumns.Value) - Html.text (columns.[i].Header.ToString()) - Html.div [ - prop.onClick (fun e -> - if columns.Length > 0 && selectionInformation.IsSome then - let selectedData = selectionInformation.Value.Columns - selectedData.[i] <- not selectedData.[i] - {selectionInformation.Value with Columns = selectedData} |> setSelectedColumns.Value) - ] - ] - ] - ] - ] - ] - - Html.tbody [ - for ri in 0 .. (table.RowCount-1) do - let row = table.GetRow(ri, true) - Html.tr [ - for c in row do - Html.td (c.ToString()) - ] - ] - ] - ] - diff --git a/src/Client/Modals/SelectiveImportModal.fs b/src/Client/Modals/SelectiveImportModal.fs index c0166d94..609ef5cc 100644 --- a/src/Client/Modals/SelectiveImportModal.fs +++ b/src/Client/Modals/SelectiveImportModal.fs @@ -11,6 +11,8 @@ open ARCtrl open JsonImport open Components +open Modals + type SelectiveImportModal = static member private MetadataImport(isActive: bool, setActive: bool -> unit, disArcFile: ArcFilesDiscriminate) = @@ -128,7 +130,7 @@ type SelectiveImportModal = Components.DeleteButton(props=[prop.onClick rmv]) ] ] - ModalElements.RadioPluginsBox( + SelectiveTemplateFromDBModal.RadioPluginsBox( "Import Type", "fa-solid fa-cog", state.ImportType, diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Modals/SelectiveTemplateFromDB.fs index 13a4aeb3..f12429e2 100644 --- a/src/Client/Modals/SelectiveTemplateFromDB.fs +++ b/src/Client/Modals/SelectiveTemplateFromDB.fs @@ -13,6 +13,80 @@ open Components type SelectiveTemplateFromDBModal = + static member RadioPluginsBox(boxName, icon, importType: TableJoinOptions, radioData: (TableJoinOptions * string)[], setImportType: TableJoinOptions -> unit) = + let myradio(target: TableJoinOptions, txt: string) = + let isChecked = importType = target + ModalElements.RadioPlugin("importType", txt, isChecked, fun (b: bool) -> if b then setImportType target) + ModalElements.Box (boxName, icon, React.fragment [ + Html.div [ + for i in 0..radioData.Length-1 do + myradio(radioData.[i]) + ] + ]) + + static member CheckBoxForTableColumnSelection(columns: CompositeColumn [], index, selectionInformation: SelectedColumns, setSelectedColumns: SelectedColumns -> unit) = + Html.div [ + prop.style [style.display.flex; style.justifyContent.center] + prop.children [ + Daisy.checkbox [ + prop.type'.checkbox + prop.style [ + style.height(length.perc 100) + ] + prop.isChecked + (if selectionInformation.Columns.Length > 0 then + selectionInformation.Columns.[index] + else true) + prop.onChange (fun (b: bool) -> + if columns.Length > 0 then + let selectedData = selectionInformation.Columns + selectedData.[index] <- b + {selectionInformation with Columns = selectedData} |> setSelectedColumns) + ] + ] + ] + + static member TableWithImportColumnCheckboxes(table: ArcTable, ?selectionInformation: SelectedColumns, ?setSelectedColumns: SelectedColumns -> unit) = + let columns = table.Columns + let displayCheckBox = + //Determine whether to display checkboxes or not + selectionInformation.IsSome && setSelectedColumns.IsSome + Daisy.table [ + prop.children [ + Html.thead [ + Html.tr [ + for i in 0..columns.Length-1 do + Html.th [ + Html.label [ + prop.className "join flex flex-row centered gap-2" + prop.children [ + if displayCheckBox then + SelectiveTemplateFromDBModal.checkBox(columns, i, selectionInformation.Value, setSelectedColumns.Value) + Html.text (columns.[i].Header.ToString()) + Html.div [ + prop.onClick (fun e -> + if columns.Length > 0 && selectionInformation.IsSome then + let selectedData = selectionInformation.Value.Columns + selectedData.[i] <- not selectedData.[i] + {selectionInformation.Value with Columns = selectedData} |> setSelectedColumns.Value) + ] + ] + ] + ] + ] + ] + + Html.tbody [ + for ri in 0 .. (table.RowCount-1) do + let row = table.GetRow(ri, true) + Html.tr [ + for c in row do + Html.td (c.ToString()) + ] + ] + ] + ] + static member ToProtocolSearchElement (model: Model) dispatch = Daisy.button.button [ prop.onClick(fun _ -> UpdateModel {model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch} |> dispatch) @@ -71,7 +145,7 @@ type SelectiveTemplateFromDBModal = ] if model.ProtocolState.TemplateSelected.IsSome then Html.div [ - ModalElements.RadioPluginsBox( + SelectiveTemplateFromDBModal.RadioPluginsBox( "Import Type", "fa-solid fa-cog", importTypeState.ImportType, From 869ade6f9f5cde20024ca14a69db1c37c68a9acd Mon Sep 17 00:00:00 2001 From: patrick blume Date: Tue, 3 Dec 2024 15:14:46 +0100 Subject: [PATCH 13/18] Implemented review suggestions --- src/Client/Modals/SelectiveImportModal.fs | 4 ++-- src/Client/Modals/SelectiveTemplateFromDB.fs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Client/Modals/SelectiveImportModal.fs b/src/Client/Modals/SelectiveImportModal.fs index 609ef5cc..99d73a44 100644 --- a/src/Client/Modals/SelectiveImportModal.fs +++ b/src/Client/Modals/SelectiveImportModal.fs @@ -79,9 +79,9 @@ type SelectiveImportModal = prop.className "overflow-x-auto" prop.children [ if isActive then - ModalElements.TableWithImportColumnCheckboxes(table0, selectedColumns, setSelectedColumns) + SelectiveTemplateFromDBModal.TableWithImportColumnCheckboxes(table0, selectedColumns, setSelectedColumns) else - ModalElements.TableWithImportColumnCheckboxes(table0) + SelectiveTemplateFromDBModal.TableWithImportColumnCheckboxes(table0) ] ] ] diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Modals/SelectiveTemplateFromDB.fs index f12429e2..1baf811d 100644 --- a/src/Client/Modals/SelectiveTemplateFromDB.fs +++ b/src/Client/Modals/SelectiveTemplateFromDB.fs @@ -61,7 +61,7 @@ type SelectiveTemplateFromDBModal = prop.className "join flex flex-row centered gap-2" prop.children [ if displayCheckBox then - SelectiveTemplateFromDBModal.checkBox(columns, i, selectionInformation.Value, setSelectedColumns.Value) + SelectiveTemplateFromDBModal.CheckBoxForTableColumnSelection(columns, i, selectionInformation.Value, setSelectedColumns.Value) Html.text (columns.[i].Header.ToString()) Html.div [ prop.onClick (fun e -> @@ -106,7 +106,7 @@ type SelectiveTemplateFromDBModal = Html.i [prop.className "fa-solid fa-cog"] Html.span $"Template: {model.ProtocolState.TemplateSelected.Value.Name}" if model.ProtocolState.TemplateSelected.IsSome then - ModalElements.TableWithImportColumnCheckboxes(model.ProtocolState.TemplateSelected.Value.Table, selectionInformation, setSelectedColumns) + SelectiveTemplateFromDBModal.TableWithImportColumnCheckboxes(model.ProtocolState.TemplateSelected.Value.Table, selectionInformation, setSelectedColumns) ] ] From ca0a1d40654836e9df021b78d40037773989090c Mon Sep 17 00:00:00 2001 From: patrick blume Date: Tue, 3 Dec 2024 15:25:56 +0100 Subject: [PATCH 14/18] Implement review changes --- src/Client/Client.fsproj | 1 + src/Client/MainComponents/Widgets.fs | 4 ++-- src/Client/Modals/ModalElements.fs | 2 +- src/Client/Modals/ModalProvider.fs | 4 ++-- src/Client/Modals/SelectiveImportModal.fs | 4 +--- src/Client/Modals/SelectiveTemplateFromDB.fs | 2 +- src/Client/Pages/DataAnnotator/DataAnnotator.fs | 3 ++- src/Client/Pages/FilePicker/FilePickerView.fs | 4 +++- src/Client/Pages/JsonExporter/JsonExporter.fs | 4 +++- src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs | 4 +++- src/Client/Pages/ProtocolTemplates/ProtocolView.fs | 2 +- src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs | 6 +++--- src/Client/Pages/TermSearch/TermSearchView.fs | 3 ++- .../States/SelectedColumns.fs} | 5 +++-- src/Client/States/SpreadsheetInterface.fs | 2 +- src/Client/Update/UpdateUtil.fs | 2 +- src/Shared/Shared.fs | 10 +++++----- src/Shared/Shared.fsproj | 1 - 18 files changed, 35 insertions(+), 28 deletions(-) rename src/{Shared/DTOs/SelectedColumnsModalDto.fs => Client/States/SelectedColumns.fs} (74%) diff --git a/src/Client/Client.fsproj b/src/Client/Client.fsproj index 6daac2d3..138d64eb 100644 --- a/src/Client/Client.fsproj +++ b/src/Client/Client.fsproj @@ -23,6 +23,7 @@ + diff --git a/src/Client/MainComponents/Widgets.fs b/src/Client/MainComponents/Widgets.fs index 452186df..d63493f7 100644 --- a/src/Client/MainComponents/Widgets.fs +++ b/src/Client/MainComponents/Widgets.fs @@ -4,8 +4,8 @@ open Feliz open Feliz.DaisyUI open Browser.Types open LocalStorage.Widgets -open Modals.Template -open Shared.DTOs.SelectedColumnsModalDto +open Modals +open SelectedColumns module private InitExtensions = diff --git a/src/Client/Modals/ModalElements.fs b/src/Client/Modals/ModalElements.fs index 217b9f00..34dd3be3 100644 --- a/src/Client/Modals/ModalElements.fs +++ b/src/Client/Modals/ModalElements.fs @@ -5,7 +5,7 @@ open Feliz.DaisyUI open Model open Messages open Shared -open Shared.DTOs.SelectedColumnsModalDto +open SelectedColumns open ARCtrl open JsonImport diff --git a/src/Client/Modals/ModalProvider.fs b/src/Client/Modals/ModalProvider.fs index 8f5c34d2..57e8a2ab 100644 --- a/src/Client/Modals/ModalProvider.fs +++ b/src/Client/Modals/ModalProvider.fs @@ -23,9 +23,9 @@ type ModalProvider = | TableModals.TermDetails term -> Modals.TermModal.Main (term, dispatch) | TableModals.SelectiveTemplateImportFromDB -> - Modals.Template.SelectiveTemplateFromDBModal.Main (model, dispatch) + Modals.SelectiveTemplateFromDBModal.Main (model, dispatch) | TableModals.SelectiveFileImport arcfile -> - Modals.Import.SelectiveImportModal.Main (arcfile, dispatch) + Modals.SelectiveImportModal.Main (arcfile, dispatch) | TableModals.BatchUpdateColumnValues (columnIndex, column) -> Modals.UpdateColumn.Main (columnIndex, column, dispatch) | TableModals.TableCellContext (mouseX, mouseY, ci, ri) -> diff --git a/src/Client/Modals/SelectiveImportModal.fs b/src/Client/Modals/SelectiveImportModal.fs index 99d73a44..339b9299 100644 --- a/src/Client/Modals/SelectiveImportModal.fs +++ b/src/Client/Modals/SelectiveImportModal.fs @@ -5,14 +5,12 @@ open Feliz.DaisyUI open Model open Messages open Shared -open Shared.DTOs.SelectedColumnsModalDto +open SelectedColumns open ARCtrl open JsonImport open Components -open Modals - type SelectiveImportModal = static member private MetadataImport(isActive: bool, setActive: bool -> unit, disArcFile: ArcFilesDiscriminate) = diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Modals/SelectiveTemplateFromDB.fs index 1baf811d..b8437b8c 100644 --- a/src/Client/Modals/SelectiveTemplateFromDB.fs +++ b/src/Client/Modals/SelectiveTemplateFromDB.fs @@ -5,7 +5,7 @@ open Feliz.DaisyUI open Model open Messages open Shared -open Shared.DTOs.SelectedColumnsModalDto +open SelectedColumns open ARCtrl open JsonImport diff --git a/src/Client/Pages/DataAnnotator/DataAnnotator.fs b/src/Client/Pages/DataAnnotator/DataAnnotator.fs index 0df2ac5f..ebbd71b8 100644 --- a/src/Client/Pages/DataAnnotator/DataAnnotator.fs +++ b/src/Client/Pages/DataAnnotator/DataAnnotator.fs @@ -6,6 +6,7 @@ open Model open Messages open Feliz open Feliz.DaisyUI +open Modals module private DataAnnotatorHelper = @@ -342,7 +343,7 @@ type DataAnnotator = SidebarComponents.SidebarLayout.Description "Specify exact data points for annotation." - SidebarComponents.SidebarLayout.LogicContainer [ + ModalElements.LogicContainer [ ModalMangementComponent ref model (fun _ -> setShowModal true) rmvFile uploadFile match model.DataAnnotatorModel, showModal with | { DataFile = Some _; ParsedFile = Some _ }, true -> DataAnnotator.Modal(model, dispatch, rmvFile, fun _ -> setShowModal false) diff --git a/src/Client/Pages/FilePicker/FilePickerView.fs b/src/Client/Pages/FilePicker/FilePickerView.fs index 4dbf8fe7..3e91745e 100644 --- a/src/Client/Pages/FilePicker/FilePickerView.fs +++ b/src/Client/Pages/FilePicker/FilePickerView.fs @@ -8,6 +8,8 @@ open Messages open Feliz open Feliz.DaisyUI +open Modals + let update (filePickerMsg:FilePicker.Msg) (state: FilePicker.Model) (model: Model.Model) : FilePicker.Model * Cmd = match filePickerMsg with | LoadNewFiles fileNames -> @@ -216,7 +218,7 @@ module FileNameTable = let fileContainer (model:Model) dispatch = - SidebarComponents.SidebarLayout.LogicContainer [ + ModalElements.LogicContainer [ uploadButton model dispatch "@md/sidebar:flex-row" diff --git a/src/Client/Pages/JsonExporter/JsonExporter.fs b/src/Client/Pages/JsonExporter/JsonExporter.fs index f14cffe0..50ca09b5 100644 --- a/src/Client/Pages/JsonExporter/JsonExporter.fs +++ b/src/Client/Pages/JsonExporter/JsonExporter.fs @@ -21,6 +21,8 @@ open GlobalBindings open ARCtrl open ARCtrl.Spreadsheet +open Modals + let download(filename, text) = let element = document.createElement("a"); element.setAttribute("href", "data:text/plain;charset=utf-8," + Fable.Core.JS.encodeURIComponent(text)); @@ -139,7 +141,7 @@ type FileExporter = ] ] ]) - SidebarComponents.SidebarLayout.LogicContainer [ + ModalElements.LogicContainer [ FileExporter.JsonExport(model, dispatch) ] ] diff --git a/src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs b/src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs index 78dab3e2..bda07645 100644 --- a/src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs +++ b/src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs @@ -7,6 +7,8 @@ open Messages open Feliz open Feliz.DaisyUI +open Modals + module private HelperProtocolSearch = let breadcrumbEle (model:Model) dispatch = @@ -53,7 +55,7 @@ type SearchContainer = Html.p "Search the database for protocol templates." - SidebarComponents.SidebarLayout.LogicContainer [ + ModalElements.LogicContainer [ Protocol.Search.InfoField() Protocol.Search.FileSortElement(model, config, setConfig) Protocol.Search.Component (filteredTemplates, model, dispatch) diff --git a/src/Client/Pages/ProtocolTemplates/ProtocolView.fs b/src/Client/Pages/ProtocolTemplates/ProtocolView.fs index bf28532e..8ad27ee5 100644 --- a/src/Client/Pages/ProtocolTemplates/ProtocolView.fs +++ b/src/Client/Pages/ProtocolTemplates/ProtocolView.fs @@ -38,7 +38,7 @@ type Templates = // Box 1 SidebarComponents.SidebarLayout.Description "Add template from database." - Modals.Template.SelectiveTemplateFromDBModal.Main(model, dispatch) + Modals.SelectiveTemplateFromDBModal.Main(model, dispatch) // Box 2 SidebarComponents.SidebarLayout.Description (Html.p [ diff --git a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs index efd3dd3f..5ef922a3 100644 --- a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs +++ b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs @@ -19,7 +19,7 @@ open Shared open ARCtrl open Fable.Core.JsInterop -open Modals.ModalElements +open Modals type private TemplateFromFileState = { /// User select type to upload @@ -99,11 +99,11 @@ type TemplateFromFile = | ArcFilesDiscriminate.Template, JsonExportFormat.ROCrate | ArcFilesDiscriminate.Template, JsonExportFormat.ISA -> true | _ -> false - SidebarComponents.SidebarLayout.LogicContainer [ + ModalElements.LogicContainer [ // modal! match state.UploadedFile with | Some af -> - Modals.Import.SelectiveImportModal.Main (af, dispatch, rmv = (fun _ -> TemplateFromFileState.init() |> setState)) + Modals.SelectiveImportModal.Main (af, dispatch, rmv = (fun _ -> TemplateFromFileState.init() |> setState)) | None -> Html.none Html.div [ Daisy.join [ diff --git a/src/Client/Pages/TermSearch/TermSearchView.fs b/src/Client/Pages/TermSearch/TermSearchView.fs index d9cff126..5fb05392 100644 --- a/src/Client/Pages/TermSearch/TermSearchView.fs +++ b/src/Client/Pages/TermSearch/TermSearchView.fs @@ -8,6 +8,7 @@ open Elmish open TermSearch open Model +open Modals let update (termSearchMsg: TermSearch.Msg) (currentState:TermSearch.Model) : TermSearch.Model * Cmd = match termSearchMsg with @@ -100,7 +101,7 @@ let Main (model:Model, dispatch) = SidebarComponents.SidebarLayout.Description "Search for an ontology term to fill into the selected field(s)" - SidebarComponents.SidebarLayout.LogicContainer [ + ModalElements.LogicContainer [ Components.TermSearch.Input(setTerm, fullwidth=true, ?parent=model.TermSearchState.ParentTerm, advancedSearchDispatch=dispatch, ?onFocus=excelGetParentTerm, autofocus=true) addButton(model, dispatch) ] diff --git a/src/Shared/DTOs/SelectedColumnsModalDto.fs b/src/Client/States/SelectedColumns.fs similarity index 74% rename from src/Shared/DTOs/SelectedColumnsModalDto.fs rename to src/Client/States/SelectedColumns.fs index e12a11bb..d2e97768 100644 --- a/src/Shared/DTOs/SelectedColumnsModalDto.fs +++ b/src/Client/States/SelectedColumns.fs @@ -1,4 +1,4 @@ -module Shared.DTOs.SelectedColumnsModalDto +module SelectedColumns type SelectedColumns = { Columns: bool [] @@ -7,4 +7,5 @@ with static member init(length) = { Columns = Array.init length (fun _ -> true) - } \ No newline at end of file + } + diff --git a/src/Client/States/SpreadsheetInterface.fs b/src/Client/States/SpreadsheetInterface.fs index 24186201..bc486e78 100644 --- a/src/Client/States/SpreadsheetInterface.fs +++ b/src/Client/States/SpreadsheetInterface.fs @@ -1,7 +1,7 @@ namespace SpreadsheetInterface open Shared -open Shared.DTOs.SelectedColumnsModalDto +open SelectedColumns open ARCtrl open JsonImport diff --git a/src/Client/Update/UpdateUtil.fs b/src/Client/Update/UpdateUtil.fs index e2efdeba..3f3a6a21 100644 --- a/src/Client/Update/UpdateUtil.fs +++ b/src/Client/Update/UpdateUtil.fs @@ -2,7 +2,7 @@ module Update.UpdateUtil open ARCtrl open Shared -open Shared.DTOs.SelectedColumnsModalDto +open SelectedColumns open Fable.Remoting.Client let download(filename, bytes:byte []) = bytes.SaveFileAs(filename) diff --git a/src/Shared/Shared.fs b/src/Shared/Shared.fs index 028c701a..3ee48e0a 100644 --- a/src/Shared/Shared.fs +++ b/src/Shared/Shared.fs @@ -234,14 +234,14 @@ type IOntologyAPIv1 = { /// getTermSuggestions : (int*string) -> Async /// (nOfReturnedResults*queryString*parentOntology). If parentOntology = "" then isNull -> Error. - getTermSuggestionsByParentTerm : (int*string*SwateObsolete.TermMinimal) -> Async - getAllTermsByParentTerm : SwateObsolete.TermMinimal -> Async + getTermSuggestionsByParentTerm : (int*string*SwateObsolete.TermMinimal) -> Async + getAllTermsByParentTerm : SwateObsolete.TermMinimal -> Async /// (nOfReturnedResults*queryString*parentOntology). If parentOntology = "" then isNull -> Error. - getTermSuggestionsByChildTerm : (int*string*SwateObsolete.TermMinimal) -> Async - getAllTermsByChildTerm : SwateObsolete.TermMinimal -> Async + getTermSuggestionsByChildTerm : (int*string*SwateObsolete.TermMinimal) -> Async + getAllTermsByChildTerm : SwateObsolete.TermMinimal -> Async getTermsForAdvancedSearch : (AdvancedSearchTypes.AdvancedSearchOptions) -> Async getUnitTermSuggestions : (int*string) -> Async - getTermsByNames : SwateObsolete.TermSearchable [] -> Async + getTermsByNames : SwateObsolete.TermSearchable [] -> Async // Tree related requests getTreeByAccession : string -> Async diff --git a/src/Shared/Shared.fsproj b/src/Shared/Shared.fsproj index 9d397f96..a426ba67 100644 --- a/src/Shared/Shared.fsproj +++ b/src/Shared/Shared.fsproj @@ -5,7 +5,6 @@ - From 6ddefa415b68129d1ccc0ee7e13d7db4089037e2 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Tue, 3 Dec 2024 17:22:23 +0100 Subject: [PATCH 15/18] Implement review changes --- src/Client/Client.fsproj | 1 - src/Client/GenericComponents.fs | 15 +++++++++++++++ src/Client/MainComponents/Widgets.fs | 2 +- src/Client/Modals/ModalElements.fs | 18 ++---------------- src/Client/Modals/SelectiveImportModal.fs | 2 +- src/Client/Modals/SelectiveTemplateFromDB.fs | 4 ++-- .../Pages/BuildingBlock/BuildingBlockView.fs | 6 +++--- .../Pages/DataAnnotator/DataAnnotator.fs | 2 +- src/Client/Pages/FilePicker/FilePickerView.fs | 4 ++-- src/Client/Pages/JsonExporter/JsonExporter.fs | 4 ++-- .../Pages/ProtocolTemplates/ProtocolSearch.fs | 4 ++-- .../ProtocolTemplates/TemplateFromFile.fs | 3 ++- src/Client/Pages/TermSearch/TermSearchView.fs | 4 ++-- src/Client/States/SelectedColumns.fs | 11 ----------- src/Client/States/SpreadsheetInterface.fs | 2 +- src/Client/Types.fs | 13 ++++++++++++- src/Client/Update/UpdateUtil.fs | 2 +- 17 files changed, 49 insertions(+), 48 deletions(-) delete mode 100644 src/Client/States/SelectedColumns.fs diff --git a/src/Client/Client.fsproj b/src/Client/Client.fsproj index 138d64eb..6daac2d3 100644 --- a/src/Client/Client.fsproj +++ b/src/Client/Client.fsproj @@ -23,7 +23,6 @@ - diff --git a/src/Client/GenericComponents.fs b/src/Client/GenericComponents.fs index 179d3539..e36a9499 100644 --- a/src/Client/GenericComponents.fs +++ b/src/Client/GenericComponents.fs @@ -10,6 +10,21 @@ module DaisyUiExtensions = static member active = prop.className "modal-open" type Components = + + static member LogicContainer (children: ReactElement list) = + Html.div [ + // prop.className "border-l-4 border-transparent px-4 py-2 shadow-md" + // prop.style [ + // let rndVal = rnd.Next(30,70) + // let colorArr = [|NFDIColors.LightBlue.Lighter10; NFDIColors.Mint.Lighter10;|] + // style.custom("borderImageSlice", "1") + // style.custom("borderImageSource", $"linear-gradient({colorArr.[if order then 0 else 1]} {100-rndVal}%%, {colorArr.[if order then 1 else 0]})") + // order <- not order + // ] + prop.className "relative flex p-4 animated-border shadow-md gap-4 flex-col" //experimental + prop.children children + ] + static member DeleteButton(?children, ?props) = Daisy.button.button [ button.square diff --git a/src/Client/MainComponents/Widgets.fs b/src/Client/MainComponents/Widgets.fs index d63493f7..b5aff016 100644 --- a/src/Client/MainComponents/Widgets.fs +++ b/src/Client/MainComponents/Widgets.fs @@ -5,7 +5,7 @@ open Feliz.DaisyUI open Browser.Types open LocalStorage.Widgets open Modals -open SelectedColumns +open Types.TableManipulation module private InitExtensions = diff --git a/src/Client/Modals/ModalElements.fs b/src/Client/Modals/ModalElements.fs index 34dd3be3..a1f77584 100644 --- a/src/Client/Modals/ModalElements.fs +++ b/src/Client/Modals/ModalElements.fs @@ -1,11 +1,11 @@ -module Modals +namespace Modals open Feliz open Feliz.DaisyUI open Model open Messages open Shared -open SelectedColumns +open Types.TableManipulation open ARCtrl open JsonImport @@ -14,20 +14,6 @@ open Fable.React.Helpers type ModalElements = - static member LogicContainer (children: ReactElement list) = - Html.div [ - // prop.className "border-l-4 border-transparent px-4 py-2 shadow-md" - // prop.style [ - // let rndVal = rnd.Next(30,70) - // let colorArr = [|NFDIColors.LightBlue.Lighter10; NFDIColors.Mint.Lighter10;|] - // style.custom("borderImageSlice", "1") - // style.custom("borderImageSource", $"linear-gradient({colorArr.[if order then 0 else 1]} {100-rndVal}%%, {colorArr.[if order then 1 else 0]})") - // order <- not order - // ] - prop.className "relative flex p-4 animated-border shadow-md gap-4 flex-col" //experimental - prop.children children - ] - static member Button(text: string, onClickAction, buttonInput, ?isDisabled: bool) = let isDisabled = defaultArg isDisabled false Daisy.button.a [ diff --git a/src/Client/Modals/SelectiveImportModal.fs b/src/Client/Modals/SelectiveImportModal.fs index 339b9299..88e7e7f4 100644 --- a/src/Client/Modals/SelectiveImportModal.fs +++ b/src/Client/Modals/SelectiveImportModal.fs @@ -5,7 +5,7 @@ open Feliz.DaisyUI open Model open Messages open Shared -open SelectedColumns +open Types.TableManipulation open ARCtrl open JsonImport diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Modals/SelectiveTemplateFromDB.fs index b8437b8c..02a2e707 100644 --- a/src/Client/Modals/SelectiveTemplateFromDB.fs +++ b/src/Client/Modals/SelectiveTemplateFromDB.fs @@ -5,7 +5,7 @@ open Feliz.DaisyUI open Model open Messages open Shared -open SelectedColumns +open Types.TableManipulation open ARCtrl open JsonImport @@ -139,7 +139,7 @@ type SelectiveTemplateFromDBModal = else 0 let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init length) let importTypeState, setImportTypeState = React.useState(SelectiveImportModalState.init) - ModalElements.LogicContainer [ + Components.LogicContainer [ Html.div [ SelectiveTemplateFromDBModal.ToProtocolSearchElement model dispatch ] diff --git a/src/Client/Pages/BuildingBlock/BuildingBlockView.fs b/src/Client/Pages/BuildingBlock/BuildingBlockView.fs index f1caa011..52de0352 100644 --- a/src/Client/Pages/BuildingBlock/BuildingBlockView.fs +++ b/src/Client/Pages/BuildingBlock/BuildingBlockView.fs @@ -4,7 +4,7 @@ open Model open Messages open Messages.BuildingBlock open Shared -open Modals +open Components open Elmish @@ -82,13 +82,13 @@ let addBuildingBlockComponent (model:Model) (dispatch:Messages.Msg -> unit) = // Input forms, etc related to add building block. SidebarComponents.SidebarLayout.Description "Add annotation building blocks (columns) to the annotation table." - ModalElements.LogicContainer [ + Components.LogicContainer [ SearchComponent.Main model dispatch ] match model.PersistentStorageState.Host with | Some Swatehost.Excel -> Html.p "Convert existing Building Block." - ModalElements.LogicContainer [ + Components.LogicContainer [ CellConvertComponent.Main () ] | _ -> Html.none diff --git a/src/Client/Pages/DataAnnotator/DataAnnotator.fs b/src/Client/Pages/DataAnnotator/DataAnnotator.fs index ebbd71b8..ee1e98b7 100644 --- a/src/Client/Pages/DataAnnotator/DataAnnotator.fs +++ b/src/Client/Pages/DataAnnotator/DataAnnotator.fs @@ -343,7 +343,7 @@ type DataAnnotator = SidebarComponents.SidebarLayout.Description "Specify exact data points for annotation." - ModalElements.LogicContainer [ + Components.LogicContainer [ ModalMangementComponent ref model (fun _ -> setShowModal true) rmvFile uploadFile match model.DataAnnotatorModel, showModal with | { DataFile = Some _; ParsedFile = Some _ }, true -> DataAnnotator.Modal(model, dispatch, rmvFile, fun _ -> setShowModal false) diff --git a/src/Client/Pages/FilePicker/FilePickerView.fs b/src/Client/Pages/FilePicker/FilePickerView.fs index 3e91745e..8e709422 100644 --- a/src/Client/Pages/FilePicker/FilePickerView.fs +++ b/src/Client/Pages/FilePicker/FilePickerView.fs @@ -8,7 +8,7 @@ open Messages open Feliz open Feliz.DaisyUI -open Modals +open Components let update (filePickerMsg:FilePicker.Msg) (state: FilePicker.Model) (model: Model.Model) : FilePicker.Model * Cmd = match filePickerMsg with @@ -218,7 +218,7 @@ module FileNameTable = let fileContainer (model:Model) dispatch = - ModalElements.LogicContainer [ + Components.LogicContainer [ uploadButton model dispatch "@md/sidebar:flex-row" diff --git a/src/Client/Pages/JsonExporter/JsonExporter.fs b/src/Client/Pages/JsonExporter/JsonExporter.fs index 50ca09b5..e4f7242b 100644 --- a/src/Client/Pages/JsonExporter/JsonExporter.fs +++ b/src/Client/Pages/JsonExporter/JsonExporter.fs @@ -21,7 +21,7 @@ open GlobalBindings open ARCtrl open ARCtrl.Spreadsheet -open Modals +open Components let download(filename, text) = let element = document.createElement("a"); @@ -141,7 +141,7 @@ type FileExporter = ] ] ]) - ModalElements.LogicContainer [ + Components.LogicContainer [ FileExporter.JsonExport(model, dispatch) ] ] diff --git a/src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs b/src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs index bda07645..2de85d1b 100644 --- a/src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs +++ b/src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs @@ -7,7 +7,7 @@ open Messages open Feliz open Feliz.DaisyUI -open Modals +open Components module private HelperProtocolSearch = @@ -55,7 +55,7 @@ type SearchContainer = Html.p "Search the database for protocol templates." - ModalElements.LogicContainer [ + Components.LogicContainer [ Protocol.Search.InfoField() Protocol.Search.FileSortElement(model, config, setConfig) Protocol.Search.Component (filteredTemplates, model, dispatch) diff --git a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs index 5ef922a3..b28e4abc 100644 --- a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs +++ b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs @@ -20,6 +20,7 @@ open ARCtrl open Fable.Core.JsInterop open Modals +open Components type private TemplateFromFileState = { /// User select type to upload @@ -99,7 +100,7 @@ type TemplateFromFile = | ArcFilesDiscriminate.Template, JsonExportFormat.ROCrate | ArcFilesDiscriminate.Template, JsonExportFormat.ISA -> true | _ -> false - ModalElements.LogicContainer [ + Components.LogicContainer [ // modal! match state.UploadedFile with | Some af -> diff --git a/src/Client/Pages/TermSearch/TermSearchView.fs b/src/Client/Pages/TermSearch/TermSearchView.fs index 5fb05392..15923cdf 100644 --- a/src/Client/Pages/TermSearch/TermSearchView.fs +++ b/src/Client/Pages/TermSearch/TermSearchView.fs @@ -8,7 +8,7 @@ open Elmish open TermSearch open Model -open Modals +open Components let update (termSearchMsg: TermSearch.Msg) (currentState:TermSearch.Model) : TermSearch.Model * Cmd = match termSearchMsg with @@ -101,7 +101,7 @@ let Main (model:Model, dispatch) = SidebarComponents.SidebarLayout.Description "Search for an ontology term to fill into the selected field(s)" - ModalElements.LogicContainer [ + Components.LogicContainer [ Components.TermSearch.Input(setTerm, fullwidth=true, ?parent=model.TermSearchState.ParentTerm, advancedSearchDispatch=dispatch, ?onFocus=excelGetParentTerm, autofocus=true) addButton(model, dispatch) ] diff --git a/src/Client/States/SelectedColumns.fs b/src/Client/States/SelectedColumns.fs deleted file mode 100644 index d2e97768..00000000 --- a/src/Client/States/SelectedColumns.fs +++ /dev/null @@ -1,11 +0,0 @@ -module SelectedColumns - -type SelectedColumns = { - Columns: bool [] -} -with - static member init(length) = - { - Columns = Array.init length (fun _ -> true) - } - diff --git a/src/Client/States/SpreadsheetInterface.fs b/src/Client/States/SpreadsheetInterface.fs index bc486e78..aa989b02 100644 --- a/src/Client/States/SpreadsheetInterface.fs +++ b/src/Client/States/SpreadsheetInterface.fs @@ -1,7 +1,7 @@ namespace SpreadsheetInterface open Shared -open SelectedColumns +open Types.TableManipulation open ARCtrl open JsonImport diff --git a/src/Client/Types.fs b/src/Client/Types.fs index 5477c772..2acdb8b6 100644 --- a/src/Client/Types.fs +++ b/src/Client/Types.fs @@ -55,4 +55,15 @@ type Style = | Some subClasses -> subClasses.TryFind name | None -> None |> Option.map _.StyleString - |> Option.defaultValue "" \ No newline at end of file + |> Option.defaultValue "" + +module TableManipulation = + + type SelectedColumns = { + Columns: bool [] + } + with + static member init(length) = + { + Columns = Array.init length (fun _ -> true) + } diff --git a/src/Client/Update/UpdateUtil.fs b/src/Client/Update/UpdateUtil.fs index 3f3a6a21..ac13cb52 100644 --- a/src/Client/Update/UpdateUtil.fs +++ b/src/Client/Update/UpdateUtil.fs @@ -2,7 +2,7 @@ module Update.UpdateUtil open ARCtrl open Shared -open SelectedColumns +open Types.TableManipulation open Fable.Remoting.Client let download(filename, bytes:byte []) = bytes.SaveFileAs(filename) From 5feb2b14239555a543c61990e33fd2159c1be3f5 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Wed, 4 Dec 2024 08:50:44 +0100 Subject: [PATCH 16/18] Implement review changes --- src/Client/Client.fsproj | 2 +- src/Client/GenericComponents.fs | 14 ---- src/Client/MainComponents/Widgets.fs | 2 +- src/Client/Modals/ModalElements.fs | 2 +- src/Client/Modals/ModalProvider.fs | 2 - src/Client/Modals/SelectiveImportModal.fs | 82 +++++++++++++++++- src/Client/OfficeInterop/OfficeInterop.fs | 8 +- .../Pages/BuildingBlock/BuildingBlockView.fs | 5 +- .../Pages/DataAnnotator/DataAnnotator.fs | 3 +- src/Client/Pages/FilePicker/FilePickerView.fs | 4 +- src/Client/Pages/JsonExporter/JsonExporter.fs | 4 +- .../Pages/ProtocolTemplates/ProtocolSearch.fs | 4 +- .../SelectiveTemplateFromDB.fs | 83 +------------------ .../ProtocolTemplates/TemplateFromFile.fs | 3 +- src/Client/Pages/TermSearch/TermSearchView.fs | 4 +- src/Client/SidebarComponents/LayoutHelper.fs | 14 ++++ src/Client/States/ModalState.fs | 1 - src/Client/States/SpreadsheetInterface.fs | 2 +- src/Client/Types.fs | 2 +- src/Client/Update/UpdateUtil.fs | 2 +- 20 files changed, 114 insertions(+), 129 deletions(-) rename src/Client/{Modals => Pages/ProtocolTemplates}/SelectiveTemplateFromDB.fs (50%) diff --git a/src/Client/Client.fsproj b/src/Client/Client.fsproj index 6daac2d3..27cb9759 100644 --- a/src/Client/Client.fsproj +++ b/src/Client/Client.fsproj @@ -52,7 +52,6 @@ - @@ -92,6 +91,7 @@ + diff --git a/src/Client/GenericComponents.fs b/src/Client/GenericComponents.fs index e36a9499..eb6d41dd 100644 --- a/src/Client/GenericComponents.fs +++ b/src/Client/GenericComponents.fs @@ -11,20 +11,6 @@ module DaisyUiExtensions = type Components = - static member LogicContainer (children: ReactElement list) = - Html.div [ - // prop.className "border-l-4 border-transparent px-4 py-2 shadow-md" - // prop.style [ - // let rndVal = rnd.Next(30,70) - // let colorArr = [|NFDIColors.LightBlue.Lighter10; NFDIColors.Mint.Lighter10;|] - // style.custom("borderImageSlice", "1") - // style.custom("borderImageSource", $"linear-gradient({colorArr.[if order then 0 else 1]} {100-rndVal}%%, {colorArr.[if order then 1 else 0]})") - // order <- not order - // ] - prop.className "relative flex p-4 animated-border shadow-md gap-4 flex-col" //experimental - prop.children children - ] - static member DeleteButton(?children, ?props) = Daisy.button.button [ button.square diff --git a/src/Client/MainComponents/Widgets.fs b/src/Client/MainComponents/Widgets.fs index b5aff016..344c915f 100644 --- a/src/Client/MainComponents/Widgets.fs +++ b/src/Client/MainComponents/Widgets.fs @@ -5,7 +5,7 @@ open Feliz.DaisyUI open Browser.Types open LocalStorage.Widgets open Modals -open Types.TableManipulation +open Types.TableImport module private InitExtensions = diff --git a/src/Client/Modals/ModalElements.fs b/src/Client/Modals/ModalElements.fs index a1f77584..22089afa 100644 --- a/src/Client/Modals/ModalElements.fs +++ b/src/Client/Modals/ModalElements.fs @@ -5,7 +5,7 @@ open Feliz.DaisyUI open Model open Messages open Shared -open Types.TableManipulation +open Types.TableImport open ARCtrl open JsonImport diff --git a/src/Client/Modals/ModalProvider.fs b/src/Client/Modals/ModalProvider.fs index 57e8a2ab..5962d320 100644 --- a/src/Client/Modals/ModalProvider.fs +++ b/src/Client/Modals/ModalProvider.fs @@ -22,8 +22,6 @@ type ModalProvider = Modals.ResetTable.Main dispatch | TableModals.TermDetails term -> Modals.TermModal.Main (term, dispatch) - | TableModals.SelectiveTemplateImportFromDB -> - Modals.SelectiveTemplateFromDBModal.Main (model, dispatch) | TableModals.SelectiveFileImport arcfile -> Modals.SelectiveImportModal.Main (arcfile, dispatch) | TableModals.BatchUpdateColumnValues (columnIndex, column) -> diff --git a/src/Client/Modals/SelectiveImportModal.fs b/src/Client/Modals/SelectiveImportModal.fs index 88e7e7f4..30904754 100644 --- a/src/Client/Modals/SelectiveImportModal.fs +++ b/src/Client/Modals/SelectiveImportModal.fs @@ -5,7 +5,7 @@ open Feliz.DaisyUI open Model open Messages open Shared -open Types.TableManipulation +open Types.TableImport open ARCtrl open JsonImport @@ -13,6 +13,80 @@ open Components type SelectiveImportModal = + static member RadioPluginsBox(boxName, icon, importType: TableJoinOptions, radioData: (TableJoinOptions * string)[], setImportType: TableJoinOptions -> unit) = + let myradio(target: TableJoinOptions, txt: string) = + let isChecked = importType = target + ModalElements.RadioPlugin("importType", txt, isChecked, fun (b: bool) -> if b then setImportType target) + ModalElements.Box (boxName, icon, React.fragment [ + Html.div [ + for i in 0..radioData.Length-1 do + myradio(radioData.[i]) + ] + ]) + + static member CheckBoxForTableColumnSelection(columns: CompositeColumn [], index, selectionInformation: SelectedColumns, setSelectedColumns: SelectedColumns -> unit) = + Html.div [ + prop.style [style.display.flex; style.justifyContent.center] + prop.children [ + Daisy.checkbox [ + prop.type'.checkbox + prop.style [ + style.height(length.perc 100) + ] + prop.isChecked + (if selectionInformation.Columns.Length > 0 then + selectionInformation.Columns.[index] + else true) + prop.onChange (fun (b: bool) -> + if columns.Length > 0 then + let selectedData = selectionInformation.Columns + selectedData.[index] <- b + {selectionInformation with Columns = selectedData} |> setSelectedColumns) + ] + ] + ] + + static member TableWithImportColumnCheckboxes(table: ArcTable, ?selectionInformation: SelectedColumns, ?setSelectedColumns: SelectedColumns -> unit) = + let columns = table.Columns + let displayCheckBox = + //Determine whether to display checkboxes or not + selectionInformation.IsSome && setSelectedColumns.IsSome + Daisy.table [ + prop.children [ + Html.thead [ + Html.tr [ + for i in 0..columns.Length-1 do + Html.th [ + Html.label [ + prop.className "join flex flex-row centered gap-2" + prop.children [ + if displayCheckBox then + SelectiveImportModal.CheckBoxForTableColumnSelection(columns, i, selectionInformation.Value, setSelectedColumns.Value) + Html.text (columns.[i].Header.ToString()) + Html.div [ + prop.onClick (fun e -> + if columns.Length > 0 && selectionInformation.IsSome then + let selectedData = selectionInformation.Value.Columns + selectedData.[i] <- not selectedData.[i] + {selectionInformation.Value with Columns = selectedData} |> setSelectedColumns.Value) + ] + ] + ] + ] + ] + ] + + Html.tbody [ + for ri in 0 .. (table.RowCount-1) do + let row = table.GetRow(ri, true) + Html.tr [ + for c in row do + Html.td (c.ToString()) + ] + ] + ] + ] + static member private MetadataImport(isActive: bool, setActive: bool -> unit, disArcFile: ArcFilesDiscriminate) = let name = string disArcFile ModalElements.Box (sprintf "%s Metadata" name, "fa-solid fa-lightbulb", React.fragment [ @@ -77,9 +151,9 @@ type SelectiveImportModal = prop.className "overflow-x-auto" prop.children [ if isActive then - SelectiveTemplateFromDBModal.TableWithImportColumnCheckboxes(table0, selectedColumns, setSelectedColumns) + SelectiveImportModal.TableWithImportColumnCheckboxes(table0, selectedColumns, setSelectedColumns) else - SelectiveTemplateFromDBModal.TableWithImportColumnCheckboxes(table0) + SelectiveImportModal.TableWithImportColumnCheckboxes(table0) ] ] ] @@ -128,7 +202,7 @@ type SelectiveImportModal = Components.DeleteButton(props=[prop.onClick rmv]) ] ] - SelectiveTemplateFromDBModal.RadioPluginsBox( + SelectiveImportModal.RadioPluginsBox( "Import Type", "fa-solid fa-cog", state.ImportType, diff --git a/src/Client/OfficeInterop/OfficeInterop.fs b/src/Client/OfficeInterop/OfficeInterop.fs index e7af6c86..edcf7282 100644 --- a/src/Client/OfficeInterop/OfficeInterop.fs +++ b/src/Client/OfficeInterop/OfficeInterop.fs @@ -1080,7 +1080,7 @@ let joinTable (tableToAdd: ArcTable, selectedColumns: bool [], options: TableJoi do! AnnotationTable.format(newTable, context, true) - return [InteropLogging.Msg.create InteropLogging.Warning $"Joined template {refinedTableToAdd.Name} to table {excelTable.name}!"] + return [InteropLogging.Msg.create InteropLogging.Info $"Joined template {refinedTableToAdd.Name} to table {excelTable.name}!"] | Result.Error _ -> return [InteropLogging.Msg.create InteropLogging.Error "No arc table could be created! This should not happen at this stage! Please report this as a bug to the developers.!"] | None -> return [InteropLogging.NoActiveTableMsg] @@ -1522,7 +1522,7 @@ let convertBuildingBlock () = if String.IsNullOrEmpty(msgText) then $"Converted building block of {snd selectedBuildingBlock.[0]} to unit" else msgText - return [InteropLogging.Msg.create InteropLogging.Warning msg] + return [InteropLogging.Msg.create InteropLogging.Info msg] | Result.Error ex -> return [InteropLogging.Msg.create InteropLogging.Error ex.Message] | None -> return [InteropLogging.NoActiveTableMsg] } @@ -1845,7 +1845,7 @@ let deleteTopLevelMetadata () = worksheet.delete() ) - return [InteropLogging.Msg.create InteropLogging.Warning $"The top level metadata work sheet has been deleted"] + return [InteropLogging.Msg.create InteropLogging.Info $"The top level metadata work sheet has been deleted"] } ) @@ -2433,7 +2433,7 @@ type Main = do! updateSelectedBuildingBlocks excelTable arcTable propertyColumns indexedTerms do! AnnotationTable.format(excelTable, context, true) - return [InteropLogging.Msg.create InteropLogging.Warning $"The annotation table {excelTable.name} is valid"] + return [InteropLogging.Msg.create InteropLogging.Info $"The annotation table {excelTable.name} is valid"] | Result.Error ex -> return [InteropLogging.Msg.create InteropLogging.Error ex.Message] } diff --git a/src/Client/Pages/BuildingBlock/BuildingBlockView.fs b/src/Client/Pages/BuildingBlock/BuildingBlockView.fs index 52de0352..bb4b0e15 100644 --- a/src/Client/Pages/BuildingBlock/BuildingBlockView.fs +++ b/src/Client/Pages/BuildingBlock/BuildingBlockView.fs @@ -4,7 +4,6 @@ open Model open Messages open Messages.BuildingBlock open Shared -open Components open Elmish @@ -82,13 +81,13 @@ let addBuildingBlockComponent (model:Model) (dispatch:Messages.Msg -> unit) = // Input forms, etc related to add building block. SidebarComponents.SidebarLayout.Description "Add annotation building blocks (columns) to the annotation table." - Components.LogicContainer [ + SidebarComponents.SidebarLayout.LogicContainer [ SearchComponent.Main model dispatch ] match model.PersistentStorageState.Host with | Some Swatehost.Excel -> Html.p "Convert existing Building Block." - Components.LogicContainer [ + SidebarComponents.SidebarLayout.LogicContainer [ CellConvertComponent.Main () ] | _ -> Html.none diff --git a/src/Client/Pages/DataAnnotator/DataAnnotator.fs b/src/Client/Pages/DataAnnotator/DataAnnotator.fs index ee1e98b7..0df2ac5f 100644 --- a/src/Client/Pages/DataAnnotator/DataAnnotator.fs +++ b/src/Client/Pages/DataAnnotator/DataAnnotator.fs @@ -6,7 +6,6 @@ open Model open Messages open Feliz open Feliz.DaisyUI -open Modals module private DataAnnotatorHelper = @@ -343,7 +342,7 @@ type DataAnnotator = SidebarComponents.SidebarLayout.Description "Specify exact data points for annotation." - Components.LogicContainer [ + SidebarComponents.SidebarLayout.LogicContainer [ ModalMangementComponent ref model (fun _ -> setShowModal true) rmvFile uploadFile match model.DataAnnotatorModel, showModal with | { DataFile = Some _; ParsedFile = Some _ }, true -> DataAnnotator.Modal(model, dispatch, rmvFile, fun _ -> setShowModal false) diff --git a/src/Client/Pages/FilePicker/FilePickerView.fs b/src/Client/Pages/FilePicker/FilePickerView.fs index 8e709422..4dbf8fe7 100644 --- a/src/Client/Pages/FilePicker/FilePickerView.fs +++ b/src/Client/Pages/FilePicker/FilePickerView.fs @@ -8,8 +8,6 @@ open Messages open Feliz open Feliz.DaisyUI -open Components - let update (filePickerMsg:FilePicker.Msg) (state: FilePicker.Model) (model: Model.Model) : FilePicker.Model * Cmd = match filePickerMsg with | LoadNewFiles fileNames -> @@ -218,7 +216,7 @@ module FileNameTable = let fileContainer (model:Model) dispatch = - Components.LogicContainer [ + SidebarComponents.SidebarLayout.LogicContainer [ uploadButton model dispatch "@md/sidebar:flex-row" diff --git a/src/Client/Pages/JsonExporter/JsonExporter.fs b/src/Client/Pages/JsonExporter/JsonExporter.fs index e4f7242b..f14cffe0 100644 --- a/src/Client/Pages/JsonExporter/JsonExporter.fs +++ b/src/Client/Pages/JsonExporter/JsonExporter.fs @@ -21,8 +21,6 @@ open GlobalBindings open ARCtrl open ARCtrl.Spreadsheet -open Components - let download(filename, text) = let element = document.createElement("a"); element.setAttribute("href", "data:text/plain;charset=utf-8," + Fable.Core.JS.encodeURIComponent(text)); @@ -141,7 +139,7 @@ type FileExporter = ] ] ]) - Components.LogicContainer [ + SidebarComponents.SidebarLayout.LogicContainer [ FileExporter.JsonExport(model, dispatch) ] ] diff --git a/src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs b/src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs index 2de85d1b..78dab3e2 100644 --- a/src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs +++ b/src/Client/Pages/ProtocolTemplates/ProtocolSearch.fs @@ -7,8 +7,6 @@ open Messages open Feliz open Feliz.DaisyUI -open Components - module private HelperProtocolSearch = let breadcrumbEle (model:Model) dispatch = @@ -55,7 +53,7 @@ type SearchContainer = Html.p "Search the database for protocol templates." - Components.LogicContainer [ + SidebarComponents.SidebarLayout.LogicContainer [ Protocol.Search.InfoField() Protocol.Search.FileSortElement(model, config, setConfig) Protocol.Search.Component (filteredTemplates, model, dispatch) diff --git a/src/Client/Modals/SelectiveTemplateFromDB.fs b/src/Client/Pages/ProtocolTemplates/SelectiveTemplateFromDB.fs similarity index 50% rename from src/Client/Modals/SelectiveTemplateFromDB.fs rename to src/Client/Pages/ProtocolTemplates/SelectiveTemplateFromDB.fs index 02a2e707..b3033ae9 100644 --- a/src/Client/Modals/SelectiveTemplateFromDB.fs +++ b/src/Client/Pages/ProtocolTemplates/SelectiveTemplateFromDB.fs @@ -5,88 +5,13 @@ open Feliz.DaisyUI open Model open Messages open Shared -open Types.TableManipulation +open Types.TableImport open ARCtrl open JsonImport -open Components type SelectiveTemplateFromDBModal = - static member RadioPluginsBox(boxName, icon, importType: TableJoinOptions, radioData: (TableJoinOptions * string)[], setImportType: TableJoinOptions -> unit) = - let myradio(target: TableJoinOptions, txt: string) = - let isChecked = importType = target - ModalElements.RadioPlugin("importType", txt, isChecked, fun (b: bool) -> if b then setImportType target) - ModalElements.Box (boxName, icon, React.fragment [ - Html.div [ - for i in 0..radioData.Length-1 do - myradio(radioData.[i]) - ] - ]) - - static member CheckBoxForTableColumnSelection(columns: CompositeColumn [], index, selectionInformation: SelectedColumns, setSelectedColumns: SelectedColumns -> unit) = - Html.div [ - prop.style [style.display.flex; style.justifyContent.center] - prop.children [ - Daisy.checkbox [ - prop.type'.checkbox - prop.style [ - style.height(length.perc 100) - ] - prop.isChecked - (if selectionInformation.Columns.Length > 0 then - selectionInformation.Columns.[index] - else true) - prop.onChange (fun (b: bool) -> - if columns.Length > 0 then - let selectedData = selectionInformation.Columns - selectedData.[index] <- b - {selectionInformation with Columns = selectedData} |> setSelectedColumns) - ] - ] - ] - - static member TableWithImportColumnCheckboxes(table: ArcTable, ?selectionInformation: SelectedColumns, ?setSelectedColumns: SelectedColumns -> unit) = - let columns = table.Columns - let displayCheckBox = - //Determine whether to display checkboxes or not - selectionInformation.IsSome && setSelectedColumns.IsSome - Daisy.table [ - prop.children [ - Html.thead [ - Html.tr [ - for i in 0..columns.Length-1 do - Html.th [ - Html.label [ - prop.className "join flex flex-row centered gap-2" - prop.children [ - if displayCheckBox then - SelectiveTemplateFromDBModal.CheckBoxForTableColumnSelection(columns, i, selectionInformation.Value, setSelectedColumns.Value) - Html.text (columns.[i].Header.ToString()) - Html.div [ - prop.onClick (fun e -> - if columns.Length > 0 && selectionInformation.IsSome then - let selectedData = selectionInformation.Value.Columns - selectedData.[i] <- not selectedData.[i] - {selectionInformation.Value with Columns = selectedData} |> setSelectedColumns.Value) - ] - ] - ] - ] - ] - ] - - Html.tbody [ - for ri in 0 .. (table.RowCount-1) do - let row = table.GetRow(ri, true) - Html.tr [ - for c in row do - Html.td (c.ToString()) - ] - ] - ] - ] - static member ToProtocolSearchElement (model: Model) dispatch = Daisy.button.button [ prop.onClick(fun _ -> UpdateModel {model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch} |> dispatch) @@ -106,7 +31,7 @@ type SelectiveTemplateFromDBModal = Html.i [prop.className "fa-solid fa-cog"] Html.span $"Template: {model.ProtocolState.TemplateSelected.Value.Name}" if model.ProtocolState.TemplateSelected.IsSome then - SelectiveTemplateFromDBModal.TableWithImportColumnCheckboxes(model.ProtocolState.TemplateSelected.Value.Table, selectionInformation, setSelectedColumns) + SelectiveImportModal.TableWithImportColumnCheckboxes(model.ProtocolState.TemplateSelected.Value.Table, selectionInformation, setSelectedColumns) ] ] @@ -139,13 +64,13 @@ type SelectiveTemplateFromDBModal = else 0 let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init length) let importTypeState, setImportTypeState = React.useState(SelectiveImportModalState.init) - Components.LogicContainer [ + SidebarComponents.SidebarLayout.LogicContainer [ Html.div [ SelectiveTemplateFromDBModal.ToProtocolSearchElement model dispatch ] if model.ProtocolState.TemplateSelected.IsSome then Html.div [ - SelectiveTemplateFromDBModal.RadioPluginsBox( + SelectiveImportModal.RadioPluginsBox( "Import Type", "fa-solid fa-cog", importTypeState.ImportType, diff --git a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs index b28e4abc..4052aa35 100644 --- a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs +++ b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs @@ -20,7 +20,6 @@ open ARCtrl open Fable.Core.JsInterop open Modals -open Components type private TemplateFromFileState = { /// User select type to upload @@ -100,7 +99,7 @@ type TemplateFromFile = | ArcFilesDiscriminate.Template, JsonExportFormat.ROCrate | ArcFilesDiscriminate.Template, JsonExportFormat.ISA -> true | _ -> false - Components.LogicContainer [ + SidebarComponents.SidebarLayout.LogicContainer [ // modal! match state.UploadedFile with | Some af -> diff --git a/src/Client/Pages/TermSearch/TermSearchView.fs b/src/Client/Pages/TermSearch/TermSearchView.fs index 15923cdf..8302d11c 100644 --- a/src/Client/Pages/TermSearch/TermSearchView.fs +++ b/src/Client/Pages/TermSearch/TermSearchView.fs @@ -8,8 +8,6 @@ open Elmish open TermSearch open Model -open Components - let update (termSearchMsg: TermSearch.Msg) (currentState:TermSearch.Model) : TermSearch.Model * Cmd = match termSearchMsg with // Toggle the search by parent ontology option on/off by clicking on a checkbox @@ -101,7 +99,7 @@ let Main (model:Model, dispatch) = SidebarComponents.SidebarLayout.Description "Search for an ontology term to fill into the selected field(s)" - Components.LogicContainer [ + SidebarComponents.SidebarLayout.LogicContainer [ Components.TermSearch.Input(setTerm, fullwidth=true, ?parent=model.TermSearchState.ParentTerm, advancedSearchDispatch=dispatch, ?onFocus=excelGetParentTerm, autofocus=true) addButton(model, dispatch) ] diff --git a/src/Client/SidebarComponents/LayoutHelper.fs b/src/Client/SidebarComponents/LayoutHelper.fs index c8fda28e..5358322b 100644 --- a/src/Client/SidebarComponents/LayoutHelper.fs +++ b/src/Client/SidebarComponents/LayoutHelper.fs @@ -21,6 +21,20 @@ module private LayoutHelperAux = type SidebarLayout = + static member LogicContainer (children: ReactElement list) = + Html.div [ + // prop.className "border-l-4 border-transparent px-4 py-2 shadow-md" + // prop.style [ + // let rndVal = rnd.Next(30,70) + // let colorArr = [|NFDIColors.LightBlue.Lighter10; NFDIColors.Mint.Lighter10;|] + // style.custom("borderImageSlice", "1") + // style.custom("borderImageSource", $"linear-gradient({colorArr.[if order then 0 else 1]} {100-rndVal}%%, {colorArr.[if order then 1 else 0]})") + // order <- not order + // ] + prop.className "relative flex p-4 animated-border shadow-md gap-4 flex-col" //experimental + prop.children children + ] + static member Container (children: ReactElement list) = Html.div [ prop.className "flex flex-col gap-2 py-4" diff --git a/src/Client/States/ModalState.fs b/src/Client/States/ModalState.fs index bcdb1a3d..982376da 100644 --- a/src/Client/States/ModalState.fs +++ b/src/Client/States/ModalState.fs @@ -13,7 +13,6 @@ module ModalState = | EditColumn of columIndex: int | MoveColumn of columnIndex: int | BatchUpdateColumnValues of columIndex: int * column: CompositeColumn - | SelectiveTemplateImportFromDB | SelectiveFileImport of ArcFiles | TermDetails of OntologyAnnotation | TableCellContext of mouseX: int * mouseY: int * columnIndex: int * rowIndex: int diff --git a/src/Client/States/SpreadsheetInterface.fs b/src/Client/States/SpreadsheetInterface.fs index aa989b02..ed69e50e 100644 --- a/src/Client/States/SpreadsheetInterface.fs +++ b/src/Client/States/SpreadsheetInterface.fs @@ -1,7 +1,7 @@ namespace SpreadsheetInterface open Shared -open Types.TableManipulation +open Types.TableImport open ARCtrl open JsonImport diff --git a/src/Client/Types.fs b/src/Client/Types.fs index 2acdb8b6..36771ca7 100644 --- a/src/Client/Types.fs +++ b/src/Client/Types.fs @@ -57,7 +57,7 @@ type Style = |> Option.map _.StyleString |> Option.defaultValue "" -module TableManipulation = +module TableImport = type SelectedColumns = { Columns: bool [] diff --git a/src/Client/Update/UpdateUtil.fs b/src/Client/Update/UpdateUtil.fs index ac13cb52..3c7c3620 100644 --- a/src/Client/Update/UpdateUtil.fs +++ b/src/Client/Update/UpdateUtil.fs @@ -2,7 +2,7 @@ module Update.UpdateUtil open ARCtrl open Shared -open Types.TableManipulation +open Types.TableImport open Fable.Remoting.Client let download(filename, bytes:byte []) = bytes.SaveFileAs(filename) From 9cf7d210dae336d4cdfa743f2bb2baa039cf3f9c Mon Sep 17 00:00:00 2001 From: patrick blume Date: Wed, 4 Dec 2024 10:19:42 +0100 Subject: [PATCH 17/18] Enable usage of selectedColumns in swate alpha --- src/Client/MainComponents/Widgets.fs | 5 +---- src/Client/States/Spreadsheet.fs | 3 ++- src/Client/Update/InterfaceUpdate.fs | 2 +- src/Client/Update/SpreadsheetUpdate.fs | 5 ++--- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Client/MainComponents/Widgets.fs b/src/Client/MainComponents/Widgets.fs index 344c915f..e705f55b 100644 --- a/src/Client/MainComponents/Widgets.fs +++ b/src/Client/MainComponents/Widgets.fs @@ -226,13 +226,10 @@ type Widget = ] let insertContent() = [ - Html.div [ - SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch) - ] Html.div [ prop.style [style.maxHeight (length.px 350); style.overflow.auto] prop.children [ - SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch) + SelectiveTemplateFromDBModal.Main(model, dispatch) ] ] ] diff --git a/src/Client/States/Spreadsheet.fs b/src/Client/States/Spreadsheet.fs index 31efc352..bc8ce57b 100644 --- a/src/Client/States/Spreadsheet.fs +++ b/src/Client/States/Spreadsheet.fs @@ -3,6 +3,7 @@ namespace Spreadsheet open Shared open ARCtrl open Fable.Core +open JsonImport type ColumnType = | Main @@ -201,7 +202,7 @@ type Msg = | AddAnnotationBlock of CompositeColumn | AddAnnotationBlocks of CompositeColumn [] | AddDataAnnotation of {| fragmentSelectors: string []; fileName: string; fileType: string; targetColumn: DataAnnotator.TargetColumn |} -| AddTemplate of ArcTable * bool[] +| AddTemplate of ArcTable * bool[] * SelectiveImportModalState | JoinTable of ArcTable * index: int option * options: TableJoinOptions option | UpdateArcFile of ArcFiles | InitFromArcFile of ArcFiles diff --git a/src/Client/Update/InterfaceUpdate.fs b/src/Client/Update/InterfaceUpdate.fs index e2966e57..dba7b580 100644 --- a/src/Client/Update/InterfaceUpdate.fs +++ b/src/Client/Update/InterfaceUpdate.fs @@ -170,7 +170,7 @@ module Interface = let cmd = OfficeInterop.AddTemplate (table, selectedColumns, importType) |> OfficeInteropMsg |> Cmd.ofMsg model, cmd | Some Swatehost.Browser | Some Swatehost.ARCitect -> - let cmd = Spreadsheet.AddTemplate (table, selectedColumns) |> SpreadsheetMsg |> Cmd.ofMsg + let cmd = Spreadsheet.AddTemplate (table, selectedColumns, importType) |> SpreadsheetMsg |> Cmd.ofMsg model, cmd | _ -> failwith "not implemented" | JoinTable (table, index, options) -> diff --git a/src/Client/Update/SpreadsheetUpdate.fs b/src/Client/Update/SpreadsheetUpdate.fs index d040ad92..1e2ce94c 100644 --- a/src/Client/Update/SpreadsheetUpdate.fs +++ b/src/Client/Update/SpreadsheetUpdate.fs @@ -103,11 +103,10 @@ module Spreadsheet = | IsTable -> Controller.BuildingBlocks.addDataAnnotation data state | IsMetadata -> failwith "Unable to add data annotation in metadata view" nextState, model, Cmd.none - | AddTemplate (table, selectedColumns) -> + | AddTemplate (table, selectedColumns, importType) -> let index = Some (Spreadsheet.Controller.BuildingBlocks.SidebarControllerAux.getNextColumnIndex model.SpreadsheetModel) /// Filter out existing building blocks and keep input/output values. - let options = Some ARCtrl.TableJoinOptions.WithValues // If changed to anything else we need different logic to keep input/output values - let msg = fun table -> JoinTable(table, index, options) |> SpreadsheetMsg + let msg = fun table -> JoinTable(table, index, Some importType.ImportType) |> SpreadsheetMsg let selectedColumnsIndices = selectedColumns |> Array.mapi (fun i item -> if item = false then Some i else None) From 95dcf24e0bbffebd4b3f1a0ec51d76aa91f270e5 Mon Sep 17 00:00:00 2001 From: patrick blume Date: Wed, 4 Dec 2024 13:24:33 +0100 Subject: [PATCH 18/18] Enable naming of radioGroup to enable usage in swate alpha --- src/Client/MainComponents/Widgets.fs | 39 +++++++++++++++++-- src/Client/Modals/ModalElements.fs | 2 +- src/Client/Modals/SelectiveImportModal.fs | 5 ++- .../SelectiveTemplateFromDB.fs | 1 + 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/Client/MainComponents/Widgets.fs b/src/Client/MainComponents/Widgets.fs index e705f55b..acce3812 100644 --- a/src/Client/MainComponents/Widgets.fs +++ b/src/Client/MainComponents/Widgets.fs @@ -6,6 +6,7 @@ open Browser.Types open LocalStorage.Widgets open Modals open Types.TableImport +open Types.JsonImport module private InitExtensions = @@ -213,9 +214,13 @@ type Widget = [] static member Templates (model: Model, dispatch, rmv: MouseEvent -> unit) = let templates, setTemplates = React.useState(model.ProtocolState.Templates) - let selectedColumnsLength = if model.ProtocolState.TemplateSelected.IsSome then model.ProtocolState.TemplateSelected.Value.Table.Columns.Length else 0 - let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init selectedColumnsLength) let config, setConfig = React.useState(TemplateFilterConfig.init) + let selectedColumnsLength = + if model.ProtocolState.TemplateSelected.IsSome then + model.ProtocolState.TemplateSelected.Value.Table.Columns.Length + else 0 + let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init selectedColumnsLength) + let importTypeState, setImportTypeState = React.useState(SelectiveImportModalState.init) let filteredTemplates = Protocol.Search.filterTemplates (templates, config) React.useEffectOnce(fun _ -> Messages.Protocol.GetAllProtocolsRequest |> Messages.ProtocolMsg |> dispatch) React.useEffect((fun _ -> setTemplates model.ProtocolState.Templates), [|box model.ProtocolState.Templates|]) @@ -229,7 +234,35 @@ type Widget = Html.div [ prop.style [style.maxHeight (length.px 350); style.overflow.auto] prop.children [ - SelectiveTemplateFromDBModal.Main(model, dispatch) + SidebarComponents.SidebarLayout.LogicContainer [ + Html.div [ + SelectiveTemplateFromDBModal.ToProtocolSearchElement model dispatch + ] + if model.ProtocolState.TemplateSelected.IsSome then + Html.div [ + SelectiveImportModal.RadioPluginsBox( + "Import Type", + "fa-solid fa-cog", + importTypeState.ImportType, + "importType ", + [| + ARCtrl.TableJoinOptions.Headers, " Column Headers"; + ARCtrl.TableJoinOptions.WithUnit, " ..With Units"; + ARCtrl.TableJoinOptions.WithValues, " ..With Values"; + |], + fun importType -> {importTypeState with ImportType = importType} |> setImportTypeState + ) + ] + Html.div [ + ModalElements.Box( + model.ProtocolState.TemplateSelected.Value.Name, + "", + SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch, false)) + ] + Html.div [ + SelectiveTemplateFromDBModal.AddFromDBToTableButton model selectedColumns importTypeState dispatch + ] + ] ] ] ] diff --git a/src/Client/Modals/ModalElements.fs b/src/Client/Modals/ModalElements.fs index 22089afa..544634ba 100644 --- a/src/Client/Modals/ModalElements.fs +++ b/src/Client/Modals/ModalElements.fs @@ -27,7 +27,7 @@ type ModalElements = prop.text text ] - static member RadioPlugin(radioGroup: string, txt:string, isChecked, onChange: bool -> unit, ?isDisabled: bool) = + static member RadioPlugin(radioGroup: string, txt: string, isChecked, onChange: bool -> unit, ?isDisabled: bool) = let isDisabled = defaultArg isDisabled false Daisy.formControl [ Daisy.label [ diff --git a/src/Client/Modals/SelectiveImportModal.fs b/src/Client/Modals/SelectiveImportModal.fs index 30904754..9dd84c41 100644 --- a/src/Client/Modals/SelectiveImportModal.fs +++ b/src/Client/Modals/SelectiveImportModal.fs @@ -13,10 +13,10 @@ open Components type SelectiveImportModal = - static member RadioPluginsBox(boxName, icon, importType: TableJoinOptions, radioData: (TableJoinOptions * string)[], setImportType: TableJoinOptions -> unit) = + static member RadioPluginsBox(boxName, icon, importType: TableJoinOptions, radioGroupName, radioData: (TableJoinOptions * string)[], setImportType: TableJoinOptions -> unit) = let myradio(target: TableJoinOptions, txt: string) = let isChecked = importType = target - ModalElements.RadioPlugin("importType", txt, isChecked, fun (b: bool) -> if b then setImportType target) + ModalElements.RadioPlugin(radioGroupName, txt, isChecked, fun (b: bool) -> if b then setImportType target) ModalElements.Box (boxName, icon, React.fragment [ Html.div [ for i in 0..radioData.Length-1 do @@ -206,6 +206,7 @@ type SelectiveImportModal = "Import Type", "fa-solid fa-cog", state.ImportType, + "importType", [| ARCtrl.TableJoinOptions.Headers, " Column Headers"; ARCtrl.TableJoinOptions.WithUnit, " ..With Units"; diff --git a/src/Client/Pages/ProtocolTemplates/SelectiveTemplateFromDB.fs b/src/Client/Pages/ProtocolTemplates/SelectiveTemplateFromDB.fs index b3033ae9..fac0583b 100644 --- a/src/Client/Pages/ProtocolTemplates/SelectiveTemplateFromDB.fs +++ b/src/Client/Pages/ProtocolTemplates/SelectiveTemplateFromDB.fs @@ -74,6 +74,7 @@ type SelectiveTemplateFromDBModal = "Import Type", "fa-solid fa-cog", importTypeState.ImportType, + "importType", [| ARCtrl.TableJoinOptions.Headers, " Column Headers"; ARCtrl.TableJoinOptions.WithUnit, " ..With Units";