@morscherlab/mint-sdk 1.4.0-beta.8 → 1.4.0
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 +14 -3
- package/dist/{ActionMenu-CScINahU.js → AlertBox-DwgNTEqf.js} +40 -209
- package/dist/AlertBox-DwgNTEqf.js.map +1 -0
- package/dist/{AppSidebar-BmiiPfz9.js → AppSidebar-CZZaxni0.js} +1249 -239
- package/dist/AppSidebar-CZZaxni0.js.map +1 -0
- package/dist/{ConfirmDialog-CJ7hqYUP.js → ConfirmDialog-CjWoxAD1.js} +2 -2
- package/dist/{ConfirmDialog-CJ7hqYUP.js.map → ConfirmDialog-CjWoxAD1.js.map} +1 -1
- package/dist/ExperimentPopover-DujiuQIO.js +337 -0
- package/dist/ExperimentPopover-DujiuQIO.js.map +1 -0
- package/dist/{ExperimentSelectorModal-D-9z7cNx.js → ExperimentSelectorModal-B4j4eUOJ.js} +244 -211
- package/dist/ExperimentSelectorModal-B4j4eUOJ.js.map +1 -0
- package/dist/{SegmentedControl-DERBN-Xd.js → SegmentedControl-DyUPH3I5.js} +2 -2
- package/dist/{SegmentedControl-DERBN-Xd.js.map → SegmentedControl-DyUPH3I5.js.map} +1 -1
- package/dist/{SettingsModal-CSLE31yg.js → SettingsModal-D2MOOtuO.js} +28 -27
- package/dist/SettingsModal-D2MOOtuO.js.map +1 -0
- package/dist/__stories__/experiment-helpers.d.ts +10 -0
- package/dist/__tests__/components/AppTopBarOnboarding.test.d.ts +1 -0
- package/dist/__tests__/components/FloatingPanelsInModal.test.d.ts +1 -0
- package/dist/__tests__/composables/useFloatingPanel.test.d.ts +1 -0
- package/dist/__tests__/composables/useOnboarding.test.d.ts +1 -0
- package/dist/__tests__/styles/paletteTokens.test.d.ts +1 -0
- package/dist/__tests__/styles/v2AuditRules.test.d.ts +1 -0
- package/dist/components/AppSidebar.vue.d.ts +1 -1
- package/dist/components/AppTopBar.vue.d.ts +20 -5
- package/dist/components/ArtifactSaveDialog.vue.d.ts +9 -1
- package/dist/components/AuditTrail.vue.d.ts +1 -1
- package/dist/components/BaseButton.vue.d.ts +4 -2
- package/dist/components/BaseSelect.vue.d.ts +2 -2
- package/dist/components/ChartContainer.vue.d.ts +2 -0
- package/dist/components/DatePicker.vue.d.ts +1 -1
- package/dist/components/ExperimentPopover.vue.d.ts +18 -0
- package/dist/components/ExperimentSelectorModal.vue.d.ts +15 -5
- package/dist/components/FilePicker.vue.d.ts +1 -1
- package/dist/components/FileUploader.vue.d.ts +2 -2
- package/dist/components/JobsStatusTray.vue.d.ts +15 -4
- package/dist/components/PluginIcon.vue.d.ts +2 -2
- package/dist/components/PluginWorkspaceView.props.d.ts +3 -0
- package/dist/components/PluginWorkspaceView.vue.d.ts +3 -0
- package/dist/components/SampleSelector.vue.d.ts +6 -6
- package/dist/components/SequenceProgressBar.vue.d.ts +3 -0
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +3 -3
- package/dist/components/StepWizard.vue.d.ts +3 -0
- package/dist/components/internal/AppTopBarOnboardingInternal.vue.d.ts +7 -0
- package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -1
- package/dist/components/internal/AutoGroupModalInternal.vue.d.ts +3 -3
- package/dist/components/internal/DatePickerTimeInternal.vue.d.ts +1 -1
- package/dist/components/internal/OnboardingLayerInternal.vue.d.ts +7 -0
- package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +2 -7
- package/dist/components/internal/SelectMultipleInternal.vue.d.ts +4 -0
- package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +8 -0
- package/dist/composables/createClient.d.ts +2 -0
- package/dist/composables/index.d.ts +1 -0
- package/dist/composables/pluginIdentity.d.ts +24 -0
- package/dist/composables/pluginWorkspaceModel.d.ts +2 -2
- package/dist/composables/useAppExperiment.d.ts +8 -0
- package/dist/composables/useCalendarGrid.d.ts +1 -1
- package/dist/composables/useExperimentSelector.d.ts +6 -2
- package/dist/composables/useFileImport.d.ts +7 -0
- package/dist/composables/useFloatingPanel.d.ts +29 -0
- package/dist/composables/useJobCenter.d.ts +41 -3
- package/dist/composables/useOnboarding.d.ts +119 -0
- package/dist/composables/usePlatform.d.ts +1 -0
- package/dist/{controlSchemaModel-CP5-_nAj.js → controlSchemaModel-Bsg0t5Ab.js} +2 -2
- package/dist/{controlSchemaModel-CP5-_nAj.js.map → controlSchemaModel-Bsg0t5Ab.js.map} +1 -1
- package/dist/{data-C2PwI_dn.js → data-Boeh4uPI.js} +180 -125
- package/dist/data-Boeh4uPI.js.map +1 -0
- package/dist/data.d.ts +1 -1
- package/dist/data.js +9 -9
- package/dist/{experiment-43OJtH7R.js → experiment-DyO54FQB.js} +25 -61
- package/dist/experiment-DyO54FQB.js.map +1 -0
- package/dist/{experimentStatus-BZmU4czV.js → experimentStatus-DCn2QKG7.js} +3 -3
- package/dist/{experimentStatus-BZmU4czV.js.map → experimentStatus-DCn2QKG7.js.map} +1 -1
- package/dist/index.js +20 -20
- package/dist/{install-DAID6c2-.js → install-BEHp7Mo-.js} +15 -14
- package/dist/{install-DAID6c2-.js.map → install-BEHp7Mo-.js.map} +1 -1
- package/dist/install.js +1 -1
- package/dist/{lab-DLwRl_Ql.js → lab-D61eaX6f.js} +136 -148
- package/dist/lab-D61eaX6f.js.map +1 -0
- package/dist/lab.js +4 -3
- package/dist/{records-BzTcxekG.js → records-D4aQXyZu.js} +647 -389
- package/dist/records-D4aQXyZu.js.map +1 -0
- package/dist/{runtime-D6gVat6r.js → runtime-q-R0CNLh.js} +417 -54
- package/dist/runtime-q-R0CNLh.js.map +1 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +7 -7
- package/dist/{shell-5ju97nOS.js → shell-6CGFvUUw.js} +10 -5
- package/dist/{shell-5ju97nOS.js.map → shell-6CGFvUUw.js.map} +1 -1
- package/dist/shell.d.ts +1 -0
- package/dist/shell.js +6 -6
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.js +2 -2
- package/dist/stores/settings.d.ts +4 -12
- package/dist/styles.css +2753 -2101
- package/dist/types/componentWorkflowTypes.d.ts +2 -0
- package/dist/types/components.d.ts +2 -0
- package/dist/types/generated/platformContext.d.ts +1 -0
- package/dist/types/generated-ui.d.ts +4 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/jobs.d.ts +9 -0
- package/dist/types/platform.d.ts +1 -1
- package/dist/{BaseModal-DfRIuVoj.js → useDropdownState-uFIz6Yv8.js} +75 -5
- package/dist/useDropdownState-uFIz6Yv8.js.map +1 -0
- package/dist/{useExperimentSamples-CxcnxZ8h.js → useExperimentSamples-QEFoq4tf.js} +48 -6
- package/dist/useExperimentSamples-QEFoq4tf.js.map +1 -0
- package/dist/{useListReorder-CcbEYxX-.js → useListReorder-DsuzwWGS.js} +219 -437
- package/dist/useListReorder-DsuzwWGS.js.map +1 -0
- package/dist/{useRackEditor-BD5NIo5s.js → useRackEditor-KpZ4iJ6o.js} +85 -81
- package/dist/useRackEditor-KpZ4iJ6o.js.map +1 -0
- package/dist/useRealtime-B58Lo-Tm.js +508 -0
- package/dist/useRealtime-B58Lo-Tm.js.map +1 -0
- package/dist/useRequest-BuDmW8Jc.js +215 -0
- package/dist/useRequest-BuDmW8Jc.js.map +1 -0
- package/dist/utils/time.d.ts +7 -0
- package/package.json +3 -2
- package/src/__stories__/experiment-helpers.ts +3 -3
- package/src/__tests__/components/ActionMenu.test.ts +2 -2
- package/src/__tests__/components/AppTopBar.test.ts +257 -3
- package/src/__tests__/components/AppTopBarOnboarding.test.ts +160 -0
- package/src/__tests__/components/AppTopBarPageSelector.test.ts +125 -9
- package/src/__tests__/components/AppTopBarPillNav.test.ts +35 -0
- package/src/__tests__/components/ArtifactSaveDialog.test.ts +78 -0
- package/src/__tests__/components/BaseButton.test.ts +19 -0
- package/src/__tests__/components/BaseModal.test.ts +13 -0
- package/src/__tests__/components/BaseSelect.multiple.test.ts +87 -32
- package/src/__tests__/components/BaseSelect.test.ts +2 -1
- package/src/__tests__/components/BatchProgressList.test.ts +16 -0
- package/src/__tests__/components/CalendarGridPanel.test.ts +2 -2
- package/src/__tests__/components/ChartContainer.test.ts +11 -0
- package/src/__tests__/components/ChemicalFormula.test.ts +6 -3
- package/src/__tests__/components/DatePicker.datetime.test.ts +6 -0
- package/src/__tests__/components/DatePicker.test.ts +21 -0
- package/src/__tests__/components/DatePicker.time.test.ts +2 -2
- package/src/__tests__/components/EmptyState.test.ts +1 -1
- package/src/__tests__/components/ExperimentPopover.test.ts +125 -16
- package/src/__tests__/components/ExperimentSelectorModal.test.ts +153 -14
- package/src/__tests__/components/FloatingPanelsInModal.test.ts +94 -0
- package/src/__tests__/components/GeneratedPluginView.test.ts +23 -2
- package/src/__tests__/components/InstrumentStateBadge.test.ts +4 -4
- package/src/__tests__/components/JobsStatusTray.test.ts +373 -71
- package/src/__tests__/components/PluginWorkspaceView.test.ts +63 -1
- package/src/__tests__/components/ResourcePickerListInternal.test.ts +0 -20
- package/src/__tests__/components/SampleSelector.test.ts +33 -8
- package/src/__tests__/components/SequenceProgressBar.test.ts +12 -0
- package/src/__tests__/components/SettingsModal.test.ts +9 -2
- package/src/__tests__/components/StepWizard.test.ts +11 -0
- package/src/__tests__/components/WellPlate.decoration.test.ts +11 -4
- package/src/__tests__/components/deprecationTags.test.ts +1 -1
- package/src/__tests__/composables/useAppExperiment.test.ts +27 -1
- package/src/__tests__/composables/useCalendarGrid.test.ts +4 -2
- package/src/__tests__/composables/useExperimentSelector.test.ts +111 -0
- package/src/__tests__/composables/useFileImport.test.ts +36 -0
- package/src/__tests__/composables/useFloatingPanel.test.ts +68 -0
- package/src/__tests__/composables/useJobCenter.test.ts +262 -3
- package/src/__tests__/composables/useOnboarding.test.ts +317 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +4 -3
- package/src/__tests__/fixtures/table-import/cells.xlsx +0 -0
- package/src/__tests__/fixtures/table-import/samples.xlsx +0 -0
- package/src/__tests__/stores/settings.test.ts +21 -21
- package/src/__tests__/styles/baseControlsRules.test.ts +6 -2
- package/src/__tests__/styles/controlRefreshRules.test.ts +0 -6
- package/src/__tests__/styles/cssTokens.test.ts +6 -3
- package/src/__tests__/styles/menusShellRefresh.test.ts +22 -12
- package/src/__tests__/styles/paletteTokens.test.ts +44 -0
- package/src/__tests__/styles/v2AuditRules.test.ts +104 -0
- package/src/__tests__/utils/plotlyTemplate.test.ts +4 -4
- package/src/__tests__/utils/time.test.ts +18 -1
- package/src/components/ActionMenu.vue +6 -37
- package/src/components/AdductText.story.vue +7 -0
- package/src/components/AdductText.vue +1 -1
- package/src/components/AlertBox.story.vue +2 -2
- package/src/components/AlertBox.vue +18 -16
- package/src/components/AppSidebar.story.vue +0 -28
- package/src/components/AppTopBar.onboarding.story.vue +69 -0
- package/src/components/AppTopBar.story.vue +105 -15
- package/src/components/AppTopBar.vue +248 -122
- package/src/components/ArtifactSaveDialog.story.vue +23 -0
- package/src/components/ArtifactSaveDialog.vue +102 -12
- package/src/components/AuditTrail.story.vue +2 -1
- package/src/components/AuditTrail.vue +2 -21
- package/src/components/Avatar.vue +10 -14
- package/src/components/BaseButton.vue +16 -2
- package/src/components/BaseModal.vue +2 -1
- package/src/components/BaseSelect.multiple.story.vue +13 -2
- package/src/components/BaseSelect.vue +4 -3
- package/src/components/BatchProgressList.vue +35 -25
- package/src/components/ChartContainer.story.vue +2 -1
- package/src/components/ChartContainer.vue +8 -6
- package/src/components/ConfirmDialog.story.vue +1 -1
- package/src/components/EmptyState.vue +1 -1
- package/src/components/ExperimentPopover.story.vue +165 -53
- package/src/components/ExperimentPopover.vue +169 -187
- package/src/components/ExperimentSelectorModal.story.vue +73 -31
- package/src/components/ExperimentSelectorModal.vue +144 -111
- package/src/components/FormField.story.vue +8 -48
- package/src/components/GeneratedPluginView.vue +11 -10
- package/src/components/GeneratedResultRenderer.vue +3 -4
- package/src/components/InstrumentStateBadge.vue +1 -1
- package/src/components/JobsStatusTray.story.vue +97 -43
- package/src/components/JobsStatusTray.vue +287 -290
- package/src/components/LayoutResizeHandle.story.vue +65 -4
- package/src/components/LoadingSpinner.vue +1 -1
- package/src/components/NumberInput.vue +3 -6
- package/src/components/PluginIcon.story.vue +2 -10
- package/src/components/PluginIcon.vue +2 -2
- package/src/components/PluginWorkspaceView.props.ts +3 -0
- package/src/components/PluginWorkspaceView.story.vue +1 -1
- package/src/components/PluginWorkspaceView.vue +17 -0
- package/src/components/RuntimeUpdateNotice.vue +1 -1
- package/src/components/SampleSelector.story.vue +22 -7
- package/src/components/SampleSelector.vue +34 -37
- package/src/components/SampleSelectorGroupNode.vue +1 -1
- package/src/components/SectionCard.story.vue +35 -2
- package/src/components/SequenceProgressBar.story.vue +9 -0
- package/src/components/SequenceProgressBar.vue +6 -1
- package/src/components/SettingsModal.schema.ts +1 -1
- package/src/components/SettingsModal.vue +9 -14
- package/src/components/SmartGroupFieldRecipe.story.vue +1 -1
- package/src/components/SmartGroupManual.story.vue +1 -1
- package/src/components/StepWizard.story.vue +33 -0
- package/src/components/StepWizard.vue +8 -1
- package/src/components/Tooltip.vue +9 -32
- package/src/components/WellPlate.story.vue +3 -1
- package/src/components/WellPlate.vue +7 -0
- package/src/components/internal/AppTopBarOnboardingInternal.vue +172 -0
- package/src/components/internal/AppTopBarPageSelectorInternal.vue +140 -72
- package/src/components/internal/AppTopBarPillNavInternal.vue +33 -2
- package/src/components/internal/DatePickerDateInternal.vue +23 -85
- package/src/components/internal/DatePickerDateTimeInternal.vue +10 -14
- package/src/components/internal/DatePickerTimeInternal.vue +13 -9
- package/src/components/internal/NumberUnitInputInternal.vue +4 -18
- package/src/components/internal/OnboardingLayerInternal.vue +173 -0
- package/src/components/internal/ResourcePickerListInternal.vue +45 -85
- package/src/components/internal/SelectMultipleInternal.vue +187 -65
- package/src/components/internal/SelectSearchableInternal.vue +8 -4
- package/src/components/internal/WellEditPopupInternal.vue +11 -22
- package/src/components/internal/WorkspaceShellInternal.vue +12 -0
- package/src/composables/createClient.ts +2 -0
- package/src/composables/index.ts +1 -0
- package/src/composables/pluginIdentity.ts +31 -0
- package/src/composables/pluginWorkspaceModel.ts +2 -2
- package/src/composables/useAppExperiment.ts +20 -0
- package/src/composables/useCalendarGrid.ts +4 -3
- package/src/composables/useExperimentSelector.ts +77 -31
- package/src/composables/useFileImport.ts +51 -1
- package/src/composables/useFloatingPanel.ts +107 -0
- package/src/composables/useJobCenter.ts +188 -6
- package/src/composables/useOnboarding.ts +465 -0
- package/src/composables/usePluginWorkspace.ts +1 -1
- package/src/data.ts +1 -0
- package/src/runtime.ts +2 -1
- package/src/shell.ts +17 -0
- package/src/stores/index.ts +0 -2
- package/src/stores/settings.ts +20 -50
- package/src/styles/components/action-menu.css +1 -1
- package/src/styles/components/adduct-text.css +13 -5
- package/src/styles/components/alert-box.css +39 -58
- package/src/styles/components/app-layout.css +10 -6
- package/src/styles/components/app-page-selector.css +121 -44
- package/src/styles/components/app-pill-nav.css +11 -12
- package/src/styles/components/app-sidebar.css +9 -18
- package/src/styles/components/app-top-bar.css +219 -55
- package/src/styles/components/artifact-save-dialog.css +153 -2
- package/src/styles/components/audit-trail.css +16 -19
- package/src/styles/components/avatar.css +6 -4
- package/src/styles/components/batch-progress-list.css +38 -36
- package/src/styles/components/button.css +36 -59
- package/src/styles/components/calendar.css +12 -12
- package/src/styles/components/chart-container.css +34 -23
- package/src/styles/components/checkbox.css +2 -12
- package/src/styles/components/collapsible-card.css +27 -29
- package/src/styles/components/confirm-dialog.css +13 -14
- package/src/styles/components/dataframe.css +13 -5
- package/src/styles/components/date-picker.css +84 -94
- package/src/styles/components/datetime-picker.css +35 -47
- package/src/styles/components/divider.css +20 -20
- package/src/styles/components/dose-calculator.css +3 -3
- package/src/styles/components/empty-state.css +20 -17
- package/src/styles/components/experiment-code-badge.css +2 -0
- package/src/styles/components/experiment-popover.css +174 -329
- package/src/styles/components/experiment-selector-modal.css +88 -56
- package/src/styles/components/file-picker.css +3 -3
- package/src/styles/components/file-uploader.css +38 -49
- package/src/styles/components/form-builder.css +3 -3
- package/src/styles/components/formula-input.css +1 -1
- package/src/styles/components/icon-button.css +18 -24
- package/src/styles/components/input.css +5 -4
- package/src/styles/components/instrument-monitor.css +13 -15
- package/src/styles/components/jobs-status-tray.css +140 -296
- package/src/styles/components/layout-resize-handle.css +59 -16
- package/src/styles/components/modal.css +5 -7
- package/src/styles/components/molecule-input.css +2 -2
- package/src/styles/components/multi-select.css +65 -117
- package/src/styles/components/number-input.css +26 -19
- package/src/styles/components/onboarding.css +368 -0
- package/src/styles/components/pill.css +3 -2
- package/src/styles/components/plate-editor.css +0 -1
- package/src/styles/components/plugin-icon.css +26 -23
- package/src/styles/components/popover.css +2 -2
- package/src/styles/components/presence-avatars.css +1 -1
- package/src/styles/components/progress-bar.css +3 -3
- package/src/styles/components/protocol-step-editor.css +1 -3
- package/src/styles/components/resource-card.css +2 -2
- package/src/styles/components/resource-picker-list.css +6 -57
- package/src/styles/components/sample-hierarchy-tree.css +4 -4
- package/src/styles/components/sample-selector.css +138 -79
- package/src/styles/components/schedule-calendar.css +8 -8
- package/src/styles/components/searchable-select.css +15 -19
- package/src/styles/components/section-card.css +54 -49
- package/src/styles/components/segmented-control.css +18 -15
- package/src/styles/components/select.css +3 -3
- package/src/styles/components/sequence-input.css +2 -2
- package/src/styles/components/sequence-progress-bar.css +17 -8
- package/src/styles/components/settings-modal.css +7 -2
- package/src/styles/components/smart-group.css +18 -18
- package/src/styles/components/step-wizard.css +52 -16
- package/src/styles/components/tabs.css +2 -2
- package/src/styles/components/textarea.css +3 -3
- package/src/styles/components/time-picker.css +37 -25
- package/src/styles/components/toast.css +13 -11
- package/src/styles/components/toggle.css +1 -11
- package/src/styles/components/tooltip.css +12 -11
- package/src/styles/components/unit-input.css +9 -8
- package/src/styles/components/well-edit-popup.css +2 -2
- package/src/styles/components/well-plate.css +9 -2
- package/src/styles/index.css +1 -0
- package/src/styles/variables.css +247 -78
- package/src/types/componentWorkflowTypes.ts +2 -0
- package/src/types/components.ts +2 -0
- package/src/types/generated/platformContext.ts +1 -0
- package/src/types/generated-ui.ts +4 -0
- package/src/types/index.ts +1 -0
- package/src/types/jobs.ts +10 -0
- package/src/types/platform.ts +1 -1
- package/src/utils/time.ts +21 -0
- package/dist/ActionMenu-CScINahU.js.map +0 -1
- package/dist/AppSidebar-BmiiPfz9.js.map +0 -1
- package/dist/BaseModal-DfRIuVoj.js.map +0 -1
- package/dist/ExperimentPopover-C4sGw-is.js +0 -359
- package/dist/ExperimentPopover-C4sGw-is.js.map +0 -1
- package/dist/ExperimentSelectorModal-D-9z7cNx.js.map +0 -1
- package/dist/SettingsModal-CSLE31yg.js.map +0 -1
- package/dist/data-C2PwI_dn.js.map +0 -1
- package/dist/experiment-43OJtH7R.js.map +0 -1
- package/dist/lab-DLwRl_Ql.js.map +0 -1
- package/dist/records-BzTcxekG.js.map +0 -1
- package/dist/runtime-D6gVat6r.js.map +0 -1
- package/dist/useExperimentSamples-CxcnxZ8h.js.map +0 -1
- package/dist/useListReorder-CcbEYxX-.js.map +0 -1
- package/dist/useRackEditor-BD5NIo5s.js.map +0 -1
- package/dist/useRealtime-PalKue84.js +0 -696
- package/dist/useRealtime-PalKue84.js.map +0 -1
- package/dist/useRequest-D5JDfsgZ.js +0 -76
- package/dist/useRequest-D5JDfsgZ.js.map +0 -1
|
@@ -1,35 +1,52 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Compact, non-modal job panel under the top bar, driven by a job center the parent owns.
|
|
4
|
+
*
|
|
5
|
+
* The parent passes the center's `topBarJob`, `completedCount` and `handleTopBarClick`
|
|
6
|
+
* to `AppTopBar`; its run slot opens this panel. `PluginWorkspaceView` does this for you
|
|
7
|
+
* when its spec sets `jobs`.
|
|
4
8
|
*
|
|
5
9
|
* @example Recommended
|
|
6
10
|
* ```vue
|
|
7
|
-
* <
|
|
11
|
+
* <AppTopBar :job="center.topBarJob.value" :jobs-done="center.completedCount.value"
|
|
12
|
+
* @job-click="center.handleTopBarClick(openResult)" />
|
|
13
|
+
* <JobsStatusTray :center="center" @select="openResult" />
|
|
8
14
|
* ```
|
|
9
15
|
*/
|
|
10
|
-
import { computed, nextTick, ref,
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
jobStatusLabel,
|
|
14
|
-
normalizeJobPercent,
|
|
15
|
-
} from '../jobs'
|
|
16
|
+
import { computed, nextTick, onBeforeUnmount, ref, watch, type Component } from 'vue'
|
|
16
17
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
CircleAlert,
|
|
19
|
+
CircleCheck,
|
|
20
|
+
CircleSlash,
|
|
21
|
+
CircleX,
|
|
22
|
+
Clock,
|
|
23
|
+
LoaderCircle,
|
|
24
|
+
RefreshCw,
|
|
25
|
+
Square,
|
|
26
|
+
Trash2,
|
|
27
|
+
X,
|
|
28
|
+
} from 'lucide-vue-next'
|
|
29
|
+
|
|
30
|
+
import { isActiveJobStatus, jobStatusLabel } from '../jobs'
|
|
31
|
+
import type { UseJobCenterReturn } from '../composables/useJobCenter'
|
|
32
|
+
import type { JobState, JobStatus } from '../types/jobs'
|
|
33
|
+
import ConfirmDialog from './ConfirmDialog.vue'
|
|
34
|
+
|
|
35
|
+
type ConfirmAction = 'cancel' | 'delete' | 'clear'
|
|
21
36
|
|
|
22
37
|
interface Props {
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
/** Job center from `useJobCenter()`; the parent owns it and opens the panel through it. */
|
|
39
|
+
center: UseJobCenterReturn
|
|
25
40
|
title?: string
|
|
26
41
|
teleportTo?: string | HTMLElement | false
|
|
42
|
+
/** Ask before Cancel, Delete and Clear (`true`), or before the named ones only. Default off. */
|
|
43
|
+
confirm?: boolean | Partial<Record<ConfirmAction, boolean>>
|
|
27
44
|
}
|
|
28
45
|
|
|
29
46
|
const props = withDefaults(defineProps<Props>(), {
|
|
30
|
-
|
|
31
|
-
title: 'Analysis jobs',
|
|
47
|
+
title: 'Jobs',
|
|
32
48
|
teleportTo: 'body',
|
|
49
|
+
confirm: false,
|
|
33
50
|
})
|
|
34
51
|
|
|
35
52
|
const emit = defineEmits<{
|
|
@@ -40,107 +57,187 @@ const emit = defineEmits<{
|
|
|
40
57
|
defineSlots<{
|
|
41
58
|
/** Extra per-row actions, rendered before the built-in Cancel / Delete. */
|
|
42
59
|
'job-actions'?: (props: { job: JobState }) => unknown
|
|
60
|
+
/** Row title. Default: the job name. */
|
|
61
|
+
'job-title'?: (props: { job: JobState }) => unknown
|
|
62
|
+
/** Detail line under the title. Default: the error, progress message or status. */
|
|
63
|
+
'job-detail'?: (props: { job: JobState }) => unknown
|
|
43
64
|
}>()
|
|
44
65
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
66
|
+
const STATUS_ICONS: Record<JobStatus, Component> = {
|
|
67
|
+
running: LoaderCircle,
|
|
68
|
+
awaiting_input: CircleAlert,
|
|
69
|
+
queued: Clock,
|
|
70
|
+
completed: CircleCheck,
|
|
71
|
+
failed: CircleX,
|
|
72
|
+
cancelled: CircleSlash,
|
|
73
|
+
}
|
|
74
|
+
|
|
48
75
|
const panelRef = ref<HTMLElement | null>(null)
|
|
49
76
|
const teleportTarget = computed(() => props.teleportTo || 'body')
|
|
50
|
-
const tray =
|
|
51
|
-
|
|
52
|
-
|
|
77
|
+
const tray = props.center
|
|
78
|
+
const titleId = `${tray.panelId}-title`
|
|
79
|
+
const rows = (jobs: JobState[]) => jobs.map(job => ({ job, view: tray.rowView(job) }))
|
|
80
|
+
// A job waiting for input is the only one that needs a person: pin it first.
|
|
81
|
+
const activeRows = computed(() => {
|
|
82
|
+
const active = rows(tray.activeJobs.value)
|
|
83
|
+
return [
|
|
84
|
+
...active.filter(row => row.view.status === 'awaiting_input'),
|
|
85
|
+
...active.filter(row => row.view.status !== 'awaiting_input'),
|
|
86
|
+
]
|
|
53
87
|
})
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const subtitle = computed(() => {
|
|
60
|
-
const count = (status: JobState['status']) => tray.activeJobs.value.filter(job => job.status === status).length
|
|
88
|
+
const sections = computed(() => [
|
|
89
|
+
{ key: 'active', title: 'Active', rows: activeRows.value },
|
|
90
|
+
{ key: 'finished', title: 'Finished', rows: rows(tray.historyJobs.value) },
|
|
91
|
+
].filter(section => section.rows.length > 0))
|
|
92
|
+
const headCount = computed(() => {
|
|
61
93
|
const parts = [
|
|
62
|
-
[
|
|
63
|
-
[
|
|
64
|
-
[
|
|
94
|
+
[tray.activeJobs.value.length, 'active'],
|
|
95
|
+
[tray.completedCount.value, 'done'],
|
|
96
|
+
[tray.historyJobs.value.filter(job => job.status === 'failed').length, 'failed'],
|
|
65
97
|
].filter(([n]) => n).map(([n, word]) => `${n} ${word}`)
|
|
66
|
-
return parts.length ? parts.join(' · ') :
|
|
98
|
+
return parts.length ? parts.join(' · ') : 'No jobs'
|
|
67
99
|
})
|
|
68
100
|
const canClear = computed(() => (
|
|
69
101
|
tray.deletableJobs.value.length > 0
|
|
70
102
|
&& tray.canClearFinished.value
|
|
71
103
|
))
|
|
72
104
|
|
|
73
|
-
|
|
74
|
-
|
|
105
|
+
// Escape returns focus to the element that opened the panel (the top-bar chip).
|
|
106
|
+
// The panel is non-modal, so Escape is heard on the document while it is open.
|
|
107
|
+
// An Escape that an inner control handled, or that belongs to a modal dialog
|
|
108
|
+
// above the tray (its own confirm dialog too), leaves the tray open.
|
|
109
|
+
let opener: HTMLElement | null = null
|
|
110
|
+
function onDocumentKeydown(event: KeyboardEvent): void {
|
|
111
|
+
if (event.key !== 'Escape' || event.defaultPrevented || confirmOpen.value) return
|
|
112
|
+
if (event.target instanceof Element && event.target.closest('[aria-modal="true"]')) return
|
|
113
|
+
closeTray()
|
|
114
|
+
}
|
|
115
|
+
watch(tray.isOpen, async (open) => {
|
|
116
|
+
if (!open) {
|
|
117
|
+
document.removeEventListener('keydown', onDocumentKeydown)
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
opener = document.activeElement instanceof HTMLElement ? document.activeElement : null
|
|
121
|
+
document.addEventListener('keydown', onDocumentKeydown)
|
|
75
122
|
await nextTick()
|
|
76
123
|
panelRef.value?.focus()
|
|
77
|
-
}
|
|
124
|
+
}, { flush: 'sync' })
|
|
125
|
+
onBeforeUnmount(() => document.removeEventListener('keydown', onDocumentKeydown))
|
|
78
126
|
|
|
79
|
-
|
|
127
|
+
// A focused row that leaves the list (an action or a refresh) drops focus to body:
|
|
128
|
+
// note the focused element before the update and keep focus in the panel after it.
|
|
129
|
+
let focusedInPanel: Element | null = null
|
|
130
|
+
watch(tray.jobs, () => {
|
|
131
|
+
const focused = document.activeElement
|
|
132
|
+
focusedInPanel = focused && panelRef.value?.contains(focused) ? focused : null
|
|
133
|
+
}, { flush: 'pre' })
|
|
134
|
+
watch(tray.jobs, () => {
|
|
135
|
+
if (tray.isOpen.value && focusedInPanel && !focusedInPanel.isConnected) panelRef.value?.focus()
|
|
136
|
+
focusedInPanel = null
|
|
137
|
+
}, { flush: 'post' })
|
|
138
|
+
|
|
139
|
+
function closeTray(): void {
|
|
80
140
|
tray.close()
|
|
81
|
-
|
|
82
|
-
|
|
141
|
+
// Safari leaves a clicked button unfocused (opener = body), and the chip node is
|
|
142
|
+
// replaced when it switches between job chip and idle button: fall back to the
|
|
143
|
+
// top-bar jobs control.
|
|
144
|
+
const target = opener?.isConnected && opener !== document.body
|
|
145
|
+
? opener
|
|
146
|
+
: document.querySelector<HTMLElement>('.mint-topbar__job')
|
|
147
|
+
target?.focus()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function formatTime(value: string | null): string {
|
|
151
|
+
if (!value) return ''
|
|
152
|
+
const date = new Date(value)
|
|
153
|
+
return Number.isNaN(date.getTime())
|
|
154
|
+
? ''
|
|
155
|
+
: date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
|
83
156
|
}
|
|
84
157
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
158
|
+
/** Right-hand value: the percent while a job moves, else when it ended or started. */
|
|
159
|
+
function jobValue(job: JobState, percent: number | null): string {
|
|
160
|
+
if (percent != null) return `${percent}%`
|
|
161
|
+
return formatTime(job.completed_at || job.started_at || job.created_at)
|
|
88
162
|
}
|
|
89
163
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
164
|
+
// Confirmation: the action waits for the dialog; it runs on the live job.
|
|
165
|
+
// `ids`: the finished jobs that the user confirmed to clear.
|
|
166
|
+
const pending = ref<{ action: ConfirmAction; job?: JobState; ids?: string[] } | null>(null)
|
|
167
|
+
const confirmOpen = ref(false)
|
|
168
|
+
// Set on request and kept, so the dialog text stays while it fades out.
|
|
169
|
+
const confirmText = ref<{ title: string; message: string; confirmLabel: string; cancelLabel: string } | null>(null)
|
|
170
|
+
const confirmEnabled = computed(() => props.confirm === true
|
|
171
|
+
|| (typeof props.confirm === 'object' && Object.values(props.confirm).some(Boolean)))
|
|
172
|
+
|
|
173
|
+
function textFor(action: ConfirmAction, job?: JobState) {
|
|
174
|
+
if (action === 'clear') {
|
|
175
|
+
const count = tray.deletableJobs.value.length
|
|
176
|
+
return {
|
|
177
|
+
title: 'Clear finished jobs?',
|
|
178
|
+
message: `${count} finished ${count === 1 ? 'job' : 'jobs'} and their stored results are deleted.`,
|
|
179
|
+
confirmLabel: 'Clear jobs',
|
|
180
|
+
cancelLabel: 'Cancel',
|
|
181
|
+
}
|
|
94
182
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
183
|
+
return action === 'cancel'
|
|
184
|
+
? { title: 'Cancel job?', message: `${job?.name} stops. Its partial results are lost.`, confirmLabel: 'Cancel job', cancelLabel: 'Keep running' }
|
|
185
|
+
: { title: 'Delete job?', message: `${job?.name} and its stored result are deleted.`, confirmLabel: 'Delete job', cancelLabel: 'Cancel' }
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function needsConfirm(action: ConfirmAction): boolean {
|
|
189
|
+
return props.confirm === true || (typeof props.confirm === 'object' && props.confirm[action] === true)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function request(action: ConfirmAction, job?: JobState): void {
|
|
193
|
+
if (!needsConfirm(action)) {
|
|
194
|
+
void run(action, job)
|
|
102
195
|
return
|
|
103
196
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (!activeElement || !focusable.includes(activeElement)) {
|
|
108
|
-
event.preventDefault()
|
|
109
|
-
if (event.shiftKey) last?.focus()
|
|
110
|
-
else first?.focus()
|
|
111
|
-
} else if (event.shiftKey && activeElement === first) {
|
|
112
|
-
event.preventDefault()
|
|
113
|
-
last?.focus()
|
|
114
|
-
} else if (!event.shiftKey && activeElement === last) {
|
|
115
|
-
event.preventDefault()
|
|
116
|
-
first?.focus()
|
|
117
|
-
}
|
|
197
|
+
pending.value = { action, job, ids: action === 'clear' ? deletableIds() : undefined }
|
|
198
|
+
confirmText.value = textFor(action, job)
|
|
199
|
+
confirmOpen.value = true
|
|
118
200
|
}
|
|
119
201
|
|
|
120
|
-
function
|
|
121
|
-
|
|
122
|
-
return job.progress.message || 'Waiting for external input'
|
|
123
|
-
}
|
|
124
|
-
if (job.status === 'queued' && job.queue_position > 0) {
|
|
125
|
-
return `Queue position ${job.queue_position}`
|
|
126
|
-
}
|
|
127
|
-
return job.progress.message || job.progress.stage || jobStatusLabel(job.status)
|
|
202
|
+
function deletableIds(): string[] {
|
|
203
|
+
return tray.deletableJobs.value.map(job => job.job_id).sort()
|
|
128
204
|
}
|
|
129
205
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
206
|
+
// A new source is a new job list: the confirmed target is gone.
|
|
207
|
+
watch(tray.source, () => onConfirmOpen(false))
|
|
208
|
+
|
|
209
|
+
function onConfirmOpen(open: boolean): void {
|
|
210
|
+
confirmOpen.value = open
|
|
211
|
+
if (!open) pending.value = null
|
|
136
212
|
}
|
|
137
213
|
|
|
138
|
-
function
|
|
139
|
-
|
|
214
|
+
async function confirmPending(): Promise<void> {
|
|
215
|
+
// A second Confirm click while the dialog fades out does nothing.
|
|
216
|
+
const current = pending.value
|
|
217
|
+
if (!confirmOpen.value || !current) return
|
|
218
|
+
if (current.action === 'clear') {
|
|
219
|
+
// Polling changed the finished jobs: ask again with the new count.
|
|
220
|
+
const ids = deletableIds()
|
|
221
|
+
if (ids.join() !== current.ids?.join()) {
|
|
222
|
+
pending.value = { ...current, ids }
|
|
223
|
+
confirmText.value = textFor('clear')
|
|
224
|
+
return
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
confirmOpen.value = false
|
|
228
|
+
pending.value = null
|
|
229
|
+
if (current.action === 'clear') return clearFinished()
|
|
230
|
+
// Polling can end or remove the job while the dialog is open: then do nothing.
|
|
231
|
+
const live = tray.jobs.value.find(job => job.job_id === current.job?.job_id)
|
|
232
|
+
const allowed = live && (current.action === 'cancel'
|
|
233
|
+
? isActiveJobStatus(live.status) && live.can_cancel
|
|
234
|
+
: live.can_delete)
|
|
235
|
+
if (allowed) await run(current.action, live)
|
|
140
236
|
}
|
|
141
237
|
|
|
142
|
-
function
|
|
143
|
-
return
|
|
238
|
+
function run(action: ConfirmAction, job?: JobState): Promise<void> {
|
|
239
|
+
if (action === 'clear') return clearFinished()
|
|
240
|
+
return action === 'cancel' ? cancelJob(job!) : deleteJob(job!)
|
|
144
241
|
}
|
|
145
242
|
|
|
146
243
|
async function cancelJob(job: JobState): Promise<void> {
|
|
@@ -162,249 +259,149 @@ async function clearFinished(): Promise<void> {
|
|
|
162
259
|
<template>
|
|
163
260
|
<Teleport :to="teleportTarget" :disabled="teleportTo === false">
|
|
164
261
|
<div class="mint-jobs-tray">
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
class="mint-jobs-
|
|
169
|
-
aria-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
@click="toggleTray"
|
|
173
|
-
>
|
|
174
|
-
<svg
|
|
175
|
-
class="mint-jobs-tray__trigger-icon"
|
|
176
|
-
:class="{ 'mint-jobs-tray__trigger-icon--active': tray.activeJobs.value.length > 0 }"
|
|
177
|
-
viewBox="0 0 24 24"
|
|
178
|
-
fill="none"
|
|
179
|
-
stroke="currentColor"
|
|
180
|
-
aria-hidden="true"
|
|
181
|
-
>
|
|
182
|
-
<path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2" />
|
|
183
|
-
<path d="M9 5a3 3 0 0 1 6 0v2H9V5Z" />
|
|
184
|
-
</svg>
|
|
185
|
-
<span class="mint-jobs-tray__trigger-label">
|
|
186
|
-
{{ tray.featuredJob.value ? jobStatusLabel(tray.featuredJob.value.status) : 'Jobs' }}
|
|
187
|
-
</span>
|
|
188
|
-
<span
|
|
189
|
-
v-if="tray.activeJobs.value.length > 0"
|
|
190
|
-
class="mint-jobs-tray__count"
|
|
191
|
-
aria-live="polite"
|
|
192
|
-
>
|
|
193
|
-
{{ tray.activeJobs.value.length }}
|
|
194
|
-
</span>
|
|
195
|
-
</button>
|
|
262
|
+
<!-- Clicks outside close the panel; the page is not dimmed. -->
|
|
263
|
+
<div
|
|
264
|
+
v-if="tray.isOpen.value"
|
|
265
|
+
class="mint-jobs-tray__scrim"
|
|
266
|
+
aria-hidden="true"
|
|
267
|
+
@click="tray.close()"
|
|
268
|
+
/>
|
|
196
269
|
|
|
197
270
|
<Transition name="mint-jobs-tray-fade">
|
|
198
|
-
<div
|
|
199
|
-
v-if="tray.isOpen.value"
|
|
200
|
-
class="mint-jobs-tray__backdrop"
|
|
201
|
-
aria-hidden="true"
|
|
202
|
-
@click="closeTray"
|
|
203
|
-
/>
|
|
204
|
-
</Transition>
|
|
205
|
-
|
|
206
|
-
<Transition name="mint-jobs-tray-slide">
|
|
207
271
|
<section
|
|
208
272
|
v-if="tray.isOpen.value"
|
|
209
|
-
:id="panelId"
|
|
273
|
+
:id="tray.panelId"
|
|
210
274
|
ref="panelRef"
|
|
211
275
|
class="mint-jobs-tray__panel"
|
|
212
|
-
role="dialog"
|
|
213
|
-
aria-modal="true"
|
|
214
276
|
:aria-labelledby="titleId"
|
|
215
277
|
tabindex="-1"
|
|
216
|
-
@keydown="onPanelKeydown"
|
|
217
278
|
>
|
|
218
|
-
<header class="mint-jobs-
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
<p class="mint-jobs-tray__subtitle">{{ subtitle }}</p>
|
|
222
|
-
</div>
|
|
223
|
-
<div class="mint-jobs-tray__header-actions">
|
|
224
|
-
<button
|
|
225
|
-
v-if="tray.canRefresh.value"
|
|
226
|
-
type="button"
|
|
227
|
-
class="mint-jobs-tray__icon-button"
|
|
228
|
-
aria-label="Refresh jobs"
|
|
229
|
-
:disabled="tray.isLoading.value"
|
|
230
|
-
@click="tray.refresh"
|
|
231
|
-
>
|
|
232
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" aria-hidden="true">
|
|
233
|
-
<path d="M20 6v5h-5M4 18v-5h5" />
|
|
234
|
-
<path d="M18.4 9A7 7 0 0 0 6.1 6.1L4 8m2 7a7 7 0 0 0 11.9 2.9L20 16" />
|
|
235
|
-
</svg>
|
|
236
|
-
</button>
|
|
237
|
-
<button
|
|
238
|
-
type="button"
|
|
239
|
-
class="mint-jobs-tray__icon-button"
|
|
240
|
-
aria-label="Close jobs"
|
|
241
|
-
@click="closeTray"
|
|
242
|
-
>
|
|
243
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" aria-hidden="true">
|
|
244
|
-
<path d="m6 6 12 12M18 6 6 18" />
|
|
245
|
-
</svg>
|
|
246
|
-
</button>
|
|
247
|
-
</div>
|
|
248
|
-
</header>
|
|
249
|
-
|
|
250
|
-
<div class="mint-jobs-tray__tabs" role="tablist" aria-label="Job groups">
|
|
279
|
+
<header class="mint-jobs-tray__head">
|
|
280
|
+
<h2 :id="titleId" class="mint-jobs-tray__title">{{ title }}</h2>
|
|
281
|
+
<span class="mint-jobs-tray__head-count">{{ headCount }}</span>
|
|
251
282
|
<button
|
|
283
|
+
v-if="tray.canRefresh.value"
|
|
252
284
|
type="button"
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
:
|
|
257
|
-
|
|
258
|
-
@click="tray.activeTab.value = 'active'"
|
|
285
|
+
class="mint-jobs-tray__icon-button"
|
|
286
|
+
aria-label="Refresh jobs"
|
|
287
|
+
title="Refresh jobs"
|
|
288
|
+
:disabled="tray.isLoading.value"
|
|
289
|
+
@click="tray.refresh"
|
|
259
290
|
>
|
|
260
|
-
|
|
261
|
-
<span class="mint-jobs-tray__tab-count">{{ tray.activeJobs.value.length }}</span>
|
|
291
|
+
<RefreshCw :size="14" aria-hidden="true" />
|
|
262
292
|
</button>
|
|
263
293
|
<button
|
|
264
294
|
type="button"
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
:aria-selected="tray.activeTab.value === 'history'"
|
|
270
|
-
@click="tray.activeTab.value = 'history'"
|
|
295
|
+
class="mint-jobs-tray__icon-button"
|
|
296
|
+
aria-label="Close jobs"
|
|
297
|
+
title="Close"
|
|
298
|
+
@click="closeTray"
|
|
271
299
|
>
|
|
272
|
-
|
|
273
|
-
<span class="mint-jobs-tray__tab-count">{{ tray.historyJobs.value.length }}</span>
|
|
300
|
+
<X :size="14" aria-hidden="true" />
|
|
274
301
|
</button>
|
|
275
|
-
</
|
|
302
|
+
</header>
|
|
276
303
|
|
|
277
304
|
<p v-if="tray.error.value" class="mint-jobs-tray__error" role="alert">
|
|
278
305
|
{{ tray.error.value }}
|
|
279
306
|
</p>
|
|
280
307
|
|
|
281
308
|
<div class="mint-jobs-tray__body">
|
|
282
|
-
<
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
309
|
+
<p v-if="sections.length === 0" class="mint-jobs-tray__empty">No jobs yet</p>
|
|
310
|
+
|
|
311
|
+
<template v-for="section in sections" :key="section.key">
|
|
312
|
+
<div class="mint-jobs-tray__section" :data-section="section.key">
|
|
313
|
+
{{ section.title }}
|
|
314
|
+
<span class="mint-jobs-tray__section-count">· {{ section.rows.length }}</span>
|
|
315
|
+
<button
|
|
316
|
+
v-if="section.key === 'finished' && canClear"
|
|
317
|
+
type="button"
|
|
318
|
+
class="mint-jobs-tray__section-action"
|
|
319
|
+
@click="request('clear')"
|
|
320
|
+
>
|
|
321
|
+
Clear
|
|
322
|
+
</button>
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
<div
|
|
326
|
+
v-for="{ job, view } in section.rows"
|
|
288
327
|
:key="job.job_id"
|
|
289
|
-
class="mint-jobs-
|
|
290
|
-
:class="{ 'mint-jobs-
|
|
328
|
+
class="mint-jobs-tray__row"
|
|
329
|
+
:class="{ 'mint-jobs-tray__row--awaiting-input': view.status === 'awaiting_input' }"
|
|
330
|
+
:data-status="job.status"
|
|
291
331
|
>
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
332
|
+
<!-- data-status is the real job status; the row view only changes how it looks. -->
|
|
333
|
+
<component
|
|
334
|
+
:is="STATUS_ICONS[view.status]"
|
|
335
|
+
:size="15"
|
|
336
|
+
:class="['mint-jobs-tray__status', `mint-jobs-tray__status--${view.status.replace('_', '-')}`]"
|
|
337
|
+
role="img"
|
|
338
|
+
:aria-label="jobStatusLabel(view.status)"
|
|
339
|
+
/>
|
|
340
|
+
<button type="button" class="mint-jobs-tray__row-title" @click="emit('select', job)">
|
|
341
|
+
<slot name="job-title" :job="job">{{ job.name }}</slot>
|
|
342
|
+
</button>
|
|
343
|
+
<span class="mint-jobs-tray__tools">
|
|
344
|
+
<span class="mint-jobs-tray__value">{{ jobValue(job, view.percent) }}</span>
|
|
345
|
+
<slot name="job-actions" :job="job" />
|
|
346
|
+
<button
|
|
347
|
+
v-if="isActiveJobStatus(job.status) && job.can_cancel && tray.canCancel.value"
|
|
348
|
+
type="button"
|
|
349
|
+
class="mint-jobs-tray__icon-button mint-jobs-tray__icon-button--danger"
|
|
350
|
+
:aria-label="`Cancel ${job.name}`"
|
|
351
|
+
title="Cancel"
|
|
352
|
+
:disabled="tray.isPending(job)"
|
|
353
|
+
@click="request('cancel', job)"
|
|
354
|
+
>
|
|
355
|
+
<Square :size="12" aria-hidden="true" />
|
|
356
|
+
</button>
|
|
357
|
+
<button
|
|
358
|
+
v-if="job.can_delete && tray.canDelete.value"
|
|
359
|
+
type="button"
|
|
360
|
+
class="mint-jobs-tray__icon-button"
|
|
361
|
+
:aria-label="`Delete ${job.name}`"
|
|
362
|
+
title="Delete"
|
|
363
|
+
:disabled="tray.isPending(job)"
|
|
364
|
+
@click="request('delete', job)"
|
|
365
|
+
>
|
|
366
|
+
<Trash2 :size="14" aria-hidden="true" />
|
|
367
|
+
</button>
|
|
368
|
+
</span>
|
|
309
369
|
<div
|
|
310
|
-
v-if="
|
|
370
|
+
v-if="view.percent != null"
|
|
311
371
|
class="mint-jobs-tray__progress"
|
|
312
372
|
role="progressbar"
|
|
313
|
-
aria-label="
|
|
373
|
+
:aria-label="`${job.name} progress`"
|
|
314
374
|
aria-valuemin="0"
|
|
315
375
|
aria-valuemax="100"
|
|
316
|
-
:aria-valuenow="
|
|
376
|
+
:aria-valuenow="view.percent"
|
|
317
377
|
>
|
|
318
|
-
<span :style="{ width: `${
|
|
378
|
+
<span :style="{ width: `${view.percent}%` }" />
|
|
319
379
|
</div>
|
|
320
|
-
<
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
<slot name="job-actions" :job="job" />
|
|
324
|
-
<button
|
|
325
|
-
v-if="job.can_cancel && tray.canCancel.value"
|
|
326
|
-
type="button"
|
|
327
|
-
class="mint-jobs-tray__action mint-jobs-tray__action--danger"
|
|
328
|
-
aria-label="Cancel job"
|
|
329
|
-
:disabled="tray.isPending(job)"
|
|
330
|
-
@click="cancelJob(job)"
|
|
331
|
-
>
|
|
332
|
-
Cancel
|
|
333
|
-
</button>
|
|
334
|
-
<button
|
|
335
|
-
v-if="job.can_delete && tray.canDelete.value"
|
|
336
|
-
type="button"
|
|
337
|
-
class="mint-jobs-tray__icon-button mint-jobs-tray__icon-button--danger"
|
|
338
|
-
aria-label="Delete job"
|
|
339
|
-
:disabled="tray.isPending(job)"
|
|
340
|
-
@click="deleteJob(job)"
|
|
341
|
-
>
|
|
342
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" aria-hidden="true">
|
|
343
|
-
<path d="M4 7h16M9 7V4h6v3m-8 0 1 13h8l1-13M10 11v5M14 11v5" />
|
|
344
|
-
</svg>
|
|
345
|
-
</button>
|
|
346
|
-
</div>
|
|
347
|
-
</div>
|
|
348
|
-
</article>
|
|
349
|
-
</template>
|
|
350
|
-
|
|
351
|
-
<template v-else>
|
|
352
|
-
<div v-if="canClear" class="mint-jobs-tray__clear-row">
|
|
353
|
-
<button
|
|
354
|
-
type="button"
|
|
355
|
-
class="mint-jobs-tray__action mint-jobs-tray__action--danger"
|
|
356
|
-
@click="clearFinished"
|
|
380
|
+
<span
|
|
381
|
+
class="mint-jobs-tray__detail"
|
|
382
|
+
:class="{ 'mint-jobs-tray__detail--error': job.status === 'failed' }"
|
|
357
383
|
>
|
|
358
|
-
|
|
359
|
-
</
|
|
384
|
+
<slot name="job-detail" :job="job">{{ view.message }}</slot>
|
|
385
|
+
</span>
|
|
360
386
|
</div>
|
|
361
|
-
<p v-if="tray.historyJobs.value.length === 0" class="mint-jobs-tray__empty">
|
|
362
|
-
No job history
|
|
363
|
-
</p>
|
|
364
|
-
<article
|
|
365
|
-
v-for="job in tray.historyJobs.value"
|
|
366
|
-
:key="job.job_id"
|
|
367
|
-
class="mint-jobs-tray__job"
|
|
368
|
-
>
|
|
369
|
-
<div class="mint-jobs-tray__job-heading">
|
|
370
|
-
<div class="mint-jobs-tray__job-identity">
|
|
371
|
-
<button
|
|
372
|
-
type="button"
|
|
373
|
-
class="mint-jobs-tray__job-select"
|
|
374
|
-
@click="emit('select', job)"
|
|
375
|
-
>
|
|
376
|
-
{{ job.name }}
|
|
377
|
-
</button>
|
|
378
|
-
<span class="mint-jobs-tray__kind">{{ job.kind }}</span>
|
|
379
|
-
</div>
|
|
380
|
-
<span class="mint-jobs-tray__status" :class="statusClass(job)">
|
|
381
|
-
<span class="mint-jobs-tray__status-dot" aria-hidden="true" />
|
|
382
|
-
{{ jobStatusLabel(job.status) }}
|
|
383
|
-
</span>
|
|
384
|
-
</div>
|
|
385
|
-
<div class="mint-jobs-tray__job-footer">
|
|
386
|
-
<span>{{ formatTimestamp(job.completed_at || job.created_at) }}</span>
|
|
387
|
-
<div class="mint-jobs-tray__job-actions">
|
|
388
|
-
<slot name="job-actions" :job="job" />
|
|
389
|
-
<button
|
|
390
|
-
v-if="job.can_delete && tray.canDelete.value"
|
|
391
|
-
type="button"
|
|
392
|
-
class="mint-jobs-tray__icon-button mint-jobs-tray__icon-button--danger"
|
|
393
|
-
aria-label="Delete job"
|
|
394
|
-
:disabled="tray.isPending(job)"
|
|
395
|
-
@click="deleteJob(job)"
|
|
396
|
-
>
|
|
397
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" aria-hidden="true">
|
|
398
|
-
<path d="M4 7h16M9 7V4h6v3m-8 0 1 13h8l1-13M10 11v5M14 11v5" />
|
|
399
|
-
</svg>
|
|
400
|
-
</button>
|
|
401
|
-
</div>
|
|
402
|
-
</div>
|
|
403
|
-
</article>
|
|
404
387
|
</template>
|
|
405
388
|
</div>
|
|
406
389
|
</section>
|
|
407
390
|
</Transition>
|
|
391
|
+
|
|
392
|
+
<ConfirmDialog
|
|
393
|
+
v-if="confirmEnabled"
|
|
394
|
+
:open="confirmOpen"
|
|
395
|
+
tone="error"
|
|
396
|
+
:title="confirmText?.title"
|
|
397
|
+
:confirm-label="confirmText?.confirmLabel"
|
|
398
|
+
:cancel-label="confirmText?.cancelLabel"
|
|
399
|
+
@update:open="onConfirmOpen"
|
|
400
|
+
@confirm="confirmPending"
|
|
401
|
+
>
|
|
402
|
+
<!-- The marker class lifts the dialog above the tray (jobs-status-tray.css). -->
|
|
403
|
+
<p class="mint-confirm__message mint-jobs-tray__confirm">{{ confirmText?.message }}</p>
|
|
404
|
+
</ConfirmDialog>
|
|
408
405
|
</div>
|
|
409
406
|
</Teleport>
|
|
410
407
|
</template>
|