@morscherlab/mld-sdk 0.9.4 → 0.9.5

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.
@@ -5,17 +5,23 @@ interface Props {
5
5
  currentExperimentId?: number | null;
6
6
  title?: string;
7
7
  size?: ModalSize;
8
+ groupByProject?: boolean;
9
+ showFilters?: boolean;
8
10
  }
9
11
  declare const _default: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
10
12
  select: (experiment: ExperimentSummary) => any;
11
13
  "update:modelValue": (value: boolean) => any;
14
+ deselect: () => any;
12
15
  }, string, import('vue').PublicProps, Readonly<Props> & Readonly<{
13
16
  onSelect?: ((experiment: ExperimentSummary) => any) | undefined;
14
17
  "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
18
+ onDeselect?: (() => any) | undefined;
15
19
  }>, {
16
20
  size: ModalSize;
17
21
  title: string;
22
+ showFilters: boolean;
18
23
  currentExperimentId: number | null;
24
+ groupByProject: boolean;
19
25
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
20
26
  listRef: HTMLDivElement;
21
27
  }, any>;
@@ -1,6 +1,6 @@
1
- import { defineComponent, ref, watch, openBlock, createBlock, withCtx, createElementVNode, createVNode, unref, createElementBlock, Fragment, renderList, toDisplayString, normalizeClass, createTextVNode, createCommentVNode, nextTick } from "vue";
1
+ import { defineComponent, ref, computed, reactive, watch, openBlock, createBlock, withCtx, createElementVNode, createVNode, unref, createElementBlock, createCommentVNode, normalizeClass, createTextVNode, withDirectives, vModelCheckbox, Fragment, renderList, toDisplayString, nextTick } from "vue";
2
2
  import { useExperimentSelector } from "../composables/useExperimentSelector.js";
3
- import { EXPERIMENT_STATUS_OPTIONS, formatExperimentDate, EXPERIMENT_STATUS_VARIANT_MAP } from "../composables/experiment-utils.js";
3
+ import { EXPERIMENT_STATUS_OPTIONS, DATE_PRESET_OPTIONS, SORT_OPTIONS, formatExperimentDate, EXPERIMENT_STATUS_VARIANT_MAP } from "../composables/experiment-utils.js";
4
4
  import _sfc_main$1 from "./BaseModal.vue.js";
5
5
  /* empty css */
6
6
  import _sfc_main$2 from "./BaseInput.vue.js";
@@ -15,23 +15,53 @@ import _sfc_main$5 from "./EmptyState.vue.js";
15
15
  /* empty css */
16
16
  import _sfc_main$6 from "./ExperimentCodeBadge.vue.js";
17
17
  /* empty css */
18
- const _hoisted_1 = { class: "mld-experiment-selector__filters" };
18
+ const _hoisted_1 = { class: "mld-experiment-selector__filters-row" };
19
19
  const _hoisted_2 = { class: "mld-experiment-selector__search" };
20
- const _hoisted_3 = { class: "mld-experiment-selector__status-filter" };
20
+ const _hoisted_3 = { class: "mld-experiment-selector__filter-select" };
21
21
  const _hoisted_4 = {
22
22
  key: 0,
23
- class: "mld-experiment-selector__skeleton"
23
+ class: "mld-experiment-selector__filter-select"
24
+ };
25
+ const _hoisted_5 = {
26
+ key: 0,
27
+ class: "mld-experiment-selector__filters-dot"
24
28
  };
25
- const _hoisted_5 = { class: "mld-experiment-selector__skeleton-content" };
26
29
  const _hoisted_6 = {
30
+ key: 0,
31
+ class: "mld-experiment-selector__filters-advanced"
32
+ };
33
+ const _hoisted_7 = {
34
+ key: 0,
35
+ class: "mld-experiment-selector__filter-select"
36
+ };
37
+ const _hoisted_8 = { class: "mld-experiment-selector__filter-select" };
38
+ const _hoisted_9 = { class: "mld-experiment-selector__filter-select" };
39
+ const _hoisted_10 = { class: "mld-experiment-selector__group-toggle" };
40
+ const _hoisted_11 = {
27
41
  key: 1,
42
+ class: "mld-experiment-selector__skeleton"
43
+ };
44
+ const _hoisted_12 = { class: "mld-experiment-selector__skeleton-content" };
45
+ const _hoisted_13 = {
46
+ key: 2,
28
47
  class: "mld-experiment-selector__error"
29
48
  };
30
- const _hoisted_7 = ["onClick", "onMouseenter"];
31
- const _hoisted_8 = { class: "mld-experiment-selector__row-content" };
32
- const _hoisted_9 = { class: "mld-experiment-selector__name" };
33
- const _hoisted_10 = { class: "mld-experiment-selector__meta" };
34
- const _hoisted_11 = { key: 0 };
49
+ const _hoisted_14 = ["onClick"];
50
+ const _hoisted_15 = { class: "mld-experiment-selector__group-name" };
51
+ const _hoisted_16 = { class: "mld-experiment-selector__group-count" };
52
+ const _hoisted_17 = ["onClick", "onMouseenter"];
53
+ const _hoisted_18 = { class: "mld-experiment-selector__row-content" };
54
+ const _hoisted_19 = { class: "mld-experiment-selector__name" };
55
+ const _hoisted_20 = { class: "mld-experiment-selector__meta" };
56
+ const _hoisted_21 = ["onClick", "onMouseenter"];
57
+ const _hoisted_22 = { class: "mld-experiment-selector__row-content" };
58
+ const _hoisted_23 = { class: "mld-experiment-selector__name" };
59
+ const _hoisted_24 = { class: "mld-experiment-selector__meta" };
60
+ const _hoisted_25 = { key: 0 };
61
+ const _hoisted_26 = {
62
+ key: 6,
63
+ class: "mld-experiment-selector__footer"
64
+ };
35
65
  const _sfc_main = /* @__PURE__ */ defineComponent({
36
66
  __name: "ExperimentSelectorModal",
37
67
  props: {
@@ -39,9 +69,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
39
69
  experimentType: {},
40
70
  currentExperimentId: { default: null },
41
71
  title: { default: "Select Experiment" },
42
- size: { default: "full" }
72
+ size: { default: "full" },
73
+ groupByProject: { type: Boolean, default: false },
74
+ showFilters: { type: Boolean, default: false }
43
75
  },
44
- emits: ["update:modelValue", "select"],
76
+ emits: ["update:modelValue", "select", "deselect"],
45
77
  setup(__props, { emit: __emit }) {
46
78
  const props = __props;
47
79
  const emit = __emit;
@@ -50,25 +82,55 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
50
82
  filters,
51
83
  isLoading,
52
84
  error,
53
- fetch: fetchExperiments
85
+ sortKey,
86
+ experimentTypes,
87
+ projects,
88
+ groupedByProject,
89
+ fetch: fetchExperiments,
90
+ fetchFilterOptions
54
91
  } = useExperimentSelector({
55
92
  experimentType: props.experimentType
56
93
  });
57
94
  const activeIndex = ref(-1);
58
95
  const listRef = ref(null);
59
- function handleStatusChange(value) {
60
- filters.status = String(value) || null;
96
+ const showAdvanced = ref(props.showFilters);
97
+ const groupToggle = ref(props.groupByProject);
98
+ const hasActiveAdvancedFilters = computed(
99
+ () => !!(filters.project || filters.experimentType || filters.datePreset || sortKey.value !== "created_at:desc")
100
+ );
101
+ const typeFilterOptions = computed(() => [
102
+ { value: "", label: "All types" },
103
+ ...experimentTypes.value.map((t) => ({ value: t.value, label: t.label }))
104
+ ]);
105
+ const projectFilterOptions = computed(() => [
106
+ { value: "", label: "All projects" },
107
+ ...projects.value
108
+ ]);
109
+ const flatExperiments = computed(() => {
110
+ if (!groupToggle.value) return experiments.value;
111
+ return groupedByProject.value.flatMap(([, exps]) => exps);
112
+ });
113
+ function setFilter(key, value) {
114
+ filters[key] = String(value) || null;
115
+ }
116
+ function handleSortChange(value) {
117
+ sortKey.value = String(value) || "created_at:desc";
61
118
  }
62
119
  function handleSelect(experiment) {
63
120
  emit("select", experiment);
64
121
  emit("update:modelValue", false);
65
122
  }
123
+ function handleDeselect() {
124
+ emit("deselect");
125
+ emit("update:modelValue", false);
126
+ }
66
127
  function handleKeydown(event) {
67
- if (!experiments.value.length) return;
128
+ const list = flatExperiments.value;
129
+ if (!list.length) return;
68
130
  switch (event.key) {
69
131
  case "ArrowDown":
70
132
  event.preventDefault();
71
- activeIndex.value = Math.min(activeIndex.value + 1, experiments.value.length - 1);
133
+ activeIndex.value = Math.min(activeIndex.value + 1, list.length - 1);
72
134
  scrollActiveIntoView();
73
135
  break;
74
136
  case "ArrowUp":
@@ -78,8 +140,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
78
140
  break;
79
141
  case "Enter":
80
142
  event.preventDefault();
81
- if (activeIndex.value >= 0 && activeIndex.value < experiments.value.length) {
82
- handleSelect(experiments.value[activeIndex.value]);
143
+ if (activeIndex.value >= 0 && activeIndex.value < list.length) {
144
+ handleSelect(list[activeIndex.value]);
83
145
  }
84
146
  break;
85
147
  }
@@ -91,6 +153,22 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
91
153
  row == null ? void 0 : row.scrollIntoView({ block: "nearest" });
92
154
  });
93
155
  }
156
+ const flatIndexMap = computed(() => {
157
+ const map = /* @__PURE__ */ new Map();
158
+ flatExperiments.value.forEach((exp, i) => map.set(exp.id, i));
159
+ return map;
160
+ });
161
+ function getFlatIndex(experiment) {
162
+ return flatIndexMap.value.get(experiment.id) ?? -1;
163
+ }
164
+ const collapsedGroups = reactive(/* @__PURE__ */ new Set());
165
+ function toggleGroup(groupName) {
166
+ if (collapsedGroups.has(groupName)) {
167
+ collapsedGroups.delete(groupName);
168
+ } else {
169
+ collapsedGroups.add(groupName);
170
+ }
171
+ }
94
172
  watch(experiments, () => {
95
173
  activeIndex.value = -1;
96
174
  });
@@ -99,6 +177,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
99
177
  (isOpen) => {
100
178
  if (isOpen) {
101
179
  activeIndex.value = -1;
180
+ collapsedGroups.clear();
181
+ fetchFilterOptions();
102
182
  fetchExperiments();
103
183
  }
104
184
  }
@@ -108,7 +188,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
108
188
  "model-value": __props.modelValue,
109
189
  title: __props.title,
110
190
  size: __props.size,
111
- "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => emit("update:modelValue", $event))
191
+ "onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => emit("update:modelValue", $event))
112
192
  }, {
113
193
  default: withCtx(() => [
114
194
  createElementVNode("div", {
@@ -130,17 +210,113 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
130
210
  "model-value": unref(filters).status ?? "",
131
211
  options: unref(EXPERIMENT_STATUS_OPTIONS),
132
212
  size: "sm",
133
- "onUpdate:modelValue": handleStatusChange
213
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = (v) => setFilter("status", v))
134
214
  }, null, 8, ["model-value", "options"])
135
- ])
215
+ ]),
216
+ typeFilterOptions.value.length > 1 ? (openBlock(), createElementBlock("div", _hoisted_4, [
217
+ createVNode(_sfc_main$3, {
218
+ "model-value": unref(filters).experimentType ?? "",
219
+ options: typeFilterOptions.value,
220
+ size: "sm",
221
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = (v) => setFilter("experimentType", v))
222
+ }, null, 8, ["model-value", "options"])
223
+ ])) : createCommentVNode("", true),
224
+ createElementVNode("button", {
225
+ class: normalizeClass(["mld-experiment-selector__filters-toggle", { "mld-experiment-selector__filters-toggle--active": hasActiveAdvancedFilters.value }]),
226
+ type: "button",
227
+ onClick: _cache[3] || (_cache[3] = ($event) => showAdvanced.value = !showAdvanced.value)
228
+ }, [
229
+ _cache[8] || (_cache[8] = createElementVNode("svg", {
230
+ width: "14",
231
+ height: "14",
232
+ viewBox: "0 0 24 24",
233
+ fill: "none",
234
+ stroke: "currentColor",
235
+ "stroke-width": "2",
236
+ "stroke-linecap": "round",
237
+ "stroke-linejoin": "round"
238
+ }, [
239
+ createElementVNode("line", {
240
+ x1: "4",
241
+ y1: "6",
242
+ x2: "20",
243
+ y2: "6"
244
+ }),
245
+ createElementVNode("line", {
246
+ x1: "8",
247
+ y1: "12",
248
+ x2: "20",
249
+ y2: "12"
250
+ }),
251
+ createElementVNode("line", {
252
+ x1: "12",
253
+ y1: "18",
254
+ x2: "20",
255
+ y2: "18"
256
+ }),
257
+ createElementVNode("circle", {
258
+ cx: "6",
259
+ cy: "12",
260
+ r: "2"
261
+ }),
262
+ createElementVNode("circle", {
263
+ cx: "10",
264
+ cy: "18",
265
+ r: "2"
266
+ }),
267
+ createElementVNode("circle", {
268
+ cx: "6",
269
+ cy: "6",
270
+ r: "2"
271
+ })
272
+ ], -1)),
273
+ _cache[9] || (_cache[9] = createTextVNode(" Filters ", -1)),
274
+ hasActiveAdvancedFilters.value ? (openBlock(), createElementBlock("span", _hoisted_5)) : createCommentVNode("", true)
275
+ ], 2)
136
276
  ]),
137
- unref(isLoading) ? (openBlock(), createElementBlock("div", _hoisted_4, [
277
+ showAdvanced.value ? (openBlock(), createElementBlock("div", _hoisted_6, [
278
+ projectFilterOptions.value.length > 1 ? (openBlock(), createElementBlock("div", _hoisted_7, [
279
+ createVNode(_sfc_main$3, {
280
+ "model-value": unref(filters).project ?? "",
281
+ options: projectFilterOptions.value,
282
+ size: "sm",
283
+ "onUpdate:modelValue": _cache[4] || (_cache[4] = (v) => setFilter("project", v))
284
+ }, null, 8, ["model-value", "options"])
285
+ ])) : createCommentVNode("", true),
286
+ createElementVNode("div", _hoisted_8, [
287
+ createVNode(_sfc_main$3, {
288
+ "model-value": unref(filters).datePreset ?? "",
289
+ options: unref(DATE_PRESET_OPTIONS),
290
+ size: "sm",
291
+ "onUpdate:modelValue": _cache[5] || (_cache[5] = (v) => setFilter("datePreset", v))
292
+ }, null, 8, ["model-value", "options"])
293
+ ]),
294
+ createElementVNode("div", _hoisted_9, [
295
+ createVNode(_sfc_main$3, {
296
+ "model-value": unref(sortKey),
297
+ options: unref(SORT_OPTIONS),
298
+ size: "sm",
299
+ "onUpdate:modelValue": handleSortChange
300
+ }, null, 8, ["model-value", "options"])
301
+ ]),
302
+ createElementVNode("label", _hoisted_10, [
303
+ withDirectives(createElementVNode("input", {
304
+ "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => groupToggle.value = $event),
305
+ type: "checkbox",
306
+ class: "mld-experiment-selector__group-checkbox"
307
+ }, null, 512), [
308
+ [vModelCheckbox, groupToggle.value]
309
+ ]),
310
+ _cache[10] || (_cache[10] = createTextVNode(" Group by project ", -1))
311
+ ])
312
+ ])) : createCommentVNode("", true),
313
+ unref(isLoading) ? (openBlock(), createElementBlock("div", _hoisted_11, [
138
314
  (openBlock(), createElementBlock(Fragment, null, renderList(4, (n) => {
139
315
  return createElementVNode("div", {
140
316
  key: n,
141
317
  class: "mld-experiment-selector__skeleton-row"
142
318
  }, [
143
- createElementVNode("div", _hoisted_5, [
319
+ createElementVNode("div", _hoisted_12, [
144
320
  createVNode(_sfc_main$4, {
145
321
  width: 120 + n * 20,
146
322
  height: "14px"
@@ -157,13 +333,79 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
157
333
  })
158
334
  ]);
159
335
  }), 64))
160
- ])) : unref(error) ? (openBlock(), createElementBlock("div", _hoisted_6, toDisplayString(unref(error)), 1)) : unref(experiments).length === 0 ? (openBlock(), createBlock(_sfc_main$5, {
161
- key: 2,
336
+ ])) : unref(error) ? (openBlock(), createElementBlock("div", _hoisted_13, toDisplayString(unref(error)), 1)) : unref(experiments).length === 0 ? (openBlock(), createBlock(_sfc_main$5, {
337
+ key: 3,
162
338
  title: "No experiments found",
163
339
  description: "Try adjusting your search or filters.",
164
340
  size: "sm"
165
- })) : (openBlock(), createElementBlock("div", {
166
- key: 3,
341
+ })) : groupToggle.value ? (openBlock(), createElementBlock("div", {
342
+ key: 4,
343
+ ref_key: "listRef",
344
+ ref: listRef,
345
+ class: "mld-experiment-selector__list"
346
+ }, [
347
+ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(groupedByProject), ([groupName, groupExps]) => {
348
+ return openBlock(), createElementBlock(Fragment, { key: groupName }, [
349
+ createElementVNode("button", {
350
+ type: "button",
351
+ class: "mld-experiment-selector__group-header",
352
+ onClick: ($event) => toggleGroup(groupName)
353
+ }, [
354
+ (openBlock(), createElementBlock("svg", {
355
+ class: normalizeClass(["mld-experiment-selector__group-chevron", { "mld-experiment-selector__group-chevron--collapsed": collapsedGroups.has(groupName) }]),
356
+ width: "14",
357
+ height: "14",
358
+ viewBox: "0 0 24 24",
359
+ fill: "none",
360
+ stroke: "currentColor",
361
+ "stroke-width": "2",
362
+ "stroke-linecap": "round",
363
+ "stroke-linejoin": "round"
364
+ }, [..._cache[11] || (_cache[11] = [
365
+ createElementVNode("polyline", { points: "6 9 12 15 18 9" }, null, -1)
366
+ ])], 2)),
367
+ createElementVNode("span", _hoisted_15, toDisplayString(groupName), 1),
368
+ createElementVNode("span", _hoisted_16, toDisplayString(groupExps.length), 1)
369
+ ], 8, _hoisted_14),
370
+ !collapsedGroups.has(groupName) ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(groupExps, (exp) => {
371
+ return openBlock(), createElementBlock("div", {
372
+ key: exp.id,
373
+ class: normalizeClass(["mld-experiment-selector__row", {
374
+ "mld-experiment-selector__row--active": exp.id === __props.currentExperimentId,
375
+ "mld-experiment-selector__row--focused": getFlatIndex(exp) === activeIndex.value
376
+ }]),
377
+ onClick: ($event) => handleSelect(exp),
378
+ onMouseenter: ($event) => activeIndex.value = getFlatIndex(exp)
379
+ }, [
380
+ createElementVNode("div", _hoisted_18, [
381
+ createElementVNode("div", _hoisted_19, [
382
+ createTextVNode(toDisplayString(exp.name) + " ", 1),
383
+ exp.experiment_code ? (openBlock(), createBlock(_sfc_main$6, {
384
+ key: 0,
385
+ code: exp.experiment_code,
386
+ size: "sm",
387
+ copyable: false
388
+ }, null, 8, ["code"])) : createCommentVNode("", true)
389
+ ]),
390
+ createElementVNode("div", _hoisted_20, [
391
+ createElementVNode("span", null, toDisplayString(unref(formatExperimentDate)(exp.created_at)), 1)
392
+ ])
393
+ ]),
394
+ createVNode(_sfc_main$7, {
395
+ variant: unref(EXPERIMENT_STATUS_VARIANT_MAP)[exp.status],
396
+ size: "sm"
397
+ }, {
398
+ default: withCtx(() => [
399
+ createTextVNode(toDisplayString(exp.status), 1)
400
+ ]),
401
+ _: 2
402
+ }, 1032, ["variant"])
403
+ ], 42, _hoisted_17);
404
+ }), 128)) : createCommentVNode("", true)
405
+ ], 64);
406
+ }), 128))
407
+ ], 512)) : (openBlock(), createElementBlock("div", {
408
+ key: 5,
167
409
  ref_key: "listRef",
168
410
  ref: listRef,
169
411
  class: "mld-experiment-selector__list"
@@ -178,8 +420,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
178
420
  onClick: ($event) => handleSelect(exp),
179
421
  onMouseenter: ($event) => activeIndex.value = idx
180
422
  }, [
181
- createElementVNode("div", _hoisted_8, [
182
- createElementVNode("div", _hoisted_9, [
423
+ createElementVNode("div", _hoisted_22, [
424
+ createElementVNode("div", _hoisted_23, [
183
425
  createTextVNode(toDisplayString(exp.name) + " ", 1),
184
426
  exp.experiment_code ? (openBlock(), createBlock(_sfc_main$6, {
185
427
  key: 0,
@@ -188,8 +430,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
188
430
  copyable: false
189
431
  }, null, 8, ["code"])) : createCommentVNode("", true)
190
432
  ]),
191
- createElementVNode("div", _hoisted_10, [
192
- exp.project ? (openBlock(), createElementBlock("span", _hoisted_11, toDisplayString(exp.project), 1)) : createCommentVNode("", true),
433
+ createElementVNode("div", _hoisted_24, [
434
+ exp.project_name || exp.project ? (openBlock(), createElementBlock("span", _hoisted_25, toDisplayString(exp.project_name || exp.project), 1)) : createCommentVNode("", true),
193
435
  createElementVNode("span", null, toDisplayString(unref(formatExperimentDate)(exp.created_at)), 1)
194
436
  ])
195
437
  ]),
@@ -202,9 +444,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
202
444
  ]),
203
445
  _: 2
204
446
  }, 1032, ["variant"])
205
- ], 42, _hoisted_7);
447
+ ], 42, _hoisted_21);
206
448
  }), 128))
207
- ], 512))
449
+ ], 512)),
450
+ __props.currentExperimentId != null ? (openBlock(), createElementBlock("div", _hoisted_26, [
451
+ createElementVNode("button", {
452
+ type: "button",
453
+ class: "mld-experiment-selector__clear-btn",
454
+ onClick: handleDeselect
455
+ }, " Clear selection ")
456
+ ])) : createCommentVNode("", true)
208
457
  ], 32)
209
458
  ]),
210
459
  _: 1
@@ -1 +1 @@
1
- {"version":3,"file":"ExperimentSelectorModal.vue.js","sources":["../../src/components/ExperimentSelectorModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, nextTick } from 'vue'\nimport type { ModalSize, ExperimentSummary, ExperimentStatus } from '../types'\nimport { useExperimentSelector } from '../composables/useExperimentSelector'\nimport {\n formatExperimentDate,\n EXPERIMENT_STATUS_OPTIONS,\n EXPERIMENT_STATUS_VARIANT_MAP,\n} from '../composables/experiment-utils'\nimport BaseModal from './BaseModal.vue'\nimport BaseInput from './BaseInput.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport BasePill from './BasePill.vue'\nimport Skeleton from './Skeleton.vue'\nimport EmptyState from './EmptyState.vue'\nimport ExperimentCodeBadge from './ExperimentCodeBadge.vue'\n\ninterface Props {\n modelValue: boolean\n experimentType?: string\n currentExperimentId?: number | null\n title?: string\n size?: ModalSize\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n currentExperimentId: null,\n title: 'Select Experiment',\n size: 'full',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n select: [experiment: ExperimentSummary]\n}>()\n\nconst {\n experiments,\n filters,\n isLoading,\n error,\n fetch: fetchExperiments,\n} = useExperimentSelector({\n experimentType: props.experimentType,\n})\n\nconst activeIndex = ref(-1)\nconst listRef = ref<HTMLElement | null>(null)\n\nfunction handleStatusChange(value: string | number) {\n filters.status = (String(value) || null) as ExperimentStatus | null\n}\n\nfunction handleSelect(experiment: ExperimentSummary) {\n emit('select', experiment)\n emit('update:modelValue', false)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!experiments.value.length) return\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n activeIndex.value = Math.min(activeIndex.value + 1, experiments.value.length - 1)\n scrollActiveIntoView()\n break\n case 'ArrowUp':\n event.preventDefault()\n activeIndex.value = Math.max(activeIndex.value - 1, 0)\n scrollActiveIntoView()\n break\n case 'Enter':\n event.preventDefault()\n if (activeIndex.value >= 0 && activeIndex.value < experiments.value.length) {\n handleSelect(experiments.value[activeIndex.value])\n }\n break\n }\n}\n\nfunction scrollActiveIntoView() {\n nextTick(() => {\n const row = listRef.value?.querySelector('.mld-experiment-selector__row--focused')\n row?.scrollIntoView({ block: 'nearest' })\n })\n}\n\n// Reset active index when experiments change\nwatch(experiments, () => { activeIndex.value = -1 })\n\n// Fetch on open\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (isOpen) {\n activeIndex.value = -1\n fetchExperiments()\n }\n },\n)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :size=\"size\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mld-experiment-selector\" @keydown=\"handleKeydown\">\n <!-- Filter bar -->\n <div class=\"mld-experiment-selector__filters\">\n <div class=\"mld-experiment-selector__search\">\n <BaseInput\n v-model=\"filters.search\"\n placeholder=\"Search experiments...\"\n size=\"sm\"\n type=\"search\"\n />\n </div>\n <div class=\"mld-experiment-selector__status-filter\">\n <BaseSelect\n :model-value=\"filters.status ?? ''\"\n :options=\"EXPERIMENT_STATUS_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"handleStatusChange\"\n />\n </div>\n </div>\n\n <!-- Loading skeleton -->\n <div v-if=\"isLoading\" class=\"mld-experiment-selector__skeleton\">\n <div v-for=\"n in 4\" :key=\"n\" class=\"mld-experiment-selector__skeleton-row\">\n <div class=\"mld-experiment-selector__skeleton-content\">\n <Skeleton :width=\"120 + n * 20\" height=\"14px\" />\n <Skeleton width=\"80px\" height=\"10px\" />\n </div>\n <Skeleton width=\"60px\" height=\"20px\" variant=\"rounded\" />\n </div>\n </div>\n\n <!-- Error -->\n <div v-else-if=\"error\" class=\"mld-experiment-selector__error\">\n {{ error }}\n </div>\n\n <!-- Empty -->\n <EmptyState\n v-else-if=\"experiments.length === 0\"\n title=\"No experiments found\"\n description=\"Try adjusting your search or filters.\"\n size=\"sm\"\n />\n\n <!-- Experiment list -->\n <div v-else ref=\"listRef\" class=\"mld-experiment-selector__list\">\n <div\n v-for=\"(exp, idx) in experiments\"\n :key=\"exp.id\"\n class=\"mld-experiment-selector__row\"\n :class=\"{\n 'mld-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mld-experiment-selector__row--focused': idx === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <div class=\"mld-experiment-selector__row-content\">\n <div class=\"mld-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mld-experiment-selector__meta\">\n <span v-if=\"exp.project\">{{ exp.project }}</span>\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"EXPERIMENT_STATUS_VARIANT_MAP[exp.status]\" size=\"sm\">\n {{ exp.status }}\n </BasePill>\n </div>\n </div>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/experiment-selector-modal.css';\n</style>\n"],"names":["_createBlock","BaseModal","_createElementVNode","_createVNode","BaseInput","_unref","BaseSelect","_openBlock","_createElementBlock","_Fragment","_renderList","Skeleton","_toDisplayString","EmptyState","ExperimentCodeBadge","BasePill","_createTextVNode"],"mappings":"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"}
1
+ {"version":3,"file":"ExperimentSelectorModal.vue.js","sources":["../../src/components/ExperimentSelectorModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, reactive, computed, watch, nextTick } from 'vue'\nimport type { ModalSize, ExperimentSummary, ExperimentFilters } from '../types'\nimport { useExperimentSelector } from '../composables/useExperimentSelector'\nimport {\n formatExperimentDate,\n EXPERIMENT_STATUS_OPTIONS,\n EXPERIMENT_STATUS_VARIANT_MAP,\n DATE_PRESET_OPTIONS,\n SORT_OPTIONS,\n} from '../composables/experiment-utils'\nimport BaseModal from './BaseModal.vue'\nimport BaseInput from './BaseInput.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport BasePill from './BasePill.vue'\nimport Skeleton from './Skeleton.vue'\nimport EmptyState from './EmptyState.vue'\nimport ExperimentCodeBadge from './ExperimentCodeBadge.vue'\n\ninterface Props {\n modelValue: boolean\n experimentType?: string\n currentExperimentId?: number | null\n title?: string\n size?: ModalSize\n groupByProject?: boolean\n showFilters?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n currentExperimentId: null,\n title: 'Select Experiment',\n size: 'full',\n groupByProject: false,\n showFilters: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n select: [experiment: ExperimentSummary]\n deselect: []\n}>()\n\nconst {\n experiments,\n filters,\n isLoading,\n error,\n sortKey,\n experimentTypes,\n projects,\n groupedByProject,\n fetch: fetchExperiments,\n fetchFilterOptions,\n} = useExperimentSelector({\n experimentType: props.experimentType,\n})\n\nconst activeIndex = ref(-1)\nconst listRef = ref<HTMLElement | null>(null)\nconst showAdvanced = ref(props.showFilters)\nconst groupToggle = ref(props.groupByProject)\n\n// Track whether any advanced filter is active (for badge dot)\nconst hasActiveAdvancedFilters = computed(() =>\n !!(filters.project || filters.experimentType || filters.datePreset || sortKey.value !== 'created_at:desc'),\n)\n\n// Build type filter options from fetched experiment types\nconst typeFilterOptions = computed(() => [\n { value: '', label: 'All types' },\n ...experimentTypes.value.map(t => ({ value: t.value, label: t.label })),\n])\n\n// Build project filter options from fetched projects\nconst projectFilterOptions = computed(() => [\n { value: '', label: 'All projects' },\n ...projects.value,\n])\n\n// Flat list of experiments for keyboard navigation (works across groups too)\nconst flatExperiments = computed(() => {\n if (!groupToggle.value) return experiments.value\n return groupedByProject.value.flatMap(([, exps]) => exps)\n})\n\nfunction setFilter<K extends keyof ExperimentFilters>(key: K, value: string | number) {\n ;(filters as Record<string, unknown>)[key] = String(value) || null\n}\n\nfunction handleSortChange(value: string | number) {\n sortKey.value = String(value) || 'created_at:desc'\n}\n\nfunction handleSelect(experiment: ExperimentSummary) {\n emit('select', experiment)\n emit('update:modelValue', false)\n}\n\nfunction handleDeselect() {\n emit('deselect')\n emit('update:modelValue', false)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n const list = flatExperiments.value\n if (!list.length) return\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n activeIndex.value = Math.min(activeIndex.value + 1, list.length - 1)\n scrollActiveIntoView()\n break\n case 'ArrowUp':\n event.preventDefault()\n activeIndex.value = Math.max(activeIndex.value - 1, 0)\n scrollActiveIntoView()\n break\n case 'Enter':\n event.preventDefault()\n if (activeIndex.value >= 0 && activeIndex.value < list.length) {\n handleSelect(list[activeIndex.value])\n }\n break\n }\n}\n\nfunction scrollActiveIntoView() {\n nextTick(() => {\n const row = listRef.value?.querySelector('.mld-experiment-selector__row--focused')\n row?.scrollIntoView({ block: 'nearest' })\n })\n}\n\n// Precomputed id → flat index for O(1) lookup in grouped mode\nconst flatIndexMap = computed(() => {\n const map = new Map<number, number>()\n flatExperiments.value.forEach((exp, i) => map.set(exp.id, i))\n return map\n})\n\nfunction getFlatIndex(experiment: ExperimentSummary): number {\n return flatIndexMap.value.get(experiment.id) ?? -1\n}\n\n// Track collapsed groups (reactive Set tracks .add/.delete/.has automatically)\nconst collapsedGroups = reactive(new Set<string>())\n\nfunction toggleGroup(groupName: string) {\n if (collapsedGroups.has(groupName)) {\n collapsedGroups.delete(groupName)\n } else {\n collapsedGroups.add(groupName)\n }\n}\n\n// Reset active index when experiments change\nwatch(experiments, () => { activeIndex.value = -1 })\n\n// Fetch on open\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (isOpen) {\n activeIndex.value = -1\n collapsedGroups.clear()\n fetchFilterOptions()\n fetchExperiments()\n }\n },\n)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :size=\"size\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mld-experiment-selector\" @keydown=\"handleKeydown\">\n <!-- Filter bar row 1 -->\n <div class=\"mld-experiment-selector__filters-row\">\n <div class=\"mld-experiment-selector__search\">\n <BaseInput\n v-model=\"filters.search\"\n placeholder=\"Search experiments...\"\n size=\"sm\"\n type=\"search\"\n />\n </div>\n <div class=\"mld-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.status ?? ''\"\n :options=\"EXPERIMENT_STATUS_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('status', v)\"\n />\n </div>\n <div v-if=\"typeFilterOptions.length > 1\" class=\"mld-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.experimentType ?? ''\"\n :options=\"typeFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('experimentType', v)\"\n />\n </div>\n <button\n class=\"mld-experiment-selector__filters-toggle\"\n :class=\"{ 'mld-experiment-selector__filters-toggle--active': hasActiveAdvancedFilters }\"\n type=\"button\"\n @click=\"showAdvanced = !showAdvanced\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\" /><line x1=\"8\" y1=\"12\" x2=\"20\" y2=\"12\" /><line x1=\"12\" y1=\"18\" x2=\"20\" y2=\"18\" />\n <circle cx=\"6\" cy=\"12\" r=\"2\" /><circle cx=\"10\" cy=\"18\" r=\"2\" /><circle cx=\"6\" cy=\"6\" r=\"2\" />\n </svg>\n Filters\n <span v-if=\"hasActiveAdvancedFilters\" class=\"mld-experiment-selector__filters-dot\" />\n </button>\n </div>\n\n <!-- Filter bar row 2 (advanced, collapsible) -->\n <div v-if=\"showAdvanced\" class=\"mld-experiment-selector__filters-advanced\">\n <div v-if=\"projectFilterOptions.length > 1\" class=\"mld-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.project ?? ''\"\n :options=\"projectFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('project', v)\"\n />\n </div>\n <div class=\"mld-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.datePreset ?? ''\"\n :options=\"DATE_PRESET_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('datePreset', v)\"\n />\n </div>\n <div class=\"mld-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"sortKey\"\n :options=\"SORT_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"handleSortChange\"\n />\n </div>\n <label class=\"mld-experiment-selector__group-toggle\">\n <input\n v-model=\"groupToggle\"\n type=\"checkbox\"\n class=\"mld-experiment-selector__group-checkbox\"\n />\n Group by project\n </label>\n </div>\n\n <!-- Loading skeleton -->\n <div v-if=\"isLoading\" class=\"mld-experiment-selector__skeleton\">\n <div v-for=\"n in 4\" :key=\"n\" class=\"mld-experiment-selector__skeleton-row\">\n <div class=\"mld-experiment-selector__skeleton-content\">\n <Skeleton :width=\"120 + n * 20\" height=\"14px\" />\n <Skeleton width=\"80px\" height=\"10px\" />\n </div>\n <Skeleton width=\"60px\" height=\"20px\" variant=\"rounded\" />\n </div>\n </div>\n\n <!-- Error -->\n <div v-else-if=\"error\" class=\"mld-experiment-selector__error\">\n {{ error }}\n </div>\n\n <!-- Empty -->\n <EmptyState\n v-else-if=\"experiments.length === 0\"\n title=\"No experiments found\"\n description=\"Try adjusting your search or filters.\"\n size=\"sm\"\n />\n\n <!-- Experiment list: grouped mode -->\n <div v-else-if=\"groupToggle\" ref=\"listRef\" class=\"mld-experiment-selector__list\">\n <template v-for=\"([groupName, groupExps]) in groupedByProject\" :key=\"groupName\">\n <button\n type=\"button\"\n class=\"mld-experiment-selector__group-header\"\n @click=\"toggleGroup(groupName)\"\n >\n <svg\n class=\"mld-experiment-selector__group-chevron\"\n :class=\"{ 'mld-experiment-selector__group-chevron--collapsed': collapsedGroups.has(groupName) }\"\n width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n <span class=\"mld-experiment-selector__group-name\">{{ groupName }}</span>\n <span class=\"mld-experiment-selector__group-count\">{{ groupExps.length }}</span>\n </button>\n <template v-if=\"!collapsedGroups.has(groupName)\">\n <div\n v-for=\"exp in groupExps\"\n :key=\"exp.id\"\n class=\"mld-experiment-selector__row\"\n :class=\"{\n 'mld-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mld-experiment-selector__row--focused': getFlatIndex(exp) === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = getFlatIndex(exp)\"\n >\n <div class=\"mld-experiment-selector__row-content\">\n <div class=\"mld-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mld-experiment-selector__meta\">\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"EXPERIMENT_STATUS_VARIANT_MAP[exp.status]\" size=\"sm\">\n {{ exp.status }}\n </BasePill>\n </div>\n </template>\n </template>\n </div>\n\n <!-- Experiment list: flat mode -->\n <div v-else ref=\"listRef\" class=\"mld-experiment-selector__list\">\n <div\n v-for=\"(exp, idx) in experiments\"\n :key=\"exp.id\"\n class=\"mld-experiment-selector__row\"\n :class=\"{\n 'mld-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mld-experiment-selector__row--focused': idx === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <div class=\"mld-experiment-selector__row-content\">\n <div class=\"mld-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mld-experiment-selector__meta\">\n <span v-if=\"exp.project_name || exp.project\">{{ exp.project_name || exp.project }}</span>\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"EXPERIMENT_STATUS_VARIANT_MAP[exp.status]\" size=\"sm\">\n {{ exp.status }}\n </BasePill>\n </div>\n </div>\n\n <!-- Footer: clear selection -->\n <div v-if=\"currentExperimentId != null\" class=\"mld-experiment-selector__footer\">\n <button\n type=\"button\"\n class=\"mld-experiment-selector__clear-btn\"\n @click=\"handleDeselect\"\n >\n Clear selection\n </button>\n </div>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/experiment-selector-modal.css';\n</style>\n"],"names":["_createBlock","BaseModal","_createElementVNode","_createVNode","BaseInput","_unref","BaseSelect","_openBlock","_createElementBlock","_normalizeClass","_Fragment","_renderList","Skeleton","_toDisplayString","EmptyState","ExperimentCodeBadge","BasePill","_createTextVNode"],"mappings":"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"}
@@ -35,9 +35,9 @@ declare const __VLS_component: import('vue').DefineComponent<Props, {}, {}, {},
35
35
  onBook?: (() => any) | undefined;
36
36
  }>, {
37
37
  compact: boolean;
38
+ status: ResourceStatus;
38
39
  size: "sm" | "md" | "lg";
39
40
  tags: string[];
40
- status: ResourceStatus;
41
41
  specs: ResourceSpec[];
42
42
  showBookAction: boolean;
43
43
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
@@ -6,8 +6,8 @@ interface Props {
6
6
  }
7
7
  declare const _default: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{}>, {
8
8
  label: string;
9
+ status: "success" | "warning" | "error" | "info" | "muted";
9
10
  color: string;
10
11
  pulse: boolean;
11
- status: "success" | "warning" | "error" | "info" | "muted";
12
12
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLSpanElement>;
13
13
  export default _default;
@@ -1,5 +1,8 @@
1
- import { ExperimentStatus, SelectOption, PillVariant } from '../types';
1
+ import { DatePreset, ExperimentStatus, SelectOption, PillVariant } from '../types';
2
2
  export declare function formatExperimentDate(dateStr: string): string;
3
+ export declare function datePresetToISO(preset: DatePreset): string;
3
4
  export declare const EXPERIMENT_STATUS_OPTIONS: SelectOption<string>[];
4
5
  export declare const EXPERIMENT_STATUS_VARIANT_MAP: Record<ExperimentStatus, PillVariant>;
5
6
  export declare const EXPERIMENT_STATUS_LABELS: Record<ExperimentStatus, string>;
7
+ export declare const DATE_PRESET_OPTIONS: SelectOption<string>[];
8
+ export declare const SORT_OPTIONS: SelectOption<string>[];
@@ -9,6 +9,12 @@ function formatExperimentDate(dateStr) {
9
9
  return dateStr;
10
10
  }
11
11
  }
12
+ function datePresetToISO(preset) {
13
+ const now = /* @__PURE__ */ new Date();
14
+ const days = preset === "last_7_days" ? 7 : preset === "last_30_days" ? 30 : 90;
15
+ const d = new Date(now.getTime() - days * 864e5);
16
+ return d.toISOString();
17
+ }
12
18
  const EXPERIMENT_STATUS_OPTIONS = [
13
19
  { value: "", label: "All statuses" },
14
20
  { value: "planned", label: "Planned" },
@@ -25,10 +31,26 @@ const EXPERIMENT_STATUS_LABELS = {
25
31
  ongoing: "Ongoing",
26
32
  completed: "Completed"
27
33
  };
34
+ const DATE_PRESET_OPTIONS = [
35
+ { value: "", label: "Any time" },
36
+ { value: "last_7_days", label: "Last 7 days" },
37
+ { value: "last_30_days", label: "Last 30 days" },
38
+ { value: "last_90_days", label: "Last 90 days" }
39
+ ];
40
+ const SORT_OPTIONS = [
41
+ { value: "created_at:desc", label: "Newest first" },
42
+ { value: "created_at:asc", label: "Oldest first" },
43
+ { value: "updated_at:desc", label: "Recently updated" },
44
+ { value: "name:asc", label: "Name A–Z" },
45
+ { value: "name:desc", label: "Name Z–A" }
46
+ ];
28
47
  export {
48
+ DATE_PRESET_OPTIONS,
29
49
  EXPERIMENT_STATUS_LABELS,
30
50
  EXPERIMENT_STATUS_OPTIONS,
31
51
  EXPERIMENT_STATUS_VARIANT_MAP,
52
+ SORT_OPTIONS,
53
+ datePresetToISO,
32
54
  formatExperimentDate
33
55
  };
34
56
  //# sourceMappingURL=experiment-utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"experiment-utils.js","sources":["../../src/composables/experiment-utils.ts"],"sourcesContent":["import type { ExperimentStatus, SelectOption, PillVariant } from '../types'\n\nexport function formatExperimentDate(dateStr: string): string {\n try {\n return new Date(dateStr).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n } catch {\n return dateStr\n }\n}\n\nexport const EXPERIMENT_STATUS_OPTIONS: SelectOption<string>[] = [\n { value: '', label: 'All statuses' },\n { value: 'planned', label: 'Planned' },\n { value: 'ongoing', label: 'Ongoing' },\n { value: 'completed', label: 'Completed' },\n]\n\nexport const EXPERIMENT_STATUS_VARIANT_MAP: Record<ExperimentStatus, PillVariant> = {\n planned: 'default',\n ongoing: 'primary',\n completed: 'success',\n}\n\nexport const EXPERIMENT_STATUS_LABELS: Record<ExperimentStatus, string> = {\n planned: 'Planned',\n ongoing: 'Ongoing',\n completed: 'Completed',\n}\n"],"names":[],"mappings":"AAEO,SAAS,qBAAqB,SAAyB;AAC5D,MAAI;AACF,WAAO,IAAI,KAAK,OAAO,EAAE,mBAAmB,QAAW;AAAA,MACrD,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAK;AAAA,IAAA,CACN;AAAA,EACH,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,MAAM,4BAAoD;AAAA,EAC/D,EAAE,OAAO,IAAI,OAAO,eAAA;AAAA,EACpB,EAAE,OAAO,WAAW,OAAO,UAAA;AAAA,EAC3B,EAAE,OAAO,WAAW,OAAO,UAAA;AAAA,EAC3B,EAAE,OAAO,aAAa,OAAO,YAAA;AAC/B;AAEO,MAAM,gCAAuE;AAAA,EAClF,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AACb;AAEO,MAAM,2BAA6D;AAAA,EACxE,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AACb;"}
1
+ {"version":3,"file":"experiment-utils.js","sources":["../../src/composables/experiment-utils.ts"],"sourcesContent":["import type { DatePreset, ExperimentStatus, SelectOption, PillVariant } from '../types'\n\nexport function formatExperimentDate(dateStr: string): string {\n try {\n return new Date(dateStr).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n } catch {\n return dateStr\n }\n}\n\nexport function datePresetToISO(preset: DatePreset): string {\n const now = new Date()\n const days = preset === 'last_7_days' ? 7 : preset === 'last_30_days' ? 30 : 90\n const d = new Date(now.getTime() - days * 86_400_000)\n return d.toISOString()\n}\n\nexport const EXPERIMENT_STATUS_OPTIONS: SelectOption<string>[] = [\n { value: '', label: 'All statuses' },\n { value: 'planned', label: 'Planned' },\n { value: 'ongoing', label: 'Ongoing' },\n { value: 'completed', label: 'Completed' },\n]\n\nexport const EXPERIMENT_STATUS_VARIANT_MAP: Record<ExperimentStatus, PillVariant> = {\n planned: 'default',\n ongoing: 'primary',\n completed: 'success',\n}\n\nexport const EXPERIMENT_STATUS_LABELS: Record<ExperimentStatus, string> = {\n planned: 'Planned',\n ongoing: 'Ongoing',\n completed: 'Completed',\n}\n\nexport const DATE_PRESET_OPTIONS: SelectOption<string>[] = [\n { value: '', label: 'Any time' },\n { value: 'last_7_days', label: 'Last 7 days' },\n { value: 'last_30_days', label: 'Last 30 days' },\n { value: 'last_90_days', label: 'Last 90 days' },\n]\n\nexport const SORT_OPTIONS: SelectOption<string>[] = [\n { value: 'created_at:desc', label: 'Newest first' },\n { value: 'created_at:asc', label: 'Oldest first' },\n { value: 'updated_at:desc', label: 'Recently updated' },\n { value: 'name:asc', label: 'Name A\\u2013Z' },\n { value: 'name:desc', label: 'Name Z\\u2013A' },\n]\n"],"names":[],"mappings":"AAEO,SAAS,qBAAqB,SAAyB;AAC5D,MAAI;AACF,WAAO,IAAI,KAAK,OAAO,EAAE,mBAAmB,QAAW;AAAA,MACrD,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAK;AAAA,IAAA,CACN;AAAA,EACH,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,gBAAgB,QAA4B;AAC1D,QAAM,0BAAU,KAAA;AAChB,QAAM,OAAO,WAAW,gBAAgB,IAAI,WAAW,iBAAiB,KAAK;AAC7E,QAAM,IAAI,IAAI,KAAK,IAAI,QAAA,IAAY,OAAO,KAAU;AACpD,SAAO,EAAE,YAAA;AACX;AAEO,MAAM,4BAAoD;AAAA,EAC/D,EAAE,OAAO,IAAI,OAAO,eAAA;AAAA,EACpB,EAAE,OAAO,WAAW,OAAO,UAAA;AAAA,EAC3B,EAAE,OAAO,WAAW,OAAO,UAAA;AAAA,EAC3B,EAAE,OAAO,aAAa,OAAO,YAAA;AAC/B;AAEO,MAAM,gCAAuE;AAAA,EAClF,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AACb;AAEO,MAAM,2BAA6D;AAAA,EACxE,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AACb;AAEO,MAAM,sBAA8C;AAAA,EACzD,EAAE,OAAO,IAAI,OAAO,WAAA;AAAA,EACpB,EAAE,OAAO,eAAe,OAAO,cAAA;AAAA,EAC/B,EAAE,OAAO,gBAAgB,OAAO,eAAA;AAAA,EAChC,EAAE,OAAO,gBAAgB,OAAO,eAAA;AAClC;AAEO,MAAM,eAAuC;AAAA,EAClD,EAAE,OAAO,mBAAmB,OAAO,eAAA;AAAA,EACnC,EAAE,OAAO,kBAAkB,OAAO,eAAA;AAAA,EAClC,EAAE,OAAO,mBAAmB,OAAO,mBAAA;AAAA,EACnC,EAAE,OAAO,YAAY,OAAO,WAAA;AAAA,EAC5B,EAAE,OAAO,aAAa,OAAO,WAAA;AAC/B;"}
@@ -19,6 +19,6 @@ export { useFormBuilder, evaluateCondition } from './useFormBuilder';
19
19
  export { useAutoGroup, DEFAULT_COLORS } from './useAutoGroup';
20
20
  export { usePluginConfig, type UsePluginConfigReturn } from './usePluginConfig';
21
21
  export { useExperimentSelector, type UseExperimentSelectorOptions, type UseExperimentSelectorReturn, } from './useExperimentSelector';
22
- export { formatExperimentDate, EXPERIMENT_STATUS_OPTIONS, EXPERIMENT_STATUS_VARIANT_MAP, EXPERIMENT_STATUS_LABELS, } from './experiment-utils';
22
+ export { formatExperimentDate, datePresetToISO, EXPERIMENT_STATUS_OPTIONS, EXPERIMENT_STATUS_VARIANT_MAP, EXPERIMENT_STATUS_LABELS, DATE_PRESET_OPTIONS, SORT_OPTIONS, } from './experiment-utils';
23
23
  export { useExperimentData, type UseExperimentDataOptions, type UseExperimentDataReturn, } from './useExperimentData';
24
24
  export { getFieldRegistryEntry, getTypeDefault, type RegistryEntry, } from './formBuilderRegistry';