@morscherlab/mint-sdk 1.4.0-beta.1 → 1.4.0-beta.10

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.
Files changed (261) hide show
  1. package/dist/{ActionMenu-CScINahU.js → AlertBox-B2Bgr3ei.js} +40 -209
  2. package/dist/AlertBox-B2Bgr3ei.js.map +1 -0
  3. package/dist/{AppSidebar-BmiiPfz9.js → AppSidebar-mvm--GxN.js} +161 -110
  4. package/dist/AppSidebar-mvm--GxN.js.map +1 -0
  5. package/dist/{ConfirmDialog-CJ7hqYUP.js → ConfirmDialog-nbJj51is.js} +2 -2
  6. package/dist/{ConfirmDialog-CJ7hqYUP.js.map → ConfirmDialog-nbJj51is.js.map} +1 -1
  7. package/dist/ExperimentPopover-5T1fAq2L.js +337 -0
  8. package/dist/ExperimentPopover-5T1fAq2L.js.map +1 -0
  9. package/dist/{ExperimentSelectorModal-D-9z7cNx.js → ExperimentSelectorModal-65peG4Up.js} +198 -181
  10. package/dist/ExperimentSelectorModal-65peG4Up.js.map +1 -0
  11. package/dist/{SegmentedControl-DERBN-Xd.js → SegmentedControl-BtnnjWzs.js} +2 -2
  12. package/dist/{SegmentedControl-DERBN-Xd.js.map → SegmentedControl-BtnnjWzs.js.map} +1 -1
  13. package/dist/{SettingsModal-CSLE31yg.js → SettingsModal-DO3IZ7qz.js} +9 -9
  14. package/dist/SettingsModal-DO3IZ7qz.js.map +1 -0
  15. package/dist/components/AppSidebar.vue.d.ts +1 -1
  16. package/dist/components/ArtifactSaveDialog.vue.d.ts +9 -1
  17. package/dist/components/AuditTrail.vue.d.ts +1 -1
  18. package/dist/components/BaseSelect.vue.d.ts +2 -2
  19. package/dist/components/ChartContainer.vue.d.ts +2 -0
  20. package/dist/components/DatePicker.vue.d.ts +1 -1
  21. package/dist/components/ExperimentPopover.vue.d.ts +18 -0
  22. package/dist/components/ExperimentSelectorModal.vue.d.ts +0 -4
  23. package/dist/components/FilePicker.vue.d.ts +1 -1
  24. package/dist/components/FileUploader.vue.d.ts +2 -2
  25. package/dist/components/PluginIcon.vue.d.ts +2 -2
  26. package/dist/components/SampleSelector.vue.d.ts +6 -6
  27. package/dist/components/SequenceProgressBar.vue.d.ts +3 -0
  28. package/dist/components/StepWizard.vue.d.ts +3 -0
  29. package/dist/components/internal/AutoGroupModalInternal.vue.d.ts +1 -1
  30. package/dist/components/internal/DatePickerTimeInternal.vue.d.ts +1 -1
  31. package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +2 -7
  32. package/dist/components/internal/SelectMultipleInternal.vue.d.ts +4 -0
  33. package/dist/composables/createClient.d.ts +2 -0
  34. package/dist/composables/index.d.ts +1 -0
  35. package/dist/composables/pluginIdentity.d.ts +24 -0
  36. package/dist/composables/pluginWorkspaceModel.d.ts +2 -2
  37. package/dist/composables/useAppExperiment.d.ts +8 -0
  38. package/dist/composables/useCalendarGrid.d.ts +1 -1
  39. package/dist/composables/useExperimentSelector.d.ts +6 -2
  40. package/dist/composables/usePlatform.d.ts +1 -0
  41. package/dist/{controlSchemaModel-CP5-_nAj.js → controlSchemaModel-B8CTXagP.js} +2 -2
  42. package/dist/{controlSchemaModel-CP5-_nAj.js.map → controlSchemaModel-B8CTXagP.js.map} +1 -1
  43. package/dist/{data-C2PwI_dn.js → data-CgILxDLW.js} +178 -124
  44. package/dist/data-CgILxDLW.js.map +1 -0
  45. package/dist/data.js +8 -8
  46. package/dist/{experimentStatus-BZmU4czV.js → experimentStatus-Cqp6qNrR.js} +3 -3
  47. package/dist/{experimentStatus-BZmU4czV.js.map → experimentStatus-Cqp6qNrR.js.map} +1 -1
  48. package/dist/index.js +19 -19
  49. package/dist/{install-DAID6c2-.js → install-CsPFJSx3.js} +15 -14
  50. package/dist/{install-DAID6c2-.js.map → install-CsPFJSx3.js.map} +1 -1
  51. package/dist/install.js +1 -1
  52. package/dist/{lab-DLwRl_Ql.js → lab-MHzp-_J8.js} +128 -147
  53. package/dist/lab-MHzp-_J8.js.map +1 -0
  54. package/dist/lab.js +4 -3
  55. package/dist/{records-BzTcxekG.js → records-B6gpM_3e.js} +472 -274
  56. package/dist/records-B6gpM_3e.js.map +1 -0
  57. package/dist/{runtime-D6gVat6r.js → runtime-8bs8EogX.js} +27 -31
  58. package/dist/runtime-8bs8EogX.js.map +1 -0
  59. package/dist/runtime.js +6 -6
  60. package/dist/{shell-5ju97nOS.js → shell-CywENwBT.js} +5 -5
  61. package/dist/{shell-5ju97nOS.js.map → shell-CywENwBT.js.map} +1 -1
  62. package/dist/shell.js +5 -5
  63. package/dist/styles.css +1606 -1666
  64. package/dist/types/componentWorkflowTypes.d.ts +2 -0
  65. package/dist/types/generated/platformContext.d.ts +1 -0
  66. package/dist/types/generated-ui.d.ts +4 -0
  67. package/dist/{BaseModal-DfRIuVoj.js → useDropdownState-8yWMu-7_.js} +60 -2
  68. package/dist/useDropdownState-8yWMu-7_.js.map +1 -0
  69. package/dist/{useExperimentSamples-CxcnxZ8h.js → useExperimentSamples-DHnxKFAF.js} +3 -3
  70. package/dist/{useExperimentSamples-CxcnxZ8h.js.map → useExperimentSamples-DHnxKFAF.js.map} +1 -1
  71. package/dist/{useListReorder-CcbEYxX-.js → useListReorder-BitdUhxn.js} +109 -277
  72. package/dist/useListReorder-BitdUhxn.js.map +1 -0
  73. package/dist/{useRackEditor-BD5NIo5s.js → useRackEditor-DiwCt77M.js} +71 -57
  74. package/dist/useRackEditor-DiwCt77M.js.map +1 -0
  75. package/dist/{useRealtime-PalKue84.js → useRealtime-C3iy9QkU.js} +7 -4
  76. package/dist/useRealtime-C3iy9QkU.js.map +1 -0
  77. package/dist/useRequest-D506eHi8.js +244 -0
  78. package/dist/useRequest-D506eHi8.js.map +1 -0
  79. package/dist/utils/time.d.ts +7 -0
  80. package/package.json +1 -1
  81. package/src/__tests__/components/AppTopBar.test.ts +51 -2
  82. package/src/__tests__/components/AppTopBarPillNav.test.ts +35 -0
  83. package/src/__tests__/components/ArtifactSaveDialog.test.ts +78 -0
  84. package/src/__tests__/components/BaseSelect.multiple.test.ts +87 -32
  85. package/src/__tests__/components/BaseSelect.test.ts +2 -1
  86. package/src/__tests__/components/BatchProgressList.test.ts +16 -0
  87. package/src/__tests__/components/CalendarGridPanel.test.ts +2 -2
  88. package/src/__tests__/components/ChartContainer.test.ts +11 -0
  89. package/src/__tests__/components/ChemicalFormula.test.ts +6 -3
  90. package/src/__tests__/components/DatePicker.datetime.test.ts +6 -0
  91. package/src/__tests__/components/DatePicker.test.ts +21 -0
  92. package/src/__tests__/components/DatePicker.time.test.ts +2 -2
  93. package/src/__tests__/components/EmptyState.test.ts +1 -1
  94. package/src/__tests__/components/ExperimentPopover.test.ts +125 -16
  95. package/src/__tests__/components/ExperimentSelectorModal.test.ts +119 -13
  96. package/src/__tests__/components/GeneratedPluginView.test.ts +22 -1
  97. package/src/__tests__/components/InstrumentStateBadge.test.ts +4 -4
  98. package/src/__tests__/components/PluginWorkspaceView.test.ts +7 -0
  99. package/src/__tests__/components/ResourcePickerListInternal.test.ts +0 -20
  100. package/src/__tests__/components/SampleSelector.test.ts +13 -8
  101. package/src/__tests__/components/SequenceProgressBar.test.ts +12 -0
  102. package/src/__tests__/components/StepWizard.test.ts +11 -0
  103. package/src/__tests__/components/WellPlate.decoration.test.ts +11 -4
  104. package/src/__tests__/components/deprecationTags.test.ts +1 -1
  105. package/src/__tests__/composables/useAppExperiment.test.ts +27 -1
  106. package/src/__tests__/composables/useCalendarGrid.test.ts +4 -2
  107. package/src/__tests__/composables/useExperimentSelector.test.ts +111 -0
  108. package/src/__tests__/styles/baseControlsRules.test.ts +6 -2
  109. package/src/__tests__/styles/controlRefreshRules.test.ts +0 -6
  110. package/src/__tests__/styles/cssTokens.test.ts +2 -2
  111. package/src/__tests__/styles/menusShellRefresh.test.ts +14 -10
  112. package/src/__tests__/utils/plotlyTemplate.test.ts +4 -4
  113. package/src/__tests__/utils/time.test.ts +18 -1
  114. package/src/components/AdductText.story.vue +7 -0
  115. package/src/components/AdductText.vue +1 -1
  116. package/src/components/AlertBox.story.vue +2 -2
  117. package/src/components/AlertBox.vue +18 -16
  118. package/src/components/AppSidebar.story.vue +0 -28
  119. package/src/components/AppTopBar.story.vue +47 -12
  120. package/src/components/AppTopBar.vue +118 -114
  121. package/src/components/ArtifactSaveDialog.story.vue +23 -0
  122. package/src/components/ArtifactSaveDialog.vue +102 -12
  123. package/src/components/AuditTrail.story.vue +2 -1
  124. package/src/components/AuditTrail.vue +2 -21
  125. package/src/components/Avatar.vue +10 -14
  126. package/src/components/BaseSelect.multiple.story.vue +13 -2
  127. package/src/components/BaseSelect.vue +4 -3
  128. package/src/components/BatchProgressList.vue +35 -25
  129. package/src/components/ChartContainer.story.vue +2 -1
  130. package/src/components/ChartContainer.vue +8 -6
  131. package/src/components/ConfirmDialog.story.vue +1 -1
  132. package/src/components/EmptyState.vue +1 -1
  133. package/src/components/ExperimentPopover.story.vue +165 -53
  134. package/src/components/ExperimentPopover.vue +169 -187
  135. package/src/components/ExperimentSelectorModal.story.vue +40 -36
  136. package/src/components/ExperimentSelectorModal.vue +124 -110
  137. package/src/components/FormField.story.vue +8 -48
  138. package/src/components/GeneratedPluginView.vue +7 -1
  139. package/src/components/InstrumentStateBadge.vue +1 -1
  140. package/src/components/JobsStatusTray.vue +1 -1
  141. package/src/components/LoadingSpinner.vue +1 -1
  142. package/src/components/NumberInput.vue +3 -6
  143. package/src/components/PluginIcon.story.vue +2 -10
  144. package/src/components/PluginIcon.vue +2 -2
  145. package/src/components/PluginWorkspaceView.story.vue +1 -1
  146. package/src/components/RuntimeUpdateNotice.vue +1 -1
  147. package/src/components/SampleSelector.story.vue +1 -1
  148. package/src/components/SampleSelector.vue +27 -37
  149. package/src/components/SampleSelectorGroupNode.vue +1 -1
  150. package/src/components/SectionCard.story.vue +2 -2
  151. package/src/components/SequenceProgressBar.story.vue +9 -0
  152. package/src/components/SequenceProgressBar.vue +6 -1
  153. package/src/components/SettingsModal.schema.ts +1 -1
  154. package/src/components/SettingsModal.vue +2 -2
  155. package/src/components/SmartGroupFieldRecipe.story.vue +1 -1
  156. package/src/components/SmartGroupManual.story.vue +1 -1
  157. package/src/components/StepWizard.story.vue +33 -0
  158. package/src/components/StepWizard.vue +8 -1
  159. package/src/components/WellPlate.story.vue +3 -1
  160. package/src/components/WellPlate.vue +7 -0
  161. package/src/components/internal/AppTopBarPillNavInternal.vue +33 -2
  162. package/src/components/internal/DatePickerDateInternal.vue +16 -40
  163. package/src/components/internal/DatePickerDateTimeInternal.vue +3 -13
  164. package/src/components/internal/DatePickerTimeInternal.vue +5 -8
  165. package/src/components/internal/ResourcePickerListInternal.vue +45 -85
  166. package/src/components/internal/SelectMultipleInternal.vue +182 -65
  167. package/src/components/internal/SelectSearchableInternal.vue +2 -3
  168. package/src/composables/createClient.ts +2 -0
  169. package/src/composables/index.ts +1 -0
  170. package/src/composables/pluginIdentity.ts +31 -0
  171. package/src/composables/pluginWorkspaceModel.ts +2 -2
  172. package/src/composables/useAppExperiment.ts +20 -0
  173. package/src/composables/useCalendarGrid.ts +4 -3
  174. package/src/composables/useExperimentSelector.ts +77 -31
  175. package/src/composables/usePluginWorkspace.ts +1 -1
  176. package/src/styles/components/adduct-text.css +13 -5
  177. package/src/styles/components/alert-box.css +39 -58
  178. package/src/styles/components/app-layout.css +6 -5
  179. package/src/styles/components/app-page-selector.css +3 -1
  180. package/src/styles/components/app-pill-nav.css +11 -12
  181. package/src/styles/components/app-sidebar.css +9 -18
  182. package/src/styles/components/app-top-bar.css +57 -55
  183. package/src/styles/components/artifact-save-dialog.css +153 -2
  184. package/src/styles/components/audit-trail.css +16 -19
  185. package/src/styles/components/avatar.css +6 -4
  186. package/src/styles/components/batch-progress-list.css +38 -36
  187. package/src/styles/components/button.css +28 -59
  188. package/src/styles/components/calendar.css +12 -12
  189. package/src/styles/components/chart-container.css +34 -23
  190. package/src/styles/components/checkbox.css +2 -12
  191. package/src/styles/components/collapsible-card.css +27 -29
  192. package/src/styles/components/confirm-dialog.css +13 -14
  193. package/src/styles/components/dataframe.css +12 -4
  194. package/src/styles/components/date-picker.css +80 -92
  195. package/src/styles/components/datetime-picker.css +31 -43
  196. package/src/styles/components/divider.css +20 -20
  197. package/src/styles/components/dose-calculator.css +3 -3
  198. package/src/styles/components/empty-state.css +20 -17
  199. package/src/styles/components/experiment-code-badge.css +2 -0
  200. package/src/styles/components/experiment-popover.css +174 -329
  201. package/src/styles/components/experiment-selector-modal.css +75 -56
  202. package/src/styles/components/file-picker.css +3 -3
  203. package/src/styles/components/file-uploader.css +38 -49
  204. package/src/styles/components/form-builder.css +3 -3
  205. package/src/styles/components/formula-input.css +1 -1
  206. package/src/styles/components/icon-button.css +18 -24
  207. package/src/styles/components/input.css +5 -4
  208. package/src/styles/components/instrument-monitor.css +13 -15
  209. package/src/styles/components/jobs-status-tray.css +31 -25
  210. package/src/styles/components/modal.css +2 -4
  211. package/src/styles/components/molecule-input.css +2 -2
  212. package/src/styles/components/multi-select.css +65 -117
  213. package/src/styles/components/number-input.css +26 -19
  214. package/src/styles/components/pill.css +3 -2
  215. package/src/styles/components/plate-editor.css +0 -1
  216. package/src/styles/components/plugin-icon.css +26 -23
  217. package/src/styles/components/popover.css +2 -2
  218. package/src/styles/components/presence-avatars.css +1 -1
  219. package/src/styles/components/protocol-step-editor.css +1 -3
  220. package/src/styles/components/resource-card.css +2 -2
  221. package/src/styles/components/resource-picker-list.css +6 -57
  222. package/src/styles/components/sample-hierarchy-tree.css +4 -4
  223. package/src/styles/components/sample-selector.css +95 -79
  224. package/src/styles/components/schedule-calendar.css +8 -8
  225. package/src/styles/components/searchable-select.css +12 -12
  226. package/src/styles/components/section-card.css +28 -47
  227. package/src/styles/components/segmented-control.css +18 -15
  228. package/src/styles/components/select.css +3 -3
  229. package/src/styles/components/sequence-input.css +2 -2
  230. package/src/styles/components/sequence-progress-bar.css +17 -8
  231. package/src/styles/components/smart-group.css +18 -18
  232. package/src/styles/components/step-wizard.css +52 -16
  233. package/src/styles/components/tabs.css +2 -2
  234. package/src/styles/components/textarea.css +3 -3
  235. package/src/styles/components/time-picker.css +36 -21
  236. package/src/styles/components/toast.css +12 -10
  237. package/src/styles/components/toggle.css +1 -11
  238. package/src/styles/components/tooltip.css +10 -9
  239. package/src/styles/components/unit-input.css +8 -7
  240. package/src/styles/components/well-plate.css +9 -2
  241. package/src/styles/variables.css +106 -75
  242. package/src/types/componentWorkflowTypes.ts +2 -0
  243. package/src/types/generated/platformContext.ts +1 -0
  244. package/src/types/generated-ui.ts +4 -0
  245. package/src/utils/time.ts +21 -0
  246. package/dist/ActionMenu-CScINahU.js.map +0 -1
  247. package/dist/AppSidebar-BmiiPfz9.js.map +0 -1
  248. package/dist/BaseModal-DfRIuVoj.js.map +0 -1
  249. package/dist/ExperimentPopover-C4sGw-is.js +0 -359
  250. package/dist/ExperimentPopover-C4sGw-is.js.map +0 -1
  251. package/dist/ExperimentSelectorModal-D-9z7cNx.js.map +0 -1
  252. package/dist/SettingsModal-CSLE31yg.js.map +0 -1
  253. package/dist/data-C2PwI_dn.js.map +0 -1
  254. package/dist/lab-DLwRl_Ql.js.map +0 -1
  255. package/dist/records-BzTcxekG.js.map +0 -1
  256. package/dist/runtime-D6gVat6r.js.map +0 -1
  257. package/dist/useListReorder-CcbEYxX-.js.map +0 -1
  258. package/dist/useRackEditor-BD5NIo5s.js.map +0 -1
  259. package/dist/useRealtime-PalKue84.js.map +0 -1
  260. package/dist/useRequest-D5JDfsgZ.js +0 -76
  261. package/dist/useRequest-D5JDfsgZ.js.map +0 -1
@@ -1,10 +1,11 @@
1
- import { a as __exportAll, t as BaseModal_default } from "./BaseModal-DfRIuVoj.js";
1
+ import { n as BaseModal_default, o as __exportAll } from "./useDropdownState-8yWMu-7_.js";
2
2
  import { o as useApi, t as useExperimentStore } from "./experiment-43OJtH7R.js";
3
- import { C as formatExperimentDate, N as BaseSelect_default, b as SORT_OPTIONS, n as ResourcePickerListInternal_default, x as datePresetToISO, y as DATE_PRESET_OPTIONS } from "./records-BzTcxekG.js";
3
+ import { C as formatExperimentDate, n as ResourcePickerListInternal_default, x as datePresetToISO } from "./records-B6gpM_3e.js";
4
+ import { n as ActionMenu_default, t as useRequest } from "./useRequest-D506eHi8.js";
4
5
  import { t as useDebouncedWatch } from "./useDebouncedWatch-BPwyl6eS.js";
5
- import { t as useRequest } from "./useRequest-D5JDfsgZ.js";
6
- import { i as tone, r as format, t as OPTIONS } from "./experimentStatus-BZmU4czV.js";
7
- import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, reactive, ref, unref, vModelCheckbox, watch, withCtx, withDirectives } from "vue";
6
+ import { n as format, r as tone } from "./experimentStatus-Cqp6qNrR.js";
7
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, reactive, ref, renderList, resolveDynamicComponent, toDisplayString, unref, watch, withCtx } from "vue";
8
+ import { ChevronDown, CircleDot, Folder, Shapes, User } from "lucide-vue-next";
8
9
  //#region src/composables/useExperimentSelector.ts
9
10
  function getPlatformContext() {
10
11
  if (typeof window === "undefined") return void 0;
@@ -15,7 +16,7 @@ function getPlatformApiUrl() {
15
16
  }
16
17
  /** Fetches a paginated, filtered experiment list from the platform API for picker and selector UIs. */
17
18
  function useExperimentSelector(options = {}) {
18
- const { limit = 100, immediate = false, experimentType, allowedExperimentTypes: optionAllowedExperimentTypes, apiBaseUrl } = options;
19
+ const { limit = 100, immediate = false, experimentType, allowedExperimentTypes: optionAllowedExperimentTypes, apiBaseUrl, withStatusCounts = false } = options;
19
20
  const platformBase = apiBaseUrl ?? getPlatformApiUrl();
20
21
  const api = useApi();
21
22
  const experiments = ref([]);
@@ -31,13 +32,17 @@ function useExperimentSelector(options = {}) {
31
32
  const experimentTypes = ref([]);
32
33
  const projects = ref([]);
33
34
  let filterOptionsFetched = false;
35
+ const statusCounts = ref({});
36
+ let statsGeneration = 0;
37
+ let countedScope = null;
34
38
  const hasMore = computed(() => experiments.value.length < total.value);
35
39
  const filters = reactive({
36
40
  search: void 0,
37
41
  status: void 0,
38
42
  project: void 0,
39
43
  experimentType: void 0,
40
- datePreset: void 0
44
+ datePreset: void 0,
45
+ mine: void 0
41
46
  });
42
47
  function normalizeExperimentTypes(values) {
43
48
  if (values == null) return null;
@@ -77,16 +82,22 @@ function useExperimentSelector(options = {}) {
77
82
  }
78
83
  return { kind: "all" };
79
84
  }
80
- function buildExperimentListUrl(typeFilter, skip, pageLimit) {
85
+ /** Query params shared by the list and the stats route; status, sort and paging are list-only. */
86
+ function scopeParams(typeFilter) {
81
87
  const params = new URLSearchParams();
82
88
  if (typeFilter) params.set("experiment_type", typeFilter);
83
- if (filters.status) params.set("status", filters.status);
84
89
  if (filters.search) params.set("search", filters.search);
85
90
  if (filters.project) params.set("project", filters.project);
91
+ if (filters.mine) params.set("mine", "true");
92
+ if (filters.datePreset) params.set("created_after", datePresetToISO(filters.datePreset));
93
+ return params;
94
+ }
95
+ function buildExperimentListUrl(typeFilter, skip, pageLimit) {
96
+ const params = scopeParams(typeFilter);
97
+ if (filters.status) params.set("status", filters.status);
86
98
  const { sortBy, sortOrder } = parseSortKey();
87
99
  params.set("sort_by", sortBy);
88
100
  params.set("sort_order", sortOrder);
89
- if (filters.datePreset) params.set("created_after", datePresetToISO(filters.datePreset));
90
101
  params.set("limit", String(pageLimit));
91
102
  params.set("skip", String(skip));
92
103
  const query = params.toString();
@@ -170,6 +181,42 @@ function useExperimentSelector(options = {}) {
170
181
  label: p.name
171
182
  }));
172
183
  }
184
+ /** The filters the stats route reads; status, sort and paging do not change the counts. */
185
+ function countScope() {
186
+ return JSON.stringify([
187
+ filters.search,
188
+ filters.project,
189
+ filters.experimentType,
190
+ filters.datePreset,
191
+ filters.mine
192
+ ]);
193
+ }
194
+ function refreshStatusCountsOnScopeChange() {
195
+ if (withStatusCounts && countScope() !== countedScope) fetchStatusCounts();
196
+ }
197
+ async function fetchStatusCounts() {
198
+ const generation = ++statsGeneration;
199
+ countedScope = countScope();
200
+ const scope = resolveExperimentTypeScope();
201
+ if (scope.kind === "blocked") {
202
+ statusCounts.value = {};
203
+ return;
204
+ }
205
+ const types = scope.kind === "multi" ? scope.experimentTypes : [scope.kind === "single" ? scope.experimentType : void 0];
206
+ const base = platformBase ?? "";
207
+ try {
208
+ const responses = await Promise.all(types.map((type) => api.get(`${base}/experiments/stats?${scopeParams(type)}`)));
209
+ if (generation !== statsGeneration) return;
210
+ const counts = {};
211
+ for (const response of responses) for (const [status, count] of Object.entries(response.by_status)) counts[status] = (counts[status] ?? 0) + count;
212
+ statusCounts.value = counts;
213
+ } catch {
214
+ if (generation === statsGeneration) {
215
+ statusCounts.value = {};
216
+ countedScope = null;
217
+ }
218
+ }
219
+ }
173
220
  async function loadMore() {
174
221
  if (!hasMore.value || isLoading.value) return;
175
222
  page.value++;
@@ -192,46 +239,37 @@ function useExperimentSelector(options = {}) {
192
239
  filters.project = void 0;
193
240
  filters.experimentType = void 0;
194
241
  filters.datePreset = void 0;
242
+ filters.mine = void 0;
195
243
  sortKey.value = "created_at:desc";
196
244
  page.value = 0;
197
245
  request.clearError();
198
246
  }
199
- const groupedByProject = computed(() => {
200
- const groups = /* @__PURE__ */ new Map();
201
- for (const exp of experiments.value) {
202
- const key = exp.project_name ?? exp.project ?? "No project";
203
- const list = groups.get(key);
204
- if (list) list.push(exp);
205
- else groups.set(key, [exp]);
206
- }
207
- return [...groups.entries()].sort(([a], [b]) => {
208
- if (a === "No project") return 1;
209
- if (b === "No project") return -1;
210
- return a.localeCompare(b);
211
- });
212
- });
213
247
  watch(() => [
214
248
  filters.search,
215
249
  filters.status,
216
250
  filters.project,
217
251
  filters.experimentType,
218
252
  filters.datePreset,
253
+ filters.mine,
219
254
  sortKey.value
220
255
  ], () => invalidateFetch(), { flush: "sync" });
221
256
  const searchWatch = useDebouncedWatch(() => filters.search, () => {
222
257
  page.value = 0;
223
258
  fetchExperiments();
259
+ refreshStatusCountsOnScopeChange();
224
260
  }, { delay: 300 });
225
261
  watch(() => [
226
262
  filters.status,
227
263
  filters.project,
228
264
  filters.experimentType,
229
265
  filters.datePreset,
266
+ filters.mine,
230
267
  sortKey.value
231
268
  ], () => {
232
269
  searchWatch.cancel();
233
270
  page.value = 0;
234
271
  fetchExperiments();
272
+ refreshStatusCountsOnScopeChange();
235
273
  });
236
274
  if (immediate) fetchExperiments();
237
275
  return {
@@ -247,42 +285,31 @@ function useExperimentSelector(options = {}) {
247
285
  sortKey,
248
286
  experimentTypes,
249
287
  projects,
250
- groupedByProject,
288
+ statusCounts,
251
289
  fetch: fetchExperiments,
252
290
  loadMore,
253
291
  reset,
254
292
  select,
255
293
  clear,
256
- fetchFilterOptions
294
+ fetchFilterOptions,
295
+ fetchStatusCounts
257
296
  };
258
297
  }
259
298
  //#endregion
260
299
  //#region src/components/ExperimentSelectorModal.vue?vue&type=script&setup=true&lang.ts
261
- var _hoisted_1 = { class: "mint-experiment-selector__filter-select" };
262
- var _hoisted_2 = {
263
- key: 0,
264
- class: "mint-experiment-selector__filter-select"
265
- };
266
- var _hoisted_3 = ["aria-expanded"];
300
+ var _hoisted_1 = [
301
+ "aria-label",
302
+ "aria-expanded",
303
+ "onClick",
304
+ "onKeydown"
305
+ ];
306
+ var _hoisted_2 = { class: "mint-experiment-selector__toggle-label" };
307
+ var _hoisted_3 = ["aria-pressed"];
267
308
  var _hoisted_4 = {
268
- key: 0,
269
- class: "mint-experiment-selector__filters-dot"
270
- };
271
- var _hoisted_5 = {
272
- key: 0,
273
- class: "mint-experiment-selector__filters-advanced"
274
- };
275
- var _hoisted_6 = {
276
- key: 0,
277
- class: "mint-experiment-selector__filter-select"
278
- };
279
- var _hoisted_7 = { class: "mint-experiment-selector__filter-select" };
280
- var _hoisted_8 = { class: "mint-experiment-selector__filter-select" };
281
- var _hoisted_9 = { class: "mint-experiment-selector__group-toggle" };
282
- var _hoisted_10 = {
283
309
  key: 0,
284
310
  class: "mint-experiment-selector__footer"
285
311
  };
312
+ /** Every item carries `checked`, so ActionMenu renders a single-choice menu. */
286
313
  var ExperimentSelectorModal_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
287
314
  __name: "ExperimentSelectorModal",
288
315
  props: {
@@ -291,15 +318,7 @@ var ExperimentSelectorModal_vue_vue_type_script_setup_true_lang_default = /*@__P
291
318
  allowedExperimentTypes: {},
292
319
  currentExperimentId: { default: null },
293
320
  title: { default: "Select Experiment" },
294
- size: { default: "full" },
295
- groupByProject: {
296
- type: Boolean,
297
- default: false
298
- },
299
- showFilters: {
300
- type: Boolean,
301
- default: false
302
- }
321
+ size: { default: "lg" }
303
322
  },
304
323
  emits: ["update:open"],
305
324
  setup(__props, { emit: __emit }) {
@@ -314,36 +333,80 @@ var ExperimentSelectorModal_vue_vue_type_script_setup_true_lang_default = /*@__P
314
333
  const props = __props;
315
334
  const emit = __emit;
316
335
  const experimentStore = useExperimentStore();
317
- const { experiments, filters, isLoading, error, sortKey, experimentTypes, projects, groupedByProject, fetch: fetchExperiments, fetchFilterOptions } = useExperimentSelector({
336
+ const { experiments, filters, isLoading, error, experimentTypes, projects, statusCounts, fetch: fetchExperiments, fetchFilterOptions, fetchStatusCounts } = useExperimentSelector({
318
337
  experimentType: props.experimentType,
319
- allowedExperimentTypes: props.allowedExperimentTypes
338
+ allowedExperimentTypes: props.allowedExperimentTypes,
339
+ withStatusCounts: true
320
340
  });
321
- const showAdvanced = ref(props.showFilters);
322
- const groupToggle = ref(props.groupByProject);
323
341
  const openCount = ref(0);
324
- const hasActiveAdvancedFilters = computed(() => !!(filters.project || filters.experimentType || filters.datePreset || sortKey.value !== "created_at:desc"));
325
- const typeFilterOptions = computed(() => [{
326
- value: "",
327
- label: "All types"
328
- }, ...experimentTypes.value.map((t) => ({
329
- value: t.value,
330
- label: t.label
331
- }))]);
332
- const canChooseExperimentType = computed(() => !props.experimentType && typeFilterOptions.value.length > 2);
333
- const projectFilterOptions = computed(() => [{
334
- value: "",
335
- label: "All projects"
336
- }, ...projects.value]);
337
- const groups = computed(() => groupToggle.value ? groupedByProject.value.map(([label, items]) => ({
338
- label,
339
- items
340
- })) : null);
342
+ const STATUS_ORDER = [
343
+ "ongoing",
344
+ "planned",
345
+ "completed",
346
+ "cancelled"
347
+ ];
348
+ const canChooseExperimentType = computed(() => !props.experimentType && experimentTypes.value.length > 1);
349
+ function choiceItems(key, anyLabel, options) {
350
+ const current = filters[key] ?? "";
351
+ return [{
352
+ value: "",
353
+ label: anyLabel
354
+ }, ...options].map((option) => ({
355
+ id: option.value,
356
+ label: option.label,
357
+ checked: option.value === current
358
+ }));
359
+ }
360
+ const filterMenus = computed(() => {
361
+ const menus = [{
362
+ key: "status",
363
+ label: "Status",
364
+ icon: CircleDot,
365
+ items: choiceItems("status", "Any status", STATUS_ORDER.map((status) => {
366
+ const count = statusCounts.value[status];
367
+ const label = format(status);
368
+ return {
369
+ value: status,
370
+ label: count === void 0 ? label : `${label} · ${count}`
371
+ };
372
+ })),
373
+ activeLabel: filters.status ? format(filters.status) : void 0,
374
+ dotTone: filters.status ? tone(filters.status) : void 0
375
+ }];
376
+ if (projects.value.length) menus.push({
377
+ key: "project",
378
+ label: "Project",
379
+ icon: Folder,
380
+ items: choiceItems("project", "Any project", projects.value),
381
+ activeLabel: filters.project ?? void 0
382
+ });
383
+ if (canChooseExperimentType.value) menus.push({
384
+ key: "experimentType",
385
+ label: "Type",
386
+ icon: Shapes,
387
+ items: choiceItems("experimentType", "Any type", experimentTypes.value),
388
+ activeLabel: experimentTypes.value.find((t) => t.value === filters.experimentType)?.label ?? filters.experimentType ?? void 0
389
+ });
390
+ return menus;
391
+ });
392
+ const activeFilterCount = computed(() => [
393
+ filters.status,
394
+ filters.project,
395
+ filters.experimentType,
396
+ filters.mine
397
+ ].filter(Boolean).length);
398
+ function clearFilters() {
399
+ filters.status = void 0;
400
+ filters.project = void 0;
401
+ filters.experimentType = void 0;
402
+ filters.mine = void 0;
403
+ }
341
404
  function describe(experiment) {
342
405
  const owner = experiment.created_by_user;
343
406
  return {
344
407
  code: experiment.experiment_code,
345
408
  title: experiment.name,
346
- meta: [...groupToggle.value ? [] : [experiment.project_name || experiment.project || ""], owner ? [owner.first_name, owner.last_name].filter(Boolean).join(" ") || owner.username : ""].filter(Boolean),
409
+ meta: [experiment.project_name || experiment.project || "", owner ? [owner.first_name, owner.last_name].filter(Boolean).join(" ") || owner.username : ""].filter(Boolean),
347
410
  status: experiment.status ? {
348
411
  label: format(experiment.status),
349
412
  tone: tone(experiment.status)
@@ -354,9 +417,6 @@ var ExperimentSelectorModal_vue_vue_type_script_setup_true_lang_default = /*@__P
354
417
  function setFilter(key, value) {
355
418
  filters[key] = String(value) || void 0;
356
419
  }
357
- function handleSortChange(value) {
358
- sortKey.value = String(value) || "created_at:desc";
359
- }
360
420
  function handleSelect(experiment) {
361
421
  experimentStore.select(experiment);
362
422
  emit("update:open", false);
@@ -370,6 +430,7 @@ var ExperimentSelectorModal_vue_vue_type_script_setup_true_lang_default = /*@__P
370
430
  openCount.value++;
371
431
  fetchFilterOptions();
372
432
  fetchExperiments();
433
+ fetchStatusCounts();
373
434
  }
374
435
  }, { immediate: true });
375
436
  return (_ctx, _cache) => {
@@ -377,133 +438,89 @@ var ExperimentSelectorModal_vue_vue_type_script_setup_true_lang_default = /*@__P
377
438
  open: __props.open,
378
439
  title: __props.title,
379
440
  size: __props.size,
380
- "onUpdate:open": _cache[7] || (_cache[7] = ($event) => emit("update:open", $event))
441
+ "onUpdate:open": _cache[2] || (_cache[2] = ($event) => emit("update:open", $event))
381
442
  }, {
382
443
  default: withCtx(() => [(openBlock(), createBlock(ResourcePickerListInternal_default, {
383
444
  key: openCount.value,
384
445
  search: unref(filters).search ?? "",
385
446
  class: "mint-experiment-selector",
447
+ "autofocus-search": "",
386
448
  label: "Experiments",
387
449
  placeholder: "Search experiments…",
388
450
  items: unref(experiments),
389
- groups: groups.value,
390
451
  "item-key": (experiment) => experiment.id,
391
452
  describe,
392
453
  "selected-key": __props.currentExperimentId,
393
454
  loading: unref(isLoading),
394
455
  error: unref(error),
395
456
  "empty-title": "No experiments found",
396
- "onUpdate:search": _cache[6] || (_cache[6] = ($event) => unref(filters).search = $event),
457
+ "onUpdate:search": _cache[1] || (_cache[1] = ($event) => unref(filters).search = $event),
397
458
  onSelect: handleSelect
398
459
  }, {
399
460
  filters: withCtx(() => [
400
- createElementVNode("div", _hoisted_1, [createVNode(BaseSelect_default, {
401
- "model-value": unref(filters).status ?? "",
402
- options: OPTIONS,
403
- size: "sm",
404
- "aria-label": "Filter by status",
405
- "onUpdate:modelValue": _cache[0] || (_cache[0] = (v) => setFilter("status", v))
406
- }, null, 8, ["model-value", "options"])]),
407
- canChooseExperimentType.value ? (openBlock(), createElementBlock("div", _hoisted_2, [createVNode(BaseSelect_default, {
408
- "model-value": unref(filters).experimentType ?? "",
409
- options: typeFilterOptions.value,
410
- size: "sm",
411
- "aria-label": "Filter by type",
412
- "onUpdate:modelValue": _cache[1] || (_cache[1] = (v) => setFilter("experimentType", v))
413
- }, null, 8, ["model-value", "options"])])) : createCommentVNode("", true),
461
+ (openBlock(true), createElementBlock(Fragment, null, renderList(filterMenus.value, (menu) => {
462
+ return openBlock(), createBlock(ActionMenu_default, {
463
+ key: menu.key,
464
+ label: menu.label,
465
+ items: menu.items,
466
+ onSelect: (item) => setFilter(menu.key, item.id)
467
+ }, {
468
+ trigger: withCtx(({ open: menuOpen, toggle, keydown }) => [createElementVNode("button", {
469
+ type: "button",
470
+ class: normalizeClass(["mint-experiment-selector__toggle", { "mint-experiment-selector__toggle--active": menu.activeLabel }]),
471
+ "aria-label": `${menu.label}: ${menu.activeLabel ?? menu.items[0].label}`,
472
+ "aria-haspopup": "menu",
473
+ "aria-expanded": menuOpen,
474
+ onClick: toggle,
475
+ onKeydown: keydown
476
+ }, [
477
+ menu.dotTone ? (openBlock(), createElementBlock("span", {
478
+ key: 0,
479
+ class: normalizeClass(["mint-experiment-selector__dot", `mint-experiment-selector__dot--${menu.dotTone}`]),
480
+ "aria-hidden": "true"
481
+ }, null, 2)) : (openBlock(), createBlock(resolveDynamicComponent(menu.icon), {
482
+ key: 1,
483
+ size: 13,
484
+ "aria-hidden": "true"
485
+ })),
486
+ createElementVNode("span", _hoisted_2, toDisplayString(menu.activeLabel ?? menu.label), 1),
487
+ createVNode(unref(ChevronDown), {
488
+ size: 12,
489
+ "aria-hidden": "true"
490
+ })
491
+ ], 42, _hoisted_1)]),
492
+ _: 2
493
+ }, 1032, [
494
+ "label",
495
+ "items",
496
+ "onSelect"
497
+ ]);
498
+ }), 128)),
414
499
  createElementVNode("button", {
415
- class: normalizeClass(["mint-experiment-selector__filters-toggle", { "mint-experiment-selector__filters-toggle--active": hasActiveAdvancedFilters.value }]),
416
500
  type: "button",
417
- "aria-expanded": showAdvanced.value,
418
- onClick: _cache[2] || (_cache[2] = ($event) => showAdvanced.value = !showAdvanced.value)
419
- }, [
420
- _cache[8] || (_cache[8] = createElementVNode("svg", {
421
- "aria-hidden": "true",
422
- width: "14",
423
- height: "14",
424
- viewBox: "0 0 24 24",
425
- fill: "none",
426
- stroke: "currentColor",
427
- "stroke-width": "2",
428
- "stroke-linecap": "round",
429
- "stroke-linejoin": "round"
430
- }, [
431
- createElementVNode("line", {
432
- x1: "4",
433
- y1: "6",
434
- x2: "20",
435
- y2: "6"
436
- }),
437
- createElementVNode("line", {
438
- x1: "8",
439
- y1: "12",
440
- x2: "20",
441
- y2: "12"
442
- }),
443
- createElementVNode("line", {
444
- x1: "12",
445
- y1: "18",
446
- x2: "20",
447
- y2: "18"
448
- }),
449
- createElementVNode("circle", {
450
- cx: "6",
451
- cy: "12",
452
- r: "2"
453
- }),
454
- createElementVNode("circle", {
455
- cx: "10",
456
- cy: "18",
457
- r: "2"
458
- }),
459
- createElementVNode("circle", {
460
- cx: "6",
461
- cy: "6",
462
- r: "2"
463
- })
464
- ], -1)),
465
- _cache[9] || (_cache[9] = createTextVNode(" Filters ", -1)),
466
- hasActiveAdvancedFilters.value ? (openBlock(), createElementBlock("span", _hoisted_4)) : createCommentVNode("", true)
467
- ], 10, _hoisted_3)
501
+ class: normalizeClass(["mint-experiment-selector__toggle", { "mint-experiment-selector__toggle--active": unref(filters).mine }]),
502
+ "aria-pressed": !!unref(filters).mine,
503
+ onClick: _cache[0] || (_cache[0] = ($event) => unref(filters).mine = unref(filters).mine ? void 0 : true)
504
+ }, [createVNode(unref(User), {
505
+ size: 13,
506
+ "aria-hidden": "true"
507
+ }), _cache[3] || (_cache[3] = createTextVNode(" Mine ", -1))], 10, _hoisted_3),
508
+ activeFilterCount.value ? (openBlock(), createElementBlock("button", {
509
+ key: 0,
510
+ type: "button",
511
+ class: "mint-experiment-selector__clear-filters",
512
+ onClick: clearFilters
513
+ }, " Clear " + toDisplayString(activeFilterCount.value), 1)) : createCommentVNode("", true)
468
514
  ]),
469
- advanced: withCtx(() => [showAdvanced.value ? (openBlock(), createElementBlock("div", _hoisted_5, [
470
- projectFilterOptions.value.length > 1 ? (openBlock(), createElementBlock("div", _hoisted_6, [createVNode(BaseSelect_default, {
471
- "model-value": unref(filters).project ?? "",
472
- options: projectFilterOptions.value,
473
- size: "sm",
474
- "aria-label": "Filter by project",
475
- "onUpdate:modelValue": _cache[3] || (_cache[3] = (v) => setFilter("project", v))
476
- }, null, 8, ["model-value", "options"])])) : createCommentVNode("", true),
477
- createElementVNode("div", _hoisted_7, [createVNode(BaseSelect_default, {
478
- "model-value": unref(filters).datePreset ?? "",
479
- options: unref(DATE_PRESET_OPTIONS),
480
- size: "sm",
481
- "aria-label": "Filter by date",
482
- "onUpdate:modelValue": _cache[4] || (_cache[4] = (v) => setFilter("datePreset", v))
483
- }, null, 8, ["model-value", "options"])]),
484
- createElementVNode("div", _hoisted_8, [createVNode(BaseSelect_default, {
485
- "model-value": unref(sortKey),
486
- options: unref(SORT_OPTIONS),
487
- size: "sm",
488
- "aria-label": "Sort",
489
- "onUpdate:modelValue": handleSortChange
490
- }, null, 8, ["model-value", "options"])]),
491
- createElementVNode("label", _hoisted_9, [withDirectives(createElementVNode("input", {
492
- "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => groupToggle.value = $event),
493
- type: "checkbox",
494
- class: "mint-experiment-selector__group-checkbox"
495
- }, null, 512), [[vModelCheckbox, groupToggle.value]]), _cache[10] || (_cache[10] = createTextVNode(" Group by project ", -1))])
496
- ])) : createCommentVNode("", true)]),
497
515
  _: 1
498
516
  }, 8, [
499
517
  "search",
500
518
  "items",
501
- "groups",
502
519
  "item-key",
503
520
  "selected-key",
504
521
  "loading",
505
522
  "error"
506
- ])), __props.currentExperimentId != null ? (openBlock(), createElementBlock("div", _hoisted_10, [createElementVNode("button", {
523
+ ])), __props.currentExperimentId != null ? (openBlock(), createElementBlock("div", _hoisted_4, [createElementVNode("button", {
507
524
  type: "button",
508
525
  class: "mint-experiment-selector__clear-btn",
509
526
  onClick: handleDeselect
@@ -524,4 +541,4 @@ var ExperimentSelectorModal_default = ExperimentSelectorModal_vue_vue_type_scrip
524
541
  //#endregion
525
542
  export { ExperimentSelectorModal_exports as n, useExperimentSelector as r, ExperimentSelectorModal_default as t };
526
543
 
527
- //# sourceMappingURL=ExperimentSelectorModal-D-9z7cNx.js.map
544
+ //# sourceMappingURL=ExperimentSelectorModal-65peG4Up.js.map