@morscherlab/mint-sdk 1.0.0-beta.2 → 1.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +218 -6
- package/dist/__tests__/components/ActionItem.test.d.ts +1 -0
- package/dist/__tests__/components/AppAvatarMenu.test.d.ts +1 -0
- package/dist/__tests__/components/AppPageSelector.test.d.ts +1 -0
- package/dist/__tests__/components/AppPillNav.test.d.ts +1 -0
- package/dist/__tests__/components/AppPluginSwitcher.test.d.ts +1 -0
- package/dist/__tests__/components/AppToastContainer.test.d.ts +1 -0
- package/dist/__tests__/components/BaseRadioGroup.test.d.ts +1 -0
- package/dist/__tests__/components/BaseSelect.test.d.ts +1 -0
- package/dist/__tests__/components/BaseTabs.test.d.ts +1 -0
- package/dist/__tests__/components/BatchProgressList.test.d.ts +1 -0
- package/dist/__tests__/components/BioTemplateExperimentWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/BioTemplatePackWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/BioTemplatePresetWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/BioTemplateRenderer.test.d.ts +1 -0
- package/dist/__tests__/components/Breadcrumb.test.d.ts +1 -0
- package/dist/__tests__/components/CalendarGridPanel.test.d.ts +1 -0
- package/dist/__tests__/components/ConcentrationInput.test.d.ts +1 -0
- package/dist/__tests__/components/ControlWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/DatePicker.test.d.ts +1 -0
- package/dist/__tests__/components/DateTimePicker.test.d.ts +1 -0
- package/dist/__tests__/components/EmptyState.test.d.ts +1 -0
- package/dist/__tests__/components/ExperimentPopover.test.d.ts +1 -0
- package/dist/__tests__/components/FormBuilder.test.d.ts +1 -0
- package/dist/__tests__/components/FormCompatibility.test.d.ts +1 -0
- package/dist/__tests__/components/GroupAssigner.test.d.ts +1 -0
- package/dist/__tests__/components/GroupingModal.test.d.ts +1 -0
- package/dist/__tests__/components/MultiSelect.test.d.ts +1 -0
- package/dist/__tests__/components/ProtocolStepEditor.test.d.ts +1 -0
- package/dist/__tests__/components/ReagentList.test.d.ts +1 -0
- package/dist/__tests__/components/SampleHierarchyTree.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.test.d.ts +1 -0
- package/dist/__tests__/components/SegmentedControl.test.d.ts +1 -0
- package/dist/__tests__/components/SettingsButton.test.d.ts +1 -0
- package/dist/__tests__/components/SettingsModal.test.d.ts +1 -0
- package/dist/__tests__/components/TagsInput.test.d.ts +1 -0
- package/dist/__tests__/components/ThemeToggle.test.d.ts +1 -0
- package/dist/__tests__/components/TimePicker.test.d.ts +1 -0
- package/dist/__tests__/composables/useBioTemplatePackWorkspace.test.d.ts +1 -0
- package/dist/__tests__/composables/useBioTemplatePresetWorkspace.test.d.ts +1 -0
- package/dist/__tests__/composables/useBioTemplateWorkspace.test.d.ts +1 -0
- package/dist/__tests__/composables/useCalendarGrid.test.d.ts +1 -0
- package/dist/__tests__/composables/useControlSchema.test.d.ts +1 -0
- package/dist/__tests__/composables/useDebouncedWatch.test.d.ts +1 -0
- package/dist/__tests__/composables/useDropdownState.test.d.ts +1 -0
- package/dist/__tests__/composables/useEventListener.test.d.ts +1 -0
- package/dist/__tests__/composables/useExpansionSet.test.d.ts +1 -0
- package/dist/__tests__/composables/useExperimentData.test.d.ts +1 -0
- package/dist/__tests__/composables/useExperimentSelector.test.d.ts +1 -0
- package/dist/__tests__/composables/useGroupAssignment.test.d.ts +1 -0
- package/dist/__tests__/composables/useListSelection.test.d.ts +1 -0
- package/dist/__tests__/composables/usePluginClient.test.d.ts +1 -0
- package/dist/__tests__/composables/usePluginConfig.test.d.ts +1 -0
- package/dist/__tests__/composables/useRequestSyncState.test.d.ts +1 -0
- package/dist/__tests__/composables/useSampleGroups.test.d.ts +1 -0
- package/dist/__tests__/composables/useSelectionLimit.test.d.ts +1 -0
- package/dist/__tests__/composables/useSortedItems.test.d.ts +1 -0
- package/dist/__tests__/composables/useTemplateCollection.test.d.ts +1 -0
- package/dist/__tests__/composables/useTextSearch.test.d.ts +1 -0
- package/dist/__tests__/composables/useTheme.test.d.ts +1 -0
- package/dist/__tests__/composables/useTimeUtils.test.d.ts +1 -0
- package/dist/__tests__/docs/frontendDocsCatalog.test.d.ts +1 -0
- package/dist/__tests__/templates/templates.test.d.ts +1 -0
- package/dist/{auth-DsI0rQ7_.js → auth-QQj2kkze.js} +12 -5
- package/dist/auth-QQj2kkze.js.map +1 -0
- package/dist/components/ActionItem.vue.d.ts +32 -0
- package/dist/components/AppAvatarMenu.vue.d.ts +2 -7
- package/dist/components/AppPageSelector.vue.d.ts +3 -6
- package/dist/components/AppPillNav.vue.d.ts +2 -2
- package/dist/components/AppSidebar.vue.d.ts +56 -3
- package/dist/components/AppToastContainer.vue.d.ts +2 -0
- package/dist/components/AppTopBar.vue.d.ts +41 -10
- package/dist/components/BaseButton.vue.d.ts +1 -1
- package/dist/components/BaseCheckbox.vue.d.ts +1 -1
- package/dist/components/BaseInput.vue.d.ts +2 -2
- package/dist/components/BasePill.vue.d.ts +2 -2
- package/dist/components/BaseRadioGroup.vue.d.ts +3 -3
- package/dist/components/BaseSelect.vue.d.ts +3 -3
- package/dist/components/BaseTabs.vue.d.ts +2 -2
- package/dist/components/BaseTextarea.vue.d.ts +1 -1
- package/dist/components/BaseToggle.vue.d.ts +1 -1
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +117 -0
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +92 -0
- package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +82 -0
- package/dist/components/BioTemplateRenderer.vue.d.ts +29 -0
- package/dist/components/Breadcrumb.vue.d.ts +2 -2
- package/dist/components/Calendar.vue.d.ts +1 -1
- package/dist/components/CalendarGridPanel.vue.d.ts +25 -0
- package/dist/components/CollapsibleCard.vue.d.ts +1 -1
- package/dist/components/ConcentrationInput.vue.d.ts +2 -2
- package/dist/components/ConfirmDialog.vue.d.ts +2 -2
- package/dist/components/ControlWorkspaceView.vue.d.ts +130 -0
- package/dist/components/DatePicker.vue.d.ts +1 -1
- package/dist/components/DateTimePicker.vue.d.ts +3 -3
- package/dist/components/Divider.vue.d.ts +1 -1
- package/dist/components/DropdownButton.vue.d.ts +3 -3
- package/dist/components/EmptyState.vue.d.ts +1 -2
- package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
- package/dist/components/ExperimentTimeline.vue.d.ts +2 -2
- package/dist/components/FileUploader.vue.d.ts +1 -1
- package/dist/components/FitPanel.vue.d.ts +1 -1
- package/dist/components/FormActions.vue.d.ts +4 -4
- package/dist/components/FormBuilder.vue.d.ts +22 -8
- package/dist/components/FormFieldRenderer.vue.d.ts +7 -10
- package/dist/components/FormSection.vue.d.ts +11 -24
- package/dist/components/FormulaInput.vue.d.ts +2 -2
- package/dist/components/MoleculeInput.vue.d.ts +2 -2
- package/dist/components/MultiSelect.vue.d.ts +3 -3
- package/dist/components/NumberInput.vue.d.ts +1 -1
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ProtocolStepEditor.vue.d.ts +3 -1
- package/dist/components/RackEditor.vue.d.ts +2 -2
- package/dist/components/SampleLegend.vue.d.ts +2 -2
- package/dist/components/ScheduleCalendar.vue.d.ts +2 -2
- package/dist/components/SegmentedControl.vue.d.ts +2 -2
- package/dist/components/SequenceInput.vue.d.ts +3 -3
- package/dist/components/SettingsButton.vue.d.ts +2 -2
- package/dist/components/SettingsModal.vue.d.ts +13 -5
- package/dist/components/StatusIndicator.vue.d.ts +1 -1
- package/dist/components/TagsInput.vue.d.ts +3 -2
- package/dist/components/TimePicker.vue.d.ts +3 -3
- package/dist/components/TimeRangeInput.vue.d.ts +1 -1
- package/dist/components/UnitInput.vue.d.ts +2 -2
- package/dist/components/WellPlate.vue.d.ts +8 -8
- package/dist/components/index.d.ts +11 -1
- package/dist/components/index.js +3 -3
- package/dist/components/internal/FormFieldRendererInternal.vue.d.ts +31 -0
- package/dist/components/internal/FormSectionRenderer.vue.d.ts +43 -0
- package/dist/{components-_XqPEhP9.js → components-D_Sr0adg.js} +7290 -6518
- package/dist/components-D_Sr0adg.js.map +1 -0
- package/dist/composables/index.d.ts +21 -2
- package/dist/composables/index.js +4 -3
- package/dist/composables/platformContextHelpers.d.ts +14 -0
- package/dist/composables/useBioTemplateComponents.d.ts +20 -0
- package/dist/composables/useBioTemplateControls.d.ts +6 -0
- package/dist/composables/useBioTemplatePackWorkspace.d.ts +45 -0
- package/dist/composables/useBioTemplatePresetWorkspace.d.ts +74 -0
- package/dist/composables/useBioTemplateWorkspace.d.ts +50 -0
- package/dist/composables/useCalendarGrid.d.ts +26 -0
- package/dist/composables/useControlSchema.d.ts +321 -0
- package/dist/composables/useDebouncedWatch.d.ts +20 -0
- package/dist/composables/useDropdownState.d.ts +19 -0
- package/dist/composables/useEventListener.d.ts +13 -0
- package/dist/composables/useExpansionSet.d.ts +21 -0
- package/dist/composables/useExperimentData.d.ts +10 -0
- package/dist/composables/useExperimentSave.d.ts +31 -2
- package/dist/composables/useExperimentSelector.d.ts +20 -0
- package/dist/composables/useForm.d.ts +2 -0
- package/dist/composables/useGroupAssignment.d.ts +31 -0
- package/dist/composables/useListSelection.d.ts +35 -0
- package/dist/composables/usePlatformContext.d.ts +21 -3
- package/dist/composables/usePluginApi.d.ts +7 -14
- package/dist/composables/usePluginClient.d.ts +109 -0
- package/dist/composables/usePluginConfig.d.ts +12 -0
- package/dist/composables/useRequestSyncState.d.ts +34 -0
- package/dist/composables/useSampleGroups.d.ts +32 -0
- package/dist/composables/useSelectionLimit.d.ts +17 -0
- package/dist/composables/useSortedItems.d.ts +32 -0
- package/dist/composables/useTemplateCollection.d.ts +58 -0
- package/dist/composables/useTextSearch.d.ts +18 -0
- package/dist/composables/useTimeUtils.d.ts +8 -0
- package/dist/{composables-tiZqLu1M.js → composables-C3dpXQN5.js} +228 -146
- package/dist/composables-C3dpXQN5.js.map +1 -0
- package/dist/index.d.ts +12 -3
- package/dist/index.js +6 -5
- package/dist/install.d.ts +7 -2
- package/dist/install.js +2 -2
- package/dist/install.js.map +1 -1
- package/dist/stores/index.js +1 -1
- package/dist/stores/settings.d.ts +4 -1
- package/dist/styles.css +5235 -5977
- package/dist/templates/adapters.d.ts +43 -0
- package/dist/templates/builders.d.ts +63 -0
- package/dist/templates/catalog.d.ts +188 -0
- package/dist/templates/componentBindings.d.ts +58 -0
- package/dist/templates/controlSchemas.d.ts +25 -0
- package/dist/templates/index.d.ts +15 -0
- package/dist/templates/index.js +2 -0
- package/dist/templates/lookup.d.ts +4 -0
- package/dist/templates/packs.d.ts +18 -0
- package/dist/templates/presets.d.ts +90 -0
- package/dist/templates/types.d.ts +531 -0
- package/dist/templates-50NPjaxL.js +9333 -0
- package/dist/templates-50NPjaxL.js.map +1 -0
- package/dist/types/components.d.ts +26 -4
- package/dist/types/form-builder.d.ts +6 -8
- package/dist/types/index.d.ts +2 -2
- package/dist/types/platform.d.ts +7 -1
- package/dist/useScheduleDrag-D4oWdh41.js +4371 -0
- package/dist/useScheduleDrag-D4oWdh41.js.map +1 -0
- package/dist/utils/formModelSync.d.ts +5 -0
- package/dist/utils/items.d.ts +8 -0
- package/dist/utils/options.d.ts +6 -0
- package/dist/utils/pluginIcon.d.ts +9 -0
- package/package.json +7 -2
- package/src/__tests__/components/ActionItem.test.ts +99 -0
- package/src/__tests__/components/AppAvatarMenu.test.ts +27 -0
- package/src/__tests__/components/AppPageSelector.test.ts +134 -0
- package/src/__tests__/components/AppPillNav.test.ts +78 -0
- package/src/__tests__/components/AppPluginSwitcher.test.ts +44 -0
- package/src/__tests__/components/AppSidebar.test.ts +370 -0
- package/src/__tests__/components/AppToastContainer.test.ts +48 -0
- package/src/__tests__/components/AppTopBar.test.ts +383 -0
- package/src/__tests__/components/BaseRadioGroup.test.ts +25 -0
- package/src/__tests__/components/BaseSelect.test.ts +21 -0
- package/src/__tests__/components/BaseTabs.test.ts +25 -0
- package/src/__tests__/components/BatchProgressList.test.ts +52 -0
- package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +153 -0
- package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +161 -0
- package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +281 -0
- package/src/__tests__/components/BioTemplateRenderer.test.ts +71 -0
- package/src/__tests__/components/Breadcrumb.test.ts +23 -0
- package/src/__tests__/components/CalendarGridPanel.test.ts +36 -0
- package/src/__tests__/components/ConcentrationInput.test.ts +45 -0
- package/src/__tests__/components/ControlWorkspaceView.test.ts +1031 -0
- package/src/__tests__/components/DataFrame.test.ts +11 -0
- package/src/__tests__/components/DatePicker.test.ts +45 -0
- package/src/__tests__/components/DateTimePicker.test.ts +48 -0
- package/src/__tests__/components/DropdownButton.test.ts +23 -0
- package/src/__tests__/components/EmptyState.test.ts +23 -0
- package/src/__tests__/components/ExperimentPopover.test.ts +56 -0
- package/src/__tests__/components/FormBuilder.test.ts +296 -0
- package/src/__tests__/components/FormCompatibility.test.ts +94 -0
- package/src/__tests__/components/GroupAssigner.test.ts +30 -0
- package/src/__tests__/components/GroupingModal.test.ts +73 -0
- package/src/__tests__/components/MultiSelect.test.ts +48 -0
- package/src/__tests__/components/ProtocolStepEditor.test.ts +33 -0
- package/src/__tests__/components/ReagentList.test.ts +82 -0
- package/src/__tests__/components/SampleHierarchyTree.test.ts +53 -0
- package/src/__tests__/components/SampleSelector.test.ts +60 -0
- package/src/__tests__/components/SegmentedControl.test.ts +24 -0
- package/src/__tests__/components/SettingsButton.test.ts +44 -0
- package/src/__tests__/components/SettingsModal.test.ts +296 -0
- package/src/__tests__/components/TagsInput.test.ts +75 -0
- package/src/__tests__/components/ThemeToggle.test.ts +47 -0
- package/src/__tests__/components/TimePicker.test.ts +38 -0
- package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +122 -0
- package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +199 -0
- package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +99 -0
- package/src/__tests__/composables/useCalendarGrid.test.ts +38 -0
- package/src/__tests__/composables/useControlSchema.test.ts +919 -0
- package/src/__tests__/composables/useDebouncedWatch.test.ts +93 -0
- package/src/__tests__/composables/useDropdownState.test.ts +95 -0
- package/src/__tests__/composables/useEventListener.test.ts +116 -0
- package/src/__tests__/composables/useExpansionSet.test.ts +62 -0
- package/src/__tests__/composables/useExperimentData.test.ts +4 -0
- package/src/__tests__/composables/useExperimentSave.test.ts +203 -8
- package/src/__tests__/composables/useExperimentSelector.test.ts +164 -0
- package/src/__tests__/composables/useForm.test.ts +58 -0
- package/src/__tests__/composables/useFormBuilder.test.ts +77 -0
- package/src/__tests__/composables/useGroupAssignment.test.ts +73 -0
- package/src/__tests__/composables/useListSelection.test.ts +66 -0
- package/src/__tests__/composables/usePluginClient.test.ts +444 -0
- package/src/__tests__/composables/usePluginConfig.test.ts +5 -0
- package/src/__tests__/composables/useRequestSyncState.test.ts +92 -0
- package/src/__tests__/composables/useSampleGroups.test.ts +66 -0
- package/src/__tests__/composables/useSelectionLimit.test.ts +41 -0
- package/src/__tests__/composables/useSortedItems.test.ts +87 -0
- package/src/__tests__/composables/useTemplateCollection.test.ts +147 -0
- package/src/__tests__/composables/useTextSearch.test.ts +55 -0
- package/src/__tests__/composables/useTheme.test.ts +91 -0
- package/src/__tests__/composables/useTimeUtils.test.ts +35 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +229 -0
- package/src/__tests__/fixtures/templates/dose-response.json +81 -0
- package/src/__tests__/fixtures/templates/plate-map.json +54 -0
- package/src/__tests__/fixtures/templates/qpcr-plate.json +96 -0
- package/src/__tests__/fixtures/templates/sample-sheet.json +71 -0
- package/src/__tests__/templates/templates.test.ts +1043 -0
- package/src/components/ActionItem.vue +82 -0
- package/src/components/AppAvatarMenu.vue +15 -69
- package/src/components/AppLayout.story.vue +25 -25
- package/src/components/AppPageSelector.vue +63 -94
- package/src/components/AppPillNav.vue +44 -39
- package/src/components/AppPluginSwitcher.vue +41 -145
- package/src/components/AppSidebar.story.vue +94 -0
- package/src/components/AppSidebar.vue +187 -12
- package/src/components/{ToastNotification.story.vue → AppToastContainer.story.vue} +6 -6
- package/src/components/AppToastContainer.vue +62 -0
- package/src/components/AppTopBar.story.vue +7 -30
- package/src/components/AppTopBar.vue +251 -57
- package/src/components/BaseModal.vue +3 -5
- package/src/components/BaseRadioGroup.vue +7 -3
- package/src/components/BaseSelect.vue +11 -7
- package/src/components/BaseTabs.vue +6 -4
- package/src/components/BatchProgressList.vue +5 -8
- package/src/components/BioTemplateExperimentWorkspaceView.story.vue +123 -0
- package/src/components/BioTemplateExperimentWorkspaceView.vue +337 -0
- package/src/components/BioTemplatePackWorkspaceView.story.vue +107 -0
- package/src/components/BioTemplatePackWorkspaceView.vue +176 -0
- package/src/components/BioTemplatePresetWorkspaceView.story.vue +151 -0
- package/src/components/BioTemplatePresetWorkspaceView.vue +392 -0
- package/src/components/BioTemplateRenderer.story.vue +57 -0
- package/src/components/BioTemplateRenderer.vue +269 -0
- package/src/components/Breadcrumb.vue +14 -8
- package/src/components/CalendarGridPanel.vue +120 -0
- package/src/components/ConcentrationInput.vue +27 -64
- package/src/components/ControlWorkspaceView.story.vue +336 -0
- package/src/components/ControlWorkspaceView.vue +347 -0
- package/src/components/DataFrame.vue +34 -50
- package/src/components/DatePicker.vue +59 -192
- package/src/components/DateTimePicker.vue +50 -171
- package/src/components/DropdownButton.vue +14 -32
- package/src/components/EmptyState.vue +4 -2
- package/src/components/ExperimentPopover.vue +5 -22
- package/src/components/FormBuilder.vue +124 -27
- package/src/components/FormFieldRenderer.vue +15 -38
- package/src/components/FormSection.vue +20 -73
- package/src/components/GroupAssigner.vue +24 -56
- package/src/components/GroupingModal.story.vue +3 -3
- package/src/components/GroupingModal.vue +30 -391
- package/src/components/MultiSelect.vue +17 -12
- package/src/components/PlateMapEditor.vue +3 -8
- package/src/components/PluginIcon.vue +2 -22
- package/src/components/ProtocolStepEditor.vue +13 -22
- package/src/components/ReagentList.vue +25 -33
- package/src/components/SampleHierarchyTree.vue +12 -23
- package/src/components/SampleSelector.vue +42 -122
- package/src/components/SegmentedControl.vue +7 -3
- package/src/components/SettingsButton.story.vue +1 -1
- package/src/components/SettingsButton.vue +15 -27
- package/src/components/SettingsModal.story.vue +1 -1
- package/src/components/SettingsModal.vue +120 -29
- package/src/components/TagsInput.vue +29 -14
- package/src/components/ThemeToggle.vue +9 -7
- package/src/components/TimePicker.vue +19 -41
- package/src/components/ToastNotification.vue +4 -57
- package/src/components/Tooltip.vue +7 -12
- package/src/components/WellEditPopup.vue +3 -8
- package/src/components/WellPlate.vue +4 -10
- package/src/components/index.ts +11 -1
- package/src/components/internal/FormFieldRendererInternal.vue +50 -0
- package/src/components/internal/FormSectionRenderer.vue +78 -0
- package/src/composables/index.ts +212 -0
- package/src/composables/platformContextHelpers.ts +74 -0
- package/src/composables/useBioTemplateComponents.ts +93 -0
- package/src/composables/useBioTemplateControls.ts +41 -0
- package/src/composables/useBioTemplatePackWorkspace.ts +181 -0
- package/src/composables/useBioTemplatePresetWorkspace.ts +337 -0
- package/src/composables/useBioTemplateWorkspace.ts +139 -0
- package/src/composables/useCalendarGrid.ts +140 -0
- package/src/composables/useControlSchema.ts +1274 -0
- package/src/composables/useDebouncedWatch.ts +119 -0
- package/src/composables/useDropdownState.ts +83 -0
- package/src/composables/useEventListener.ts +111 -0
- package/src/composables/useExpansionSet.ts +117 -0
- package/src/composables/useExperimentData.ts +20 -11
- package/src/composables/useExperimentSave.ts +202 -50
- package/src/composables/useExperimentSelector.ts +86 -72
- package/src/composables/useForm.ts +49 -4
- package/src/composables/useFormBuilder.ts +93 -42
- package/src/composables/useGroupAssignment.ts +148 -0
- package/src/composables/useListSelection.ts +158 -0
- package/src/composables/usePluginApi.ts +7 -14
- package/src/composables/usePluginClient.ts +425 -0
- package/src/composables/usePluginConfig.ts +34 -13
- package/src/composables/useRequestSyncState.ts +126 -0
- package/src/composables/useSampleGroups.ts +126 -0
- package/src/composables/useSelectionLimit.ts +57 -0
- package/src/composables/useSortedItems.ts +118 -0
- package/src/composables/useTemplateCollection.ts +229 -0
- package/src/composables/useTextSearch.ts +60 -0
- package/src/composables/useTheme.ts +2 -28
- package/src/composables/useTimeUtils.ts +26 -2
- package/src/composables/useWellPlateEditor.ts +13 -9
- package/src/index.ts +224 -4
- package/src/install.ts +11 -4
- package/src/stores/settings.ts +13 -9
- package/src/styles/components/app-page-selector.css +23 -0
- package/src/styles/components/app-pill-nav.css +7 -0
- package/src/styles/components/app-top-bar.css +34 -0
- package/src/styles/components/concentration-input.css +3 -142
- package/src/styles/components/empty-state.css +0 -16
- package/src/styles/components/settings-button.css +3 -66
- package/src/styles/components/theme-toggle.css +3 -66
- package/src/styles/index.css +0 -1
- package/src/templates/adapters.ts +785 -0
- package/src/templates/builders.ts +2149 -0
- package/src/templates/catalog.ts +245 -0
- package/src/templates/componentBindings.ts +615 -0
- package/src/templates/controlSchemas.ts +718 -0
- package/src/templates/index.ts +314 -0
- package/src/templates/lookup.ts +18 -0
- package/src/templates/packs.ts +156 -0
- package/src/templates/presets.ts +146 -0
- package/src/templates/types.ts +668 -0
- package/src/types/components.ts +41 -4
- package/src/types/form-builder.ts +7 -2
- package/src/types/index.ts +14 -0
- package/src/types/platform.ts +7 -1
- package/src/utils/formModelSync.ts +52 -0
- package/src/utils/items.ts +28 -0
- package/src/utils/options.ts +23 -0
- package/src/utils/pluginIcon.ts +30 -0
- package/dist/auth-DsI0rQ7_.js.map +0 -1
- package/dist/components-_XqPEhP9.js.map +0 -1
- package/dist/composables-tiZqLu1M.js.map +0 -1
- package/dist/useScheduleDrag-CA9sGNJG.js +0 -7181
- package/dist/useScheduleDrag-CA9sGNJG.js.map +0 -1
- package/src/styles/components/grouping-modal.css +0 -323
|
@@ -1,5 +1,22 @@
|
|
|
1
|
-
import { ref, type Ref } from 'vue'
|
|
1
|
+
import { computed, ref, type ComputedRef, type Ref } from 'vue'
|
|
2
2
|
import { useApi } from './useApi'
|
|
3
|
+
import { useRequestSyncState } from './useRequestSyncState'
|
|
4
|
+
import { createBioTemplatePresetCollection } from '../templates'
|
|
5
|
+
import type {
|
|
6
|
+
CreateLcmsBatchCollectionOptions,
|
|
7
|
+
CreateQpcrExpressionCollectionOptions,
|
|
8
|
+
CreateWellPlateScreenCollectionOptions,
|
|
9
|
+
TemplatePresetId,
|
|
10
|
+
} from '../templates'
|
|
11
|
+
import {
|
|
12
|
+
getInjectedPlatformContext,
|
|
13
|
+
resolveCurrentExperimentId,
|
|
14
|
+
} from './platformContextHelpers'
|
|
15
|
+
|
|
16
|
+
type TemplatePresetCollectionOptions =
|
|
17
|
+
| CreateWellPlateScreenCollectionOptions
|
|
18
|
+
| CreateQpcrExpressionCollectionOptions
|
|
19
|
+
| CreateLcmsBatchCollectionOptions
|
|
3
20
|
|
|
4
21
|
export interface UseExperimentSaveOptions {
|
|
5
22
|
/** Default plugin_id for all save/load calls. */
|
|
@@ -11,16 +28,39 @@ export interface UseExperimentSaveOptions {
|
|
|
11
28
|
}
|
|
12
29
|
|
|
13
30
|
export interface UseExperimentSaveReturn {
|
|
31
|
+
/** Current experiment id from platform injection, URL query, or URL path. */
|
|
32
|
+
currentExperimentId: ComputedRef<number | undefined>
|
|
33
|
+
/** Whether a current experiment id is available. */
|
|
34
|
+
hasCurrentExperiment: ComputedRef<boolean>
|
|
14
35
|
/** Whether a save operation is in progress. */
|
|
15
36
|
isSaving: Ref<boolean>
|
|
37
|
+
/** Whether a load or delete operation is in progress. */
|
|
38
|
+
isLoading: Ref<boolean>
|
|
16
39
|
/** Error message from the last failed operation, or null. */
|
|
17
40
|
error: Ref<string | null>
|
|
41
|
+
/** Timestamp of the last successful load, or null. */
|
|
42
|
+
lastLoadedAt: Ref<Date | null>
|
|
18
43
|
/** Timestamp of the last successful save, or null. */
|
|
19
44
|
lastSavedAt: Ref<Date | null>
|
|
20
45
|
/** Save design data for an experiment. */
|
|
21
46
|
saveDesign: (experimentId: number, data: Record<string, unknown>) => Promise<boolean>
|
|
47
|
+
/** Save design data for the current experiment. */
|
|
48
|
+
saveCurrentDesign: (data: Record<string, unknown>) => Promise<boolean>
|
|
49
|
+
/** Create and save a built-in biology template preset as experiment design data. */
|
|
50
|
+
saveTemplatePreset: (
|
|
51
|
+
experimentId: number,
|
|
52
|
+
name: TemplatePresetId | string,
|
|
53
|
+
options?: TemplatePresetCollectionOptions,
|
|
54
|
+
) => Promise<boolean>
|
|
55
|
+
/** Create and save a built-in biology template preset for the current experiment. */
|
|
56
|
+
saveCurrentTemplatePreset: (
|
|
57
|
+
name: TemplatePresetId | string,
|
|
58
|
+
options?: TemplatePresetCollectionOptions,
|
|
59
|
+
) => Promise<boolean>
|
|
22
60
|
/** Save analysis result for an experiment. */
|
|
23
61
|
saveAnalysis: (experimentId: number, result: Record<string, unknown>) => Promise<boolean>
|
|
62
|
+
/** Save analysis result for the current experiment. */
|
|
63
|
+
saveCurrentAnalysis: (result: Record<string, unknown>) => Promise<boolean>
|
|
24
64
|
/** Save design and/or analysis in one call. */
|
|
25
65
|
save: (experimentId: number, opts: {
|
|
26
66
|
design?: Record<string, unknown>
|
|
@@ -28,28 +68,59 @@ export interface UseExperimentSaveReturn {
|
|
|
28
68
|
}) => Promise<boolean>
|
|
29
69
|
/** Load design data for an experiment. */
|
|
30
70
|
loadDesign: (experimentId: number) => Promise<Record<string, unknown> | null>
|
|
71
|
+
/** Load design data for the current experiment. */
|
|
72
|
+
loadCurrentDesign: () => Promise<Record<string, unknown> | null>
|
|
31
73
|
/** Load analysis result for an experiment. */
|
|
32
74
|
loadAnalysis: (experimentId: number) => Promise<Record<string, unknown> | null>
|
|
75
|
+
/** Load analysis result for the current experiment. */
|
|
76
|
+
loadCurrentAnalysis: () => Promise<Record<string, unknown> | null>
|
|
33
77
|
/** Delete design data for an experiment. */
|
|
34
78
|
deleteDesign: (experimentId: number) => Promise<boolean>
|
|
79
|
+
/** Delete design data for the current experiment. */
|
|
80
|
+
deleteCurrentDesign: () => Promise<boolean>
|
|
35
81
|
/** Delete analysis result for an experiment. */
|
|
36
82
|
deleteAnalysis: (experimentId: number) => Promise<boolean>
|
|
83
|
+
/** Delete analysis result for the current experiment. */
|
|
84
|
+
deleteCurrentAnalysis: () => Promise<boolean>
|
|
85
|
+
/** Require and return the current experiment id, or throw when missing. */
|
|
86
|
+
requireCurrentExperimentId: () => number
|
|
37
87
|
}
|
|
38
88
|
|
|
39
|
-
/** Persists
|
|
89
|
+
/** Persists experiment design, analysis, and built-in template preset data through the platform API. */
|
|
40
90
|
export function useExperimentSave(
|
|
41
91
|
options: UseExperimentSaveOptions = {},
|
|
42
92
|
): UseExperimentSaveReturn {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
93
|
+
const injectedContext = getInjectedPlatformContext()
|
|
94
|
+
const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl })
|
|
95
|
+
const pluginId = options.pluginId ?? injectedContext?.plugin?.id
|
|
45
96
|
const schemaVersion = options.schemaVersion ?? '1.0'
|
|
97
|
+
const currentExperimentId = computed<number | undefined>(() => {
|
|
98
|
+
return resolveCurrentExperimentId(injectedContext)
|
|
99
|
+
})
|
|
100
|
+
const hasCurrentExperiment = computed(() => currentExperimentId.value !== undefined)
|
|
46
101
|
|
|
102
|
+
const request = useRequestSyncState('Experiment request failed.')
|
|
47
103
|
const isSaving = ref(false)
|
|
48
|
-
const
|
|
49
|
-
const
|
|
104
|
+
const isLoading = ref(false)
|
|
105
|
+
const error = request.error
|
|
106
|
+
const lastLoadedAt = request.lastLoadedAt
|
|
107
|
+
const lastSavedAt = request.lastSavedAt
|
|
50
108
|
|
|
51
|
-
function
|
|
52
|
-
|
|
109
|
+
function requireCurrentExperimentId(): number {
|
|
110
|
+
const id = currentExperimentId.value
|
|
111
|
+
if (id === undefined) {
|
|
112
|
+
throw new Error('[MINT SDK] No current experiment is selected.')
|
|
113
|
+
}
|
|
114
|
+
return id
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function currentExperimentIdOrError(action: string): number | undefined {
|
|
118
|
+
const id = currentExperimentId.value
|
|
119
|
+
if (id === undefined) {
|
|
120
|
+
request.setError(`No current experiment is selected for ${action}`)
|
|
121
|
+
return undefined
|
|
122
|
+
}
|
|
123
|
+
return id
|
|
53
124
|
}
|
|
54
125
|
|
|
55
126
|
async function saveDesign(
|
|
@@ -57,78 +128,102 @@ export function useExperimentSave(
|
|
|
57
128
|
data: Record<string, unknown>,
|
|
58
129
|
): Promise<boolean> {
|
|
59
130
|
if (!pluginId) {
|
|
60
|
-
|
|
131
|
+
request.setError('pluginId is required for saveDesign')
|
|
61
132
|
return false
|
|
62
133
|
}
|
|
63
134
|
isSaving.value = true
|
|
64
|
-
error.value = null
|
|
65
135
|
try {
|
|
66
|
-
await
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
136
|
+
await request.run(async () => {
|
|
137
|
+
await api.put(`/experiments/${experimentId}/data`, {
|
|
138
|
+
plugin_id: pluginId,
|
|
139
|
+
data,
|
|
140
|
+
schema_version: schemaVersion,
|
|
141
|
+
})
|
|
142
|
+
}, { success: 'save' })
|
|
72
143
|
return true
|
|
73
|
-
} catch
|
|
74
|
-
setError(e)
|
|
144
|
+
} catch {
|
|
75
145
|
return false
|
|
76
146
|
} finally {
|
|
77
147
|
isSaving.value = false
|
|
78
148
|
}
|
|
79
149
|
}
|
|
80
150
|
|
|
151
|
+
async function saveCurrentDesign(data: Record<string, unknown>): Promise<boolean> {
|
|
152
|
+
const experimentId = currentExperimentIdOrError('saveDesign')
|
|
153
|
+
return experimentId === undefined ? false : saveDesign(experimentId, data)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function saveTemplatePreset(
|
|
157
|
+
experimentId: number,
|
|
158
|
+
name: TemplatePresetId | string,
|
|
159
|
+
presetOptions: TemplatePresetCollectionOptions = {},
|
|
160
|
+
): Promise<boolean> {
|
|
161
|
+
const collection = createBioTemplatePresetCollection(name, presetOptions)
|
|
162
|
+
return saveDesign(experimentId, collection as unknown as Record<string, unknown>)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function saveCurrentTemplatePreset(
|
|
166
|
+
name: TemplatePresetId | string,
|
|
167
|
+
presetOptions: TemplatePresetCollectionOptions = {},
|
|
168
|
+
): Promise<boolean> {
|
|
169
|
+
const experimentId = currentExperimentIdOrError('saveTemplatePreset')
|
|
170
|
+
return experimentId === undefined ? false : saveTemplatePreset(experimentId, name, presetOptions)
|
|
171
|
+
}
|
|
172
|
+
|
|
81
173
|
async function saveAnalysis(
|
|
82
174
|
experimentId: number,
|
|
83
175
|
result: Record<string, unknown>,
|
|
84
176
|
): Promise<boolean> {
|
|
85
177
|
if (!pluginId) {
|
|
86
|
-
|
|
178
|
+
request.setError('pluginId is required for saveAnalysis')
|
|
87
179
|
return false
|
|
88
180
|
}
|
|
89
181
|
isSaving.value = true
|
|
90
|
-
error.value = null
|
|
91
182
|
try {
|
|
92
|
-
await
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
183
|
+
await request.run(async () => {
|
|
184
|
+
await api.put(`/experiments/${experimentId}/results/${pluginId}`, {
|
|
185
|
+
result,
|
|
186
|
+
})
|
|
187
|
+
}, { success: 'save' })
|
|
96
188
|
return true
|
|
97
|
-
} catch
|
|
98
|
-
setError(e)
|
|
189
|
+
} catch {
|
|
99
190
|
return false
|
|
100
191
|
} finally {
|
|
101
192
|
isSaving.value = false
|
|
102
193
|
}
|
|
103
194
|
}
|
|
104
195
|
|
|
196
|
+
async function saveCurrentAnalysis(result: Record<string, unknown>): Promise<boolean> {
|
|
197
|
+
const experimentId = currentExperimentIdOrError('saveAnalysis')
|
|
198
|
+
return experimentId === undefined ? false : saveAnalysis(experimentId, result)
|
|
199
|
+
}
|
|
200
|
+
|
|
105
201
|
async function save(
|
|
106
202
|
experimentId: number,
|
|
107
203
|
opts: { design?: Record<string, unknown>; analysis?: Record<string, unknown> },
|
|
108
204
|
): Promise<boolean> {
|
|
109
205
|
if ((opts.design || opts.analysis) && !pluginId) {
|
|
110
|
-
|
|
206
|
+
request.setError('pluginId is required for save')
|
|
111
207
|
return false
|
|
112
208
|
}
|
|
113
209
|
isSaving.value = true
|
|
114
|
-
error.value = null
|
|
115
210
|
try {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
211
|
+
await request.run(async () => {
|
|
212
|
+
if (opts.design) {
|
|
213
|
+
await api.put(`/experiments/${experimentId}/data`, {
|
|
214
|
+
plugin_id: pluginId,
|
|
215
|
+
data: opts.design,
|
|
216
|
+
schema_version: schemaVersion,
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
if (opts.analysis) {
|
|
220
|
+
await api.put(`/experiments/${experimentId}/results/${pluginId}`, {
|
|
221
|
+
result: opts.analysis,
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
}, { success: 'save' })
|
|
129
225
|
return true
|
|
130
|
-
} catch
|
|
131
|
-
setError(e)
|
|
226
|
+
} catch {
|
|
132
227
|
return false
|
|
133
228
|
} finally {
|
|
134
229
|
isSaving.value = false
|
|
@@ -138,55 +233,112 @@ export function useExperimentSave(
|
|
|
138
233
|
async function loadDesign(
|
|
139
234
|
experimentId: number,
|
|
140
235
|
): Promise<Record<string, unknown> | null> {
|
|
236
|
+
isLoading.value = true
|
|
141
237
|
try {
|
|
142
|
-
return await
|
|
238
|
+
return await request.run(
|
|
239
|
+
() => api.get<Record<string, unknown>>(`/experiments/${experimentId}/data`),
|
|
240
|
+
{ success: 'load' },
|
|
241
|
+
)
|
|
143
242
|
} catch {
|
|
144
243
|
return null
|
|
244
|
+
} finally {
|
|
245
|
+
isLoading.value = false
|
|
145
246
|
}
|
|
146
247
|
}
|
|
147
248
|
|
|
249
|
+
async function loadCurrentDesign(): Promise<Record<string, unknown> | null> {
|
|
250
|
+
const experimentId = currentExperimentIdOrError('loadDesign')
|
|
251
|
+
return experimentId === undefined ? null : loadDesign(experimentId)
|
|
252
|
+
}
|
|
253
|
+
|
|
148
254
|
async function loadAnalysis(
|
|
149
255
|
experimentId: number,
|
|
150
256
|
): Promise<Record<string, unknown> | null> {
|
|
151
|
-
if (!pluginId)
|
|
257
|
+
if (!pluginId) {
|
|
258
|
+
request.setError('pluginId is required for loadAnalysis')
|
|
259
|
+
return null
|
|
260
|
+
}
|
|
261
|
+
isLoading.value = true
|
|
152
262
|
try {
|
|
153
|
-
return await
|
|
154
|
-
|
|
263
|
+
return await request.run(
|
|
264
|
+
() => api.get<Record<string, unknown>>(
|
|
265
|
+
`/experiments/${experimentId}/results/${pluginId}`,
|
|
266
|
+
),
|
|
267
|
+
{ success: 'load' },
|
|
155
268
|
)
|
|
156
269
|
} catch {
|
|
157
270
|
return null
|
|
271
|
+
} finally {
|
|
272
|
+
isLoading.value = false
|
|
158
273
|
}
|
|
159
274
|
}
|
|
160
275
|
|
|
276
|
+
async function loadCurrentAnalysis(): Promise<Record<string, unknown> | null> {
|
|
277
|
+
const experimentId = currentExperimentIdOrError('loadAnalysis')
|
|
278
|
+
return experimentId === undefined ? null : loadAnalysis(experimentId)
|
|
279
|
+
}
|
|
280
|
+
|
|
161
281
|
async function deleteDesign(experimentId: number): Promise<boolean> {
|
|
282
|
+
isLoading.value = true
|
|
162
283
|
try {
|
|
163
|
-
await api.delete(`/experiments/${experimentId}/data`)
|
|
284
|
+
await request.run(() => api.delete(`/experiments/${experimentId}/data`))
|
|
164
285
|
return true
|
|
165
286
|
} catch {
|
|
166
287
|
return false
|
|
288
|
+
} finally {
|
|
289
|
+
isLoading.value = false
|
|
167
290
|
}
|
|
168
291
|
}
|
|
169
292
|
|
|
293
|
+
async function deleteCurrentDesign(): Promise<boolean> {
|
|
294
|
+
const experimentId = currentExperimentIdOrError('deleteDesign')
|
|
295
|
+
return experimentId === undefined ? false : deleteDesign(experimentId)
|
|
296
|
+
}
|
|
297
|
+
|
|
170
298
|
async function deleteAnalysis(experimentId: number): Promise<boolean> {
|
|
171
|
-
if (!pluginId)
|
|
299
|
+
if (!pluginId) {
|
|
300
|
+
request.setError('pluginId is required for deleteAnalysis')
|
|
301
|
+
return false
|
|
302
|
+
}
|
|
303
|
+
isLoading.value = true
|
|
172
304
|
try {
|
|
173
|
-
await api.delete(`/experiments/${experimentId}/results/${pluginId}`)
|
|
305
|
+
await request.run(() => api.delete(`/experiments/${experimentId}/results/${pluginId}`))
|
|
174
306
|
return true
|
|
175
307
|
} catch {
|
|
176
308
|
return false
|
|
309
|
+
} finally {
|
|
310
|
+
isLoading.value = false
|
|
177
311
|
}
|
|
178
312
|
}
|
|
179
313
|
|
|
314
|
+
async function deleteCurrentAnalysis(): Promise<boolean> {
|
|
315
|
+
const experimentId = currentExperimentIdOrError('deleteAnalysis')
|
|
316
|
+
return experimentId === undefined ? false : deleteAnalysis(experimentId)
|
|
317
|
+
}
|
|
318
|
+
|
|
180
319
|
return {
|
|
320
|
+
currentExperimentId,
|
|
321
|
+
hasCurrentExperiment,
|
|
181
322
|
isSaving,
|
|
323
|
+
isLoading,
|
|
182
324
|
error,
|
|
325
|
+
lastLoadedAt,
|
|
183
326
|
lastSavedAt,
|
|
184
327
|
saveDesign,
|
|
328
|
+
saveCurrentDesign,
|
|
329
|
+
saveTemplatePreset,
|
|
330
|
+
saveCurrentTemplatePreset,
|
|
185
331
|
saveAnalysis,
|
|
332
|
+
saveCurrentAnalysis,
|
|
186
333
|
save,
|
|
187
334
|
loadDesign,
|
|
335
|
+
loadCurrentDesign,
|
|
188
336
|
loadAnalysis,
|
|
337
|
+
loadCurrentAnalysis,
|
|
189
338
|
deleteDesign,
|
|
339
|
+
deleteCurrentDesign,
|
|
190
340
|
deleteAnalysis,
|
|
341
|
+
deleteCurrentAnalysis,
|
|
342
|
+
requireCurrentExperimentId,
|
|
191
343
|
}
|
|
192
344
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { ref, reactive, computed, watch,
|
|
1
|
+
import { ref, reactive, computed, watch, type Ref, type ComputedRef } from 'vue'
|
|
2
2
|
import { useApi } from './useApi'
|
|
3
|
+
import { useDebouncedWatch } from './useDebouncedWatch'
|
|
4
|
+
import { useRequestSyncState } from './useRequestSyncState'
|
|
3
5
|
import { datePresetToISO } from './experiment-utils'
|
|
4
6
|
import type {
|
|
5
7
|
ExperimentSummary,
|
|
@@ -28,27 +30,47 @@ export interface UseExperimentSelectorOptions {
|
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export interface UseExperimentSelectorReturn {
|
|
33
|
+
/** Current page of fetched experiment rows. */
|
|
31
34
|
experiments: Ref<ExperimentSummary[]>
|
|
35
|
+
/** Total matching experiment count reported by the platform. */
|
|
32
36
|
total: Ref<number>
|
|
37
|
+
/** Currently selected experiment, or null. */
|
|
33
38
|
selectedExperiment: Ref<ExperimentSummary | null>
|
|
39
|
+
/** Mutable filters used for the next fetch. */
|
|
34
40
|
filters: ExperimentFilters
|
|
41
|
+
/** Whether experiment rows are currently loading. */
|
|
35
42
|
isLoading: Ref<boolean>
|
|
43
|
+
/** Error message from the last failed fetch, or null. */
|
|
36
44
|
error: Ref<string | null>
|
|
45
|
+
/** Timestamp of the last successful experiment fetch, or null. */
|
|
46
|
+
lastLoadedAt: Ref<Date | null>
|
|
47
|
+
/** Current zero-based page index. */
|
|
37
48
|
page: Ref<number>
|
|
49
|
+
/** Whether another page can be loaded. */
|
|
38
50
|
hasMore: ComputedRef<boolean>
|
|
39
51
|
// Sort
|
|
52
|
+
/** Combined sort key in the form `<field>:<asc|desc>`. */
|
|
40
53
|
sortKey: Ref<string>
|
|
41
54
|
// Filter options
|
|
55
|
+
/** Available experiment type options for filter UI. */
|
|
42
56
|
experimentTypes: Ref<ExperimentTypeOption[]>
|
|
57
|
+
/** Available project options for filter UI. */
|
|
43
58
|
projects: Ref<SelectOption<string>[]>
|
|
44
59
|
// Grouped view
|
|
60
|
+
/** Fetched experiments grouped by project name. */
|
|
45
61
|
groupedByProject: ComputedRef<[string, ExperimentSummary[]][]>
|
|
46
62
|
// Methods
|
|
63
|
+
/** Fetch the current page with the current filters. */
|
|
47
64
|
fetch: () => Promise<void>
|
|
65
|
+
/** Fetch and append the next page when available. */
|
|
48
66
|
loadMore: () => Promise<void>
|
|
67
|
+
/** Clear fetched rows and refetch page zero. */
|
|
49
68
|
reset: () => void
|
|
69
|
+
/** Select an experiment. */
|
|
50
70
|
select: (experiment: ExperimentSummary) => void
|
|
71
|
+
/** Clear selection and filters. */
|
|
51
72
|
clear: () => void
|
|
73
|
+
/** Fetch experiment type and project filter options. */
|
|
52
74
|
fetchFilterOptions: () => Promise<void>
|
|
53
75
|
}
|
|
54
76
|
|
|
@@ -63,8 +85,10 @@ export function useExperimentSelector(
|
|
|
63
85
|
const experiments = ref<ExperimentSummary[]>([])
|
|
64
86
|
const total = ref(0)
|
|
65
87
|
const selectedExperiment = ref<ExperimentSummary | null>(null)
|
|
66
|
-
const
|
|
67
|
-
const
|
|
88
|
+
const request = useRequestSyncState('Failed to fetch experiments')
|
|
89
|
+
const isLoading = request.loading
|
|
90
|
+
const error = request.error
|
|
91
|
+
const lastLoadedAt = request.lastLoadedAt
|
|
68
92
|
const page = ref(0)
|
|
69
93
|
|
|
70
94
|
// Sort: combined key like "created_at:desc"
|
|
@@ -94,72 +118,69 @@ export function useExperimentSelector(
|
|
|
94
118
|
}
|
|
95
119
|
|
|
96
120
|
async function fetchExperiments(): Promise<void> {
|
|
97
|
-
isLoading.value = true
|
|
98
|
-
error.value = null
|
|
99
121
|
try {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
await request.run(async () => {
|
|
123
|
+
const params = new URLSearchParams()
|
|
124
|
+
// Priority: explicit option > platform context (single type) > filter dropdown > no filter
|
|
125
|
+
const allowedTypes = getPlatformContext()?.allowedExperimentTypes
|
|
126
|
+
const effectiveType = experimentType
|
|
127
|
+
?? (allowedTypes?.length === 1 ? allowedTypes[0] : undefined)
|
|
128
|
+
?? filters.experimentType
|
|
129
|
+
?? undefined
|
|
130
|
+
if (effectiveType) params.set('experiment_type', effectiveType)
|
|
131
|
+
if (filters.status) params.set('status', filters.status)
|
|
132
|
+
if (filters.search) params.set('search', filters.search)
|
|
133
|
+
if (filters.project) params.set('project', filters.project)
|
|
134
|
+
|
|
135
|
+
// Sort params
|
|
136
|
+
const { sortBy, sortOrder } = parseSortKey()
|
|
137
|
+
params.set('sort_by', sortBy)
|
|
138
|
+
params.set('sort_order', sortOrder)
|
|
139
|
+
|
|
140
|
+
// Date preset → created_after
|
|
141
|
+
if (filters.datePreset) {
|
|
142
|
+
params.set('created_after', datePresetToISO(filters.datePreset))
|
|
143
|
+
}
|
|
121
144
|
|
|
122
|
-
|
|
123
|
-
|
|
145
|
+
params.set('limit', String(limit))
|
|
146
|
+
params.set('skip', String(page.value * limit))
|
|
124
147
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
148
|
+
const query = params.toString()
|
|
149
|
+
const base = platformBase ?? ''
|
|
150
|
+
const url = `${base}/experiments${query ? `?${query}` : ''}`
|
|
151
|
+
const data = await api.get<ExperimentListResponse>(url)
|
|
129
152
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
153
|
+
// Client-side filter for multiple allowed types (backend only supports single type)
|
|
154
|
+
let filtered = data.experiments
|
|
155
|
+
if (!effectiveType && allowedTypes && allowedTypes.length > 1) {
|
|
156
|
+
const typeSet = new Set(allowedTypes)
|
|
157
|
+
filtered = filtered.filter(e => typeSet.has(e.experiment_type))
|
|
158
|
+
}
|
|
136
159
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
} else {
|
|
140
|
-
experiments.value = [...experiments.value, ...filtered]
|
|
141
|
-
}
|
|
142
|
-
// When client-side filtering is active (multiple allowedTypes), we can't
|
|
143
|
-
// use data.total since it counts all types. Check if server has more pages.
|
|
144
|
-
if (!effectiveType && allowedTypes && allowedTypes.length > 1) {
|
|
145
|
-
if (data.experiments.length < limit) {
|
|
146
|
-
// Server returned less than a full page — no more data
|
|
147
|
-
total.value = experiments.value.length
|
|
160
|
+
if (page.value === 0) {
|
|
161
|
+
experiments.value = filtered
|
|
148
162
|
} else {
|
|
149
|
-
|
|
150
|
-
total.value = experiments.value.length + 1
|
|
163
|
+
experiments.value = [...experiments.value, ...filtered]
|
|
151
164
|
}
|
|
152
|
-
|
|
153
|
-
total.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
165
|
+
// When client-side filtering is active (multiple allowedTypes), we can't
|
|
166
|
+
// use data.total since it counts all types. Check if server has more pages.
|
|
167
|
+
if (!effectiveType && allowedTypes && allowedTypes.length > 1) {
|
|
168
|
+
if (data.experiments.length < limit) {
|
|
169
|
+
// Server returned less than a full page — no more data
|
|
170
|
+
total.value = experiments.value.length
|
|
171
|
+
} else {
|
|
172
|
+
// Might be more pages on the server
|
|
173
|
+
total.value = experiments.value.length + 1
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
total.value = data.total
|
|
177
|
+
}
|
|
178
|
+
}, { success: 'load', errorMessage: 'Failed to fetch experiments' })
|
|
179
|
+
} catch {
|
|
157
180
|
if (page.value === 0) {
|
|
158
181
|
experiments.value = []
|
|
159
182
|
total.value = 0
|
|
160
183
|
}
|
|
161
|
-
} finally {
|
|
162
|
-
isLoading.value = false
|
|
163
184
|
}
|
|
164
185
|
}
|
|
165
186
|
|
|
@@ -215,6 +236,7 @@ export function useExperimentSelector(
|
|
|
215
236
|
filters.datePreset = undefined
|
|
216
237
|
sortKey.value = 'created_at:desc'
|
|
217
238
|
page.value = 0
|
|
239
|
+
request.clearError()
|
|
218
240
|
}
|
|
219
241
|
|
|
220
242
|
// Group experiments by project (client-side)
|
|
@@ -237,34 +259,25 @@ export function useExperimentSelector(
|
|
|
237
259
|
})
|
|
238
260
|
})
|
|
239
261
|
|
|
240
|
-
|
|
241
|
-
let debounceTimer: ReturnType<typeof setTimeout> | null = null
|
|
242
|
-
watch(
|
|
262
|
+
const searchWatch = useDebouncedWatch(
|
|
243
263
|
() => filters.search,
|
|
244
264
|
() => {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
page.value = 0
|
|
248
|
-
fetchExperiments()
|
|
249
|
-
}, 300)
|
|
265
|
+
page.value = 0
|
|
266
|
+
fetchExperiments()
|
|
250
267
|
},
|
|
268
|
+
{ delay: 300 },
|
|
251
269
|
)
|
|
252
270
|
|
|
253
271
|
// Immediate watch on discrete filters and sort (cancel any pending search debounce)
|
|
254
272
|
watch(
|
|
255
273
|
() => [filters.status, filters.project, filters.experimentType, filters.datePreset, sortKey.value],
|
|
256
274
|
() => {
|
|
257
|
-
|
|
258
|
-
debounceTimer = null
|
|
275
|
+
searchWatch.cancel()
|
|
259
276
|
page.value = 0
|
|
260
277
|
fetchExperiments()
|
|
261
278
|
},
|
|
262
279
|
)
|
|
263
280
|
|
|
264
|
-
onScopeDispose(() => {
|
|
265
|
-
if (debounceTimer) clearTimeout(debounceTimer)
|
|
266
|
-
})
|
|
267
|
-
|
|
268
281
|
if (immediate) {
|
|
269
282
|
fetchExperiments()
|
|
270
283
|
}
|
|
@@ -276,6 +289,7 @@ export function useExperimentSelector(
|
|
|
276
289
|
filters,
|
|
277
290
|
isLoading,
|
|
278
291
|
error,
|
|
292
|
+
lastLoadedAt,
|
|
279
293
|
page,
|
|
280
294
|
hasMore,
|
|
281
295
|
sortKey,
|