@morscherlab/mint-sdk 1.0.0-beta.4 → 1.0.0-beta.6
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/dist/components/AppSidebar.vue.d.ts +1 -1
- package/dist/components/index.js +2 -2
- package/dist/{components-BkGF4B4y.js → components-DihbSJjU.js} +2524 -2517
- package/dist/components-DihbSJjU.js.map +1 -0
- package/dist/composables/index.js +3 -3
- package/dist/{composables-CHsME9H1.js → composables-BcgZ6diz.js} +2 -2
- package/dist/{composables-CHsME9H1.js.map → composables-BcgZ6diz.js.map} +1 -1
- package/dist/index.js +5 -5
- package/dist/install.js +2 -2
- package/dist/styles.css +738 -738
- package/dist/templates/adapters.d.ts +7 -1
- package/dist/templates/catalog.d.ts +5 -5
- package/dist/templates/index.d.ts +2 -2
- package/dist/templates/index.js +2 -2
- package/dist/templates/presets.d.ts +4 -4
- package/dist/templates/types.d.ts +4 -1
- package/dist/{templates-B5jmTWuk.js → templates-Cyt0Suwf.js} +213 -19
- package/dist/{templates-B5jmTWuk.js.map → templates-Cyt0Suwf.js.map} +1 -1
- package/dist/{useScheduleDrag-BgzpQT53.js → useExperimentData-CM6Y0u5L.js} +183 -183
- package/dist/useExperimentData-CM6Y0u5L.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/components/AppSidebar.test.ts +4 -2
- package/src/__tests__/components/AppTopBar.test.ts +9 -3
- package/src/__tests__/components/{AppPageSelector.test.ts → AppTopBarPageSelector.test.ts} +8 -8
- package/src/__tests__/components/{AppPillNav.test.ts → AppTopBarPillNav.test.ts} +7 -7
- package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +17 -0
- package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +22 -0
- package/src/__tests__/components/BioTemplateRenderer.test.ts +25 -0
- package/src/__tests__/components/ComponentBindingRenderer.test.ts +117 -0
- package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +1 -1
- package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +1 -1
- package/src/__tests__/composables/useControlSchema.test.ts +1 -1
- package/src/__tests__/templates/templates.test.ts +44 -0
- package/src/components/AppSidebar.vue +3 -3
- package/src/components/AppTopBar.vue +7 -7
- package/src/components/BioTemplatePresetWorkspaceView.vue +3 -3
- package/src/components/BioTemplateRenderer.story.vue +2 -2
- package/src/components/ComponentBindingRenderer.story.vue +30 -0
- package/src/components/ComponentBindingRenderer.vue +9 -0
- package/src/components/ExperimentPopover.story.vue +2 -2
- package/src/styles/components/app-page-selector.css +1 -1
- package/src/styles/components/app-pill-nav.css +1 -1
- package/src/styles/components/experiment-popover.css +2 -2
- package/src/templates/adapters.ts +193 -0
- package/src/templates/catalog.ts +5 -5
- package/src/templates/componentBindings.ts +52 -3
- package/src/templates/index.ts +6 -0
- package/src/templates/packs.ts +10 -1
- package/src/templates/presets.ts +14 -4
- package/src/templates/types.ts +4 -0
- package/dist/components-BkGF4B4y.js.map +0 -1
- package/dist/useScheduleDrag-BgzpQT53.js.map +0 -1
- /package/dist/__tests__/components/{AppPageSelector.test.d.ts → AppTopBarPageSelector.test.d.ts} +0 -0
- /package/dist/__tests__/components/{AppPillNav.test.d.ts → AppTopBarPillNav.test.d.ts} +0 -0
- /package/dist/components/internal/{AppPageSelectorInternal.vue.d.ts → AppTopBarPageSelectorInternal.vue.d.ts} +0 -0
- /package/dist/components/internal/{AppPillNavInternal.vue.d.ts → AppTopBarPillNavInternal.vue.d.ts} +0 -0
- /package/src/components/internal/{AppPageSelectorInternal.vue → AppTopBarPageSelectorInternal.vue} +0 -0
- /package/src/components/internal/{AppPillNavInternal.vue → AppTopBarPillNavInternal.vue} +0 -0
|
@@ -44,11 +44,11 @@ const lcmsBatch = createLcmsBatchCollection({
|
|
|
44
44
|
</div>
|
|
45
45
|
</Variant>
|
|
46
46
|
|
|
47
|
-
<Variant title="
|
|
47
|
+
<Variant title="LC-MS Batch Preview">
|
|
48
48
|
<div style="padding: 2rem; background: var(--bg-primary, #f8fafc);">
|
|
49
49
|
<BioTemplateRenderer
|
|
50
50
|
:target="lcmsBatch"
|
|
51
|
-
:include="['DataFrame']"
|
|
51
|
+
:include="['DataFrame', 'ExperimentTimeline', 'ScheduleCalendar']"
|
|
52
52
|
dense
|
|
53
53
|
/>
|
|
54
54
|
</div>
|
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
defineDoseDesignControlModel,
|
|
6
6
|
useControlWorkspace,
|
|
7
7
|
} from '../composables/useControlSchema'
|
|
8
|
+
import {
|
|
9
|
+
createLcmsBatchCollection,
|
|
10
|
+
toBioTemplateComponentBindings,
|
|
11
|
+
} from '../templates'
|
|
8
12
|
|
|
9
13
|
const model = defineDoseDesignControlModel({
|
|
10
14
|
selectedWells: ['A1', 'A2', 'B1'],
|
|
@@ -12,6 +16,18 @@ const model = defineDoseDesignControlModel({
|
|
|
12
16
|
includeMolecularWeight: true,
|
|
13
17
|
})
|
|
14
18
|
const workspace = useControlWorkspace(model)
|
|
19
|
+
const lcmsCollection = createLcmsBatchCollection({
|
|
20
|
+
samples: [
|
|
21
|
+
{ sampleId: 'blank-1', name: 'Blank', group: 'Blank' },
|
|
22
|
+
{ sampleId: 'qc-pool', name: 'Pooled QC', group: 'QC' },
|
|
23
|
+
{ sampleId: 's001', name: 'Patient 001', group: 'Case' },
|
|
24
|
+
{ sampleId: 's002', name: 'Patient 002', group: 'Control' },
|
|
25
|
+
],
|
|
26
|
+
features: ['Glucose', 'Lactate', 'Citrate'],
|
|
27
|
+
instrument: 'Orbitrap LC-MS',
|
|
28
|
+
method: 'HILIC negative',
|
|
29
|
+
includeQc: true,
|
|
30
|
+
})
|
|
15
31
|
|
|
16
32
|
const state = ref({
|
|
17
33
|
dense: false,
|
|
@@ -22,6 +38,7 @@ const state = ref({
|
|
|
22
38
|
})
|
|
23
39
|
|
|
24
40
|
const bindings = computed(() => workspace.componentBindings.value)
|
|
41
|
+
const lcmsBindings = computed(() => toBioTemplateComponentBindings(lcmsCollection))
|
|
25
42
|
</script>
|
|
26
43
|
|
|
27
44
|
<template>
|
|
@@ -39,6 +56,19 @@ const bindings = computed(() => workspace.componentBindings.value)
|
|
|
39
56
|
</div>
|
|
40
57
|
</Variant>
|
|
41
58
|
|
|
59
|
+
<Variant title="LC-MS Batch Bindings">
|
|
60
|
+
<div style="padding: 2rem; min-height: 640px; background: var(--bg-secondary, #f8fafc);">
|
|
61
|
+
<ComponentBindingRenderer
|
|
62
|
+
:bindings="lcmsBindings"
|
|
63
|
+
:dense="state.dense"
|
|
64
|
+
:readonly="state.readonly"
|
|
65
|
+
:show-headers="state.showHeaders"
|
|
66
|
+
:show-descriptions="state.showDescriptions"
|
|
67
|
+
:layout="state.layout"
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
</Variant>
|
|
71
|
+
|
|
42
72
|
<template #controls>
|
|
43
73
|
<HstCheckbox v-model="state.dense" title="Dense" />
|
|
44
74
|
<HstCheckbox v-model="state.readonly" title="Readonly" />
|
|
@@ -7,6 +7,7 @@ import ExperimentTimeline from './ExperimentTimeline.vue'
|
|
|
7
7
|
import PlateMapEditor from './PlateMapEditor.vue'
|
|
8
8
|
import ReagentList from './ReagentList.vue'
|
|
9
9
|
import SampleSelector from './SampleSelector.vue'
|
|
10
|
+
import ScheduleCalendar from './ScheduleCalendar.vue'
|
|
10
11
|
import WellPlate from './WellPlate.vue'
|
|
11
12
|
|
|
12
13
|
export interface ComponentBindingRendererBinding {
|
|
@@ -69,6 +70,7 @@ const componentRegistry: Record<string, Component> = {
|
|
|
69
70
|
PlateMapEditor,
|
|
70
71
|
ReagentList,
|
|
71
72
|
SampleSelector,
|
|
73
|
+
ScheduleCalendar,
|
|
72
74
|
WellPlate,
|
|
73
75
|
}
|
|
74
76
|
|
|
@@ -152,6 +154,13 @@ function normalizeProps(binding: ComponentBindingRendererBinding): Record<string
|
|
|
152
154
|
enableGrouping: props.readonly ? false : (base.enableGrouping ?? true),
|
|
153
155
|
enableSmartGroup: props.readonly ? false : (base.enableSmartGroup ?? true),
|
|
154
156
|
}
|
|
157
|
+
case 'ScheduleCalendar':
|
|
158
|
+
return {
|
|
159
|
+
...base,
|
|
160
|
+
readonly: props.readonly || Boolean(base.readonly),
|
|
161
|
+
showNavigation: props.dense ? false : (base.showNavigation ?? true),
|
|
162
|
+
showViewToggle: props.dense ? false : (base.showViewToggle ?? true),
|
|
163
|
+
}
|
|
155
164
|
default:
|
|
156
165
|
return base
|
|
157
166
|
}
|
|
@@ -283,7 +283,7 @@ const STATUSES = ['planned', 'ongoing', 'completed', 'ready_to_extract', 'proces
|
|
|
283
283
|
</div>
|
|
284
284
|
</div>
|
|
285
285
|
<p style="margin-top: 1rem; font-size: 0.75rem; color: var(--text-muted); text-align: center;">
|
|
286
|
-
Same 2.375rem height and refresh-design motion as AppPluginSwitcher,
|
|
286
|
+
Same 2.375rem height and refresh-design motion as AppPluginSwitcher, AppTopBar page selector, AppAvatarMenu.
|
|
287
287
|
</p>
|
|
288
288
|
</div>
|
|
289
289
|
</Variant>
|
|
@@ -363,7 +363,7 @@ optionally pairs with an inline Save button as a split unit.
|
|
|
363
363
|
|
|
364
364
|
### Refresh-design alignment
|
|
365
365
|
|
|
366
|
-
- **Height** `2.375rem` — matches `AppPluginSwitcher` / `
|
|
366
|
+
- **Height** `2.375rem` — matches `AppPluginSwitcher` / `AppTopBar page selector` so the
|
|
367
367
|
topbar's right cluster reads at one optical baseline.
|
|
368
368
|
- **Surface** `--bg-card` + `--border-color` — consistent with the white-card +
|
|
369
369
|
border language.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* ExperimentPopover — aligned with refresh design (AppPluginSwitcher /
|
|
1
|
+
/* ExperimentPopover — aligned with refresh design (AppPluginSwitcher / AppTopBar page selector) */
|
|
2
2
|
|
|
3
3
|
/* Container */
|
|
4
4
|
.mint-experiment-popover {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
transition-duration: 0.05s;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
/* Active (panel open) — ring treatment, same language as PluginSwitcher/
|
|
68
|
+
/* Active (panel open) — ring treatment, same language as PluginSwitcher/page selector */
|
|
69
69
|
.mint-experiment-popover__trigger--active {
|
|
70
70
|
border-color: var(--color-primary);
|
|
71
71
|
box-shadow: 0 0 0 3px var(--color-primary-soft);
|
|
@@ -2,6 +2,11 @@ import type {
|
|
|
2
2
|
DataFrameColumn,
|
|
3
3
|
PlateCondition,
|
|
4
4
|
PlateMapEditorState,
|
|
5
|
+
ProtocolStep,
|
|
6
|
+
ProtocolStepStatus,
|
|
7
|
+
ProtocolStepType,
|
|
8
|
+
ScheduleEvent,
|
|
9
|
+
ScheduleEventStatus,
|
|
5
10
|
SelectOption,
|
|
6
11
|
Well,
|
|
7
12
|
} from '../types'
|
|
@@ -24,6 +29,7 @@ import type {
|
|
|
24
29
|
AssayMatrixColumnsAdapterResult,
|
|
25
30
|
AssayMatrixDataFrameAdapterResult,
|
|
26
31
|
AssayMatrixRowsAdapterResult,
|
|
32
|
+
AssayMatrixSampleOptionsAdapterResult,
|
|
27
33
|
AssayMatrixTemplate,
|
|
28
34
|
AssayMatrixTemplateData,
|
|
29
35
|
BioTemplateEnvelope,
|
|
@@ -44,8 +50,13 @@ import type {
|
|
|
44
50
|
InstrumentRunColumnsAdapterResult,
|
|
45
51
|
InstrumentRunDataFrameAdapterResult,
|
|
46
52
|
InstrumentRunRowsAdapterResult,
|
|
53
|
+
InstrumentRunScheduleEventsAdapterResult,
|
|
54
|
+
InstrumentRunStepsAdapterResult,
|
|
47
55
|
InstrumentRunTemplate,
|
|
48
56
|
InstrumentRunTemplateData,
|
|
57
|
+
InstrumentRunItem,
|
|
58
|
+
InstrumentRunItemKind,
|
|
59
|
+
InstrumentRunStatus,
|
|
49
60
|
PlateMapEditorAdapterResult,
|
|
50
61
|
PlateMapTemplate,
|
|
51
62
|
PlateMapTemplateData,
|
|
@@ -478,6 +489,19 @@ export function toAssayMatrixDataFrame(
|
|
|
478
489
|
}
|
|
479
490
|
}
|
|
480
491
|
|
|
492
|
+
/** Convert an assay-matrix template into SampleSelector options derived from assay samples. */
|
|
493
|
+
export function toAssayMatrixSampleOptions(
|
|
494
|
+
template: AssayMatrixTemplate | AssayMatrixTemplateData
|
|
495
|
+
): AssayMatrixSampleOptionsAdapterResult {
|
|
496
|
+
const data = getTemplateData(template, 'assay-matrix')
|
|
497
|
+
validateAssayMatrixData(data)
|
|
498
|
+
return data.samples.map((sample): SelectOption<string> => ({
|
|
499
|
+
value: sample.sampleId,
|
|
500
|
+
label: sample.name ?? sample.sampleId,
|
|
501
|
+
description: sample.group,
|
|
502
|
+
}))
|
|
503
|
+
}
|
|
504
|
+
|
|
481
505
|
export function toReagentListItems(
|
|
482
506
|
template: ReagentListTemplate | ReagentListTemplateData
|
|
483
507
|
): ReagentListItemsAdapterResult {
|
|
@@ -649,6 +673,91 @@ export function toInstrumentRunDataFrame(
|
|
|
649
673
|
}
|
|
650
674
|
}
|
|
651
675
|
|
|
676
|
+
/** Convert an instrument-run template into ExperimentTimeline protocol steps for acquisition queue previews. */
|
|
677
|
+
export function toInstrumentRunSteps(
|
|
678
|
+
template: InstrumentRunTemplate | InstrumentRunTemplateData
|
|
679
|
+
): InstrumentRunStepsAdapterResult {
|
|
680
|
+
const data = getTemplateData(template, 'instrument-run')
|
|
681
|
+
validateInstrumentRunData(data)
|
|
682
|
+
const methods = new Map(data.methods.map(method => [method.id, method]))
|
|
683
|
+
return [...data.items]
|
|
684
|
+
.sort((a, b) => a.order - b.order)
|
|
685
|
+
.map((item): ProtocolStep => {
|
|
686
|
+
const method = methods.get(item.methodId)
|
|
687
|
+
const name = item.name ?? item.sampleId ?? item.kind
|
|
688
|
+
return {
|
|
689
|
+
id: item.id,
|
|
690
|
+
type: instrumentRunStepType(item.kind),
|
|
691
|
+
name,
|
|
692
|
+
description: [
|
|
693
|
+
item.kind,
|
|
694
|
+
method?.name,
|
|
695
|
+
item.vial ? `Vial ${item.vial}` : undefined,
|
|
696
|
+
item.wellId ? `Well ${item.wellId}` : undefined,
|
|
697
|
+
]
|
|
698
|
+
.filter(Boolean)
|
|
699
|
+
.join(' / '),
|
|
700
|
+
duration: item.expectedDurationMin,
|
|
701
|
+
status: instrumentRunStepStatus(item.status),
|
|
702
|
+
parameters: {
|
|
703
|
+
kind: item.kind,
|
|
704
|
+
sampleId: item.sampleId,
|
|
705
|
+
methodId: item.methodId,
|
|
706
|
+
method: method?.name,
|
|
707
|
+
vial: item.vial,
|
|
708
|
+
plateId: item.plateId,
|
|
709
|
+
wellId: item.wellId,
|
|
710
|
+
injectionVolume: item.injectionVolume,
|
|
711
|
+
instrument: data.instrument ?? method?.instrument,
|
|
712
|
+
},
|
|
713
|
+
order: item.order,
|
|
714
|
+
}
|
|
715
|
+
})
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/** Convert an instrument-run template into readonly ScheduleCalendar events ordered by run queue timing. */
|
|
719
|
+
export function toInstrumentRunScheduleEvents(
|
|
720
|
+
template: InstrumentRunTemplate | InstrumentRunTemplateData
|
|
721
|
+
): InstrumentRunScheduleEventsAdapterResult {
|
|
722
|
+
const data = getTemplateData(template, 'instrument-run')
|
|
723
|
+
validateInstrumentRunData(data)
|
|
724
|
+
const methods = new Map(data.methods.map(method => [method.id, method]))
|
|
725
|
+
let cursor = dateFromMetadata(data.metadata, ['scheduledStart', 'runStart', 'start', 'startedAt'])
|
|
726
|
+
?? new Date('2024-01-01T08:00:00.000Z')
|
|
727
|
+
|
|
728
|
+
return [...data.items]
|
|
729
|
+
.sort((a, b) => a.order - b.order)
|
|
730
|
+
.map((item): ScheduleEvent => {
|
|
731
|
+
const method = methods.get(item.methodId)
|
|
732
|
+
const start = dateFromMetadata(item.metadata, ['scheduledStart', 'start', 'startedAt']) ?? cursor
|
|
733
|
+
const end = dateFromMetadata(item.metadata, ['scheduledEnd', 'end', 'completedAt'])
|
|
734
|
+
?? addMinutes(start, Math.max(item.expectedDurationMin ?? 10, 1))
|
|
735
|
+
cursor = end
|
|
736
|
+
|
|
737
|
+
return {
|
|
738
|
+
id: item.id,
|
|
739
|
+
title: item.name ?? item.sampleId ?? item.kind,
|
|
740
|
+
start: start.toISOString(),
|
|
741
|
+
end: end.toISOString(),
|
|
742
|
+
color: instrumentRunScheduleColor(item.kind),
|
|
743
|
+
status: instrumentRunScheduleStatus(item.status),
|
|
744
|
+
draggable: false,
|
|
745
|
+
resizable: false,
|
|
746
|
+
metadata: {
|
|
747
|
+
kind: item.kind,
|
|
748
|
+
sampleId: item.sampleId,
|
|
749
|
+
methodId: item.methodId,
|
|
750
|
+
method: method?.name,
|
|
751
|
+
vial: item.vial,
|
|
752
|
+
plateId: item.plateId,
|
|
753
|
+
wellId: item.wellId,
|
|
754
|
+
injectionVolume: item.injectionVolume,
|
|
755
|
+
instrument: data.instrument ?? method?.instrument,
|
|
756
|
+
},
|
|
757
|
+
}
|
|
758
|
+
})
|
|
759
|
+
}
|
|
760
|
+
|
|
652
761
|
export function toQpcrRows(
|
|
653
762
|
template: QpcrPlateTemplate | QpcrPlateTemplateData
|
|
654
763
|
): QpcrRowsAdapterResult {
|
|
@@ -783,3 +892,87 @@ function sampleColumnKey(columnId: string): string {
|
|
|
783
892
|
}
|
|
784
893
|
return aliases[columnId] ?? columnId
|
|
785
894
|
}
|
|
895
|
+
|
|
896
|
+
function instrumentRunStepType(kind: InstrumentRunItemKind): ProtocolStepType {
|
|
897
|
+
switch (kind) {
|
|
898
|
+
case 'wash':
|
|
899
|
+
return 'wash'
|
|
900
|
+
case 'blank':
|
|
901
|
+
case 'qc':
|
|
902
|
+
case 'standard':
|
|
903
|
+
case 'calibration':
|
|
904
|
+
case 'sample':
|
|
905
|
+
return 'measurement'
|
|
906
|
+
default:
|
|
907
|
+
return 'custom'
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
function instrumentRunStepStatus(status: InstrumentRunStatus): ProtocolStepStatus {
|
|
912
|
+
switch (status) {
|
|
913
|
+
case 'running':
|
|
914
|
+
return 'in_progress'
|
|
915
|
+
case 'completed':
|
|
916
|
+
return 'completed'
|
|
917
|
+
case 'failed':
|
|
918
|
+
return 'failed'
|
|
919
|
+
case 'skipped':
|
|
920
|
+
return 'skipped'
|
|
921
|
+
case 'planned':
|
|
922
|
+
case 'queued':
|
|
923
|
+
default:
|
|
924
|
+
return 'pending'
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
function instrumentRunScheduleStatus(status: InstrumentRunStatus): ScheduleEventStatus {
|
|
929
|
+
switch (status) {
|
|
930
|
+
case 'running':
|
|
931
|
+
return 'in-progress'
|
|
932
|
+
case 'completed':
|
|
933
|
+
return 'confirmed'
|
|
934
|
+
case 'failed':
|
|
935
|
+
case 'skipped':
|
|
936
|
+
return 'cancelled'
|
|
937
|
+
case 'planned':
|
|
938
|
+
case 'queued':
|
|
939
|
+
default:
|
|
940
|
+
return 'pending'
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
function instrumentRunScheduleColor(kind: InstrumentRunItem['kind']): string {
|
|
945
|
+
switch (kind) {
|
|
946
|
+
case 'blank':
|
|
947
|
+
return '#64748b'
|
|
948
|
+
case 'qc':
|
|
949
|
+
return '#8b5cf6'
|
|
950
|
+
case 'standard':
|
|
951
|
+
case 'calibration':
|
|
952
|
+
return '#f59e0b'
|
|
953
|
+
case 'wash':
|
|
954
|
+
return '#06b6d4'
|
|
955
|
+
case 'sample':
|
|
956
|
+
return '#2563eb'
|
|
957
|
+
default:
|
|
958
|
+
return '#475569'
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function dateFromMetadata(
|
|
963
|
+
metadata: Record<string, unknown> | undefined,
|
|
964
|
+
keys: readonly string[]
|
|
965
|
+
): Date | undefined {
|
|
966
|
+
if (!metadata) return undefined
|
|
967
|
+
for (const key of keys) {
|
|
968
|
+
const value = metadata[key]
|
|
969
|
+
if (!(typeof value === 'string' || value instanceof Date)) continue
|
|
970
|
+
const date = new Date(value)
|
|
971
|
+
if (!Number.isNaN(date.getTime())) return date
|
|
972
|
+
}
|
|
973
|
+
return undefined
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
function addMinutes(date: Date, minutes: number): Date {
|
|
977
|
+
return new Date(date.getTime() + minutes * 60_000)
|
|
978
|
+
}
|
package/src/templates/catalog.ts
CHANGED
|
@@ -142,8 +142,8 @@ export const bioTemplateCatalog = [
|
|
|
142
142
|
aliases: ['omics matrix', 'measurement matrix', 'readout matrix', 'feature table'],
|
|
143
143
|
python_import: 'from mint_sdk.templates import AssayMatrixTemplate, save_template',
|
|
144
144
|
python_example: 'AssayMatrixTemplate.create(samples=["S001", "S002"], features=["Lactate", "Glucose"])',
|
|
145
|
-
frontend_import: "import { toTemplateDataFrame } from '@morscherlab/mint-sdk/templates'",
|
|
146
|
-
frontend_adapters: ['toTemplateDataFrame', 'toAssayMatrixDataFrame', 'toAssayMatrixRows', 'toAssayMatrixColumns'],
|
|
145
|
+
frontend_import: "import { toAssayMatrixSampleOptions, toTemplateDataFrame } from '@morscherlab/mint-sdk/templates'",
|
|
146
|
+
frontend_adapters: ['toTemplateDataFrame', 'toAssayMatrixDataFrame', 'toAssayMatrixRows', 'toAssayMatrixColumns', 'toAssayMatrixSampleOptions'],
|
|
147
147
|
components: ['DataFrame', 'ChartContainer', 'SampleSelector'],
|
|
148
148
|
add_command: 'mint add data-template assay-matrix --page',
|
|
149
149
|
},
|
|
@@ -187,9 +187,9 @@ export const bioTemplateCatalog = [
|
|
|
187
187
|
aliases: ['run queue', 'sequence table', 'sample queue', 'lcms run', 'instrument sequence', 'batch run'],
|
|
188
188
|
python_import: 'from mint_sdk.templates import InstrumentRunTemplate, save_template',
|
|
189
189
|
python_example: 'InstrumentRunTemplate.create(["S001", "S002"], instrument="LC-MS")',
|
|
190
|
-
frontend_import: "import { toInstrumentRunDataFrame, toTemplateDataFrame } from '@morscherlab/mint-sdk/templates'",
|
|
191
|
-
frontend_adapters: ['toTemplateDataFrame', 'toInstrumentRunDataFrame', 'toInstrumentRunRows', 'toInstrumentRunColumns'],
|
|
192
|
-
components: ['DataFrame', 'ScheduleCalendar', 'SampleSelector'],
|
|
190
|
+
frontend_import: "import { toInstrumentRunDataFrame, toInstrumentRunScheduleEvents, toInstrumentRunSteps, toTemplateDataFrame } from '@morscherlab/mint-sdk/templates'",
|
|
191
|
+
frontend_adapters: ['toTemplateDataFrame', 'toInstrumentRunDataFrame', 'toInstrumentRunRows', 'toInstrumentRunColumns', 'toInstrumentRunSteps', 'toInstrumentRunScheduleEvents'],
|
|
192
|
+
components: ['DataFrame', 'ExperimentTimeline', 'ScheduleCalendar', 'SampleSelector'],
|
|
193
193
|
add_command: 'mint add data-template instrument-run --page',
|
|
194
194
|
},
|
|
195
195
|
{
|
|
@@ -4,8 +4,11 @@ import {
|
|
|
4
4
|
getTemplateData,
|
|
5
5
|
} from './builders'
|
|
6
6
|
import {
|
|
7
|
+
toAssayMatrixSampleOptions,
|
|
7
8
|
toDoseConditions,
|
|
8
9
|
toDoseLayoutState,
|
|
10
|
+
toInstrumentRunScheduleEvents,
|
|
11
|
+
toInstrumentRunSteps,
|
|
9
12
|
toPlateMapEditorState,
|
|
10
13
|
toProtocolSteps,
|
|
11
14
|
toQpcrWellPlateWells,
|
|
@@ -26,8 +29,10 @@ import {
|
|
|
26
29
|
} from './packs'
|
|
27
30
|
import type {
|
|
28
31
|
BioTemplateEnvelope,
|
|
32
|
+
AssayMatrixTemplate,
|
|
29
33
|
DataFrameTemplate,
|
|
30
34
|
DoseResponseTemplate,
|
|
35
|
+
InstrumentRunTemplate,
|
|
31
36
|
PlateMapTemplate,
|
|
32
37
|
QpcrPlateTemplate,
|
|
33
38
|
ReagentListTemplate,
|
|
@@ -140,6 +145,7 @@ const templateComponentBindings = {
|
|
|
140
145
|
],
|
|
141
146
|
'assay-matrix': [
|
|
142
147
|
binding('assay-matrix', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render sample-by-feature measurements.'),
|
|
148
|
+
binding('assay-matrix', 'SampleSelector', ['toAssayMatrixSampleOptions'], ['samples', 'modelValue'], 'Select samples from assay-matrix records.'),
|
|
143
149
|
],
|
|
144
150
|
'reagent-list': [
|
|
145
151
|
binding('reagent-list', 'ReagentList', ['toReagentListItems'], ['modelValue'], 'Render reagents with lot, storage, and stock metadata.'),
|
|
@@ -150,6 +156,20 @@ const templateComponentBindings = {
|
|
|
150
156
|
],
|
|
151
157
|
'instrument-run': [
|
|
152
158
|
binding('instrument-run', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render acquisition queues, methods, QC, and run status.'),
|
|
159
|
+
binding(
|
|
160
|
+
'instrument-run',
|
|
161
|
+
'ScheduleCalendar',
|
|
162
|
+
['toInstrumentRunScheduleEvents'],
|
|
163
|
+
['modelValue', 'events', 'view', 'readonly', 'showNavigation', 'showViewToggle'],
|
|
164
|
+
'Render acquisition queues as a scheduled run calendar.',
|
|
165
|
+
),
|
|
166
|
+
binding(
|
|
167
|
+
'instrument-run',
|
|
168
|
+
'ExperimentTimeline',
|
|
169
|
+
['toInstrumentRunSteps'],
|
|
170
|
+
['modelValue', 'orientation', 'showDuration', 'editable'],
|
|
171
|
+
'Render acquisition queues as an ordered run timeline.',
|
|
172
|
+
),
|
|
153
173
|
],
|
|
154
174
|
'qpcr-plate': [
|
|
155
175
|
binding('qpcr-plate', 'WellPlate', ['toQpcrWellPlateWells'], ['format', 'wells', 'sampleColors', 'legendItems'], 'Render qPCR reactions on a well plate.'),
|
|
@@ -329,10 +349,19 @@ function propsForTemplate(template: BioTemplateEnvelope<unknown>): BioTemplateCo
|
|
|
329
349
|
]
|
|
330
350
|
case 'sample-prep':
|
|
331
351
|
case 'calibration-curve':
|
|
332
|
-
case 'assay-matrix':
|
|
333
352
|
case 'flow-cytometry-panel':
|
|
334
|
-
case 'instrument-run':
|
|
335
353
|
return [dataFrameProps(template as DataFrameTemplate)]
|
|
354
|
+
case 'assay-matrix':
|
|
355
|
+
return [
|
|
356
|
+
dataFrameProps(template as DataFrameTemplate),
|
|
357
|
+
assayMatrixSampleSelectorProps(template as AssayMatrixTemplate),
|
|
358
|
+
]
|
|
359
|
+
case 'instrument-run':
|
|
360
|
+
return [
|
|
361
|
+
dataFrameProps(template as DataFrameTemplate),
|
|
362
|
+
scheduleCalendarProps(template as InstrumentRunTemplate),
|
|
363
|
+
timelineProps(template as InstrumentRunTemplate, toInstrumentRunSteps(template as InstrumentRunTemplate)),
|
|
364
|
+
]
|
|
336
365
|
case 'dose-response':
|
|
337
366
|
return doseResponseProps(template as DoseResponseTemplate)
|
|
338
367
|
case 'time-course':
|
|
@@ -446,8 +475,16 @@ function sampleSelectorProps(template: SampleSheetTemplate): BioTemplateComponen
|
|
|
446
475
|
})
|
|
447
476
|
}
|
|
448
477
|
|
|
478
|
+
function assayMatrixSampleSelectorProps(template: AssayMatrixTemplate): BioTemplateComponentPropsBinding {
|
|
479
|
+
const options = toAssayMatrixSampleOptions(template)
|
|
480
|
+
return propsBinding('assay-matrix', 'SampleSelector', {
|
|
481
|
+
samples: options.map(option => option.label),
|
|
482
|
+
modelValue: [],
|
|
483
|
+
})
|
|
484
|
+
}
|
|
485
|
+
|
|
449
486
|
function timelineProps(
|
|
450
|
-
template: TimeCourseTemplate | ProtocolStepsTemplate,
|
|
487
|
+
template: TimeCourseTemplate | ProtocolStepsTemplate | InstrumentRunTemplate,
|
|
451
488
|
steps: unknown[],
|
|
452
489
|
editable = false
|
|
453
490
|
): BioTemplateComponentPropsBinding {
|
|
@@ -458,6 +495,18 @@ function timelineProps(
|
|
|
458
495
|
})
|
|
459
496
|
}
|
|
460
497
|
|
|
498
|
+
function scheduleCalendarProps(template: InstrumentRunTemplate): BioTemplateComponentPropsBinding {
|
|
499
|
+
const events = toInstrumentRunScheduleEvents(template)
|
|
500
|
+
return propsBinding('instrument-run', 'ScheduleCalendar', {
|
|
501
|
+
modelValue: events[0]?.start,
|
|
502
|
+
events,
|
|
503
|
+
view: 'day',
|
|
504
|
+
readonly: true,
|
|
505
|
+
showNavigation: false,
|
|
506
|
+
showViewToggle: false,
|
|
507
|
+
})
|
|
508
|
+
}
|
|
509
|
+
|
|
461
510
|
function reagentListProps(template: ReagentListTemplate): BioTemplateComponentPropsBinding {
|
|
462
511
|
return propsBinding('reagent-list', 'ReagentList', {
|
|
463
512
|
modelValue: toReagentListItems(template),
|
package/src/templates/index.ts
CHANGED
|
@@ -106,6 +106,7 @@ export {
|
|
|
106
106
|
toAssayMatrixColumns,
|
|
107
107
|
toAssayMatrixDataFrame,
|
|
108
108
|
toAssayMatrixRows,
|
|
109
|
+
toAssayMatrixSampleOptions,
|
|
109
110
|
toCalibrationCurveColumns,
|
|
110
111
|
toCalibrationCurveDataFrame,
|
|
111
112
|
toCalibrationCurveRows,
|
|
@@ -117,6 +118,8 @@ export {
|
|
|
117
118
|
toInstrumentRunColumns,
|
|
118
119
|
toInstrumentRunDataFrame,
|
|
119
120
|
toInstrumentRunRows,
|
|
121
|
+
toInstrumentRunScheduleEvents,
|
|
122
|
+
toInstrumentRunSteps,
|
|
120
123
|
toPlateMapEditorState,
|
|
121
124
|
toProtocolColumns,
|
|
122
125
|
toProtocolDataFrame,
|
|
@@ -152,6 +155,7 @@ export type {
|
|
|
152
155
|
AssayMatrixColumnsAdapterResult,
|
|
153
156
|
AssayMatrixDataFrameAdapterResult,
|
|
154
157
|
AssayMatrixRowsAdapterResult,
|
|
158
|
+
AssayMatrixSampleOptionsAdapterResult,
|
|
155
159
|
AssayMatrixTemplate,
|
|
156
160
|
AssayMatrixTemplateData,
|
|
157
161
|
AssayMeasurement,
|
|
@@ -211,6 +215,8 @@ export type {
|
|
|
211
215
|
InstrumentRunItemInput,
|
|
212
216
|
InstrumentRunItemKind,
|
|
213
217
|
InstrumentRunRowsAdapterResult,
|
|
218
|
+
InstrumentRunScheduleEventsAdapterResult,
|
|
219
|
+
InstrumentRunStepsAdapterResult,
|
|
214
220
|
InstrumentRunStatus,
|
|
215
221
|
InstrumentRunTemplate,
|
|
216
222
|
InstrumentRunTemplateData,
|
package/src/templates/packs.ts
CHANGED
|
@@ -89,7 +89,16 @@ const bioTemplatePackDefinitions = [
|
|
|
89
89
|
'calibration-curve',
|
|
90
90
|
'assay-matrix',
|
|
91
91
|
],
|
|
92
|
-
aliases: [
|
|
92
|
+
aliases: [
|
|
93
|
+
'omics',
|
|
94
|
+
'metabolomics',
|
|
95
|
+
'metabolism',
|
|
96
|
+
'metabolite profiling',
|
|
97
|
+
'proteomics',
|
|
98
|
+
'assay matrix',
|
|
99
|
+
'lcms',
|
|
100
|
+
'standard curve',
|
|
101
|
+
],
|
|
93
102
|
},
|
|
94
103
|
{
|
|
95
104
|
name: 'longitudinal-study',
|
package/src/templates/presets.ts
CHANGED
|
@@ -57,12 +57,22 @@ export const bioTemplatePresets = [
|
|
|
57
57
|
category: 'bio-template-preset',
|
|
58
58
|
purpose: 'Sample metadata, instrument queue, and assay matrix payloads for omics acquisition batches.',
|
|
59
59
|
templates: ['sample-sheet', 'instrument-run', 'assay-matrix'],
|
|
60
|
-
aliases: [
|
|
60
|
+
aliases: [
|
|
61
|
+
'lcms',
|
|
62
|
+
'lc-ms',
|
|
63
|
+
'mass spec batch',
|
|
64
|
+
'omics batch',
|
|
65
|
+
'metabolomics',
|
|
66
|
+
'metabolism',
|
|
67
|
+
'metabolite profiling',
|
|
68
|
+
'run queue',
|
|
69
|
+
'sequence table',
|
|
70
|
+
],
|
|
61
71
|
python_import: 'from mint_sdk.templates import create_lcms_batch_collection, save_template_collection',
|
|
62
72
|
python_example: 'create_lcms_batch_collection(samples=["S001", "S002"], features=["Glucose", "Lactate"], instrument="LC-MS")',
|
|
63
|
-
frontend_import: "import { createLcmsBatchCollection, toInstrumentRunDataFrame, toTemplateDataFrame } from '@morscherlab/mint-sdk/templates'",
|
|
64
|
-
frontend_adapters: ['toTemplateDataFrame', 'toInstrumentRunDataFrame', 'toAssayMatrixDataFrame'],
|
|
65
|
-
components: ['DataFrame', 'ScheduleCalendar', 'ChartContainer', 'SampleSelector'],
|
|
73
|
+
frontend_import: "import { createLcmsBatchCollection, toAssayMatrixSampleOptions, toInstrumentRunDataFrame, toInstrumentRunScheduleEvents, toInstrumentRunSteps, toTemplateDataFrame } from '@morscherlab/mint-sdk/templates'",
|
|
74
|
+
frontend_adapters: ['toTemplateDataFrame', 'toInstrumentRunDataFrame', 'toInstrumentRunSteps', 'toInstrumentRunScheduleEvents', 'toAssayMatrixDataFrame', 'toAssayMatrixSampleOptions'],
|
|
75
|
+
components: ['DataFrame', 'ExperimentTimeline', 'ScheduleCalendar', 'ChartContainer', 'SampleSelector'],
|
|
66
76
|
},
|
|
67
77
|
{
|
|
68
78
|
name: 'elisa-assay',
|
package/src/templates/types.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
PlateMapEditorState,
|
|
6
6
|
ProtocolStep,
|
|
7
7
|
Reagent,
|
|
8
|
+
ScheduleEvent,
|
|
8
9
|
SampleType,
|
|
9
10
|
SelectOption,
|
|
10
11
|
Well,
|
|
@@ -651,6 +652,7 @@ export type ProtocolDataFrameAdapterResult = DataFrameAdapterResult
|
|
|
651
652
|
export type AssayMatrixRowsAdapterResult = Array<Record<string, unknown>>
|
|
652
653
|
export type AssayMatrixColumnsAdapterResult = DataFrameColumn<Record<string, unknown>>[]
|
|
653
654
|
export type AssayMatrixDataFrameAdapterResult = DataFrameAdapterResult
|
|
655
|
+
export type AssayMatrixSampleOptionsAdapterResult = SelectOption<string>[]
|
|
654
656
|
export type ReagentListItemsAdapterResult = Reagent[]
|
|
655
657
|
export type ReagentRowsAdapterResult = Array<Record<string, unknown>>
|
|
656
658
|
export type ReagentColumnsAdapterResult = DataFrameColumn<Record<string, unknown>>[]
|
|
@@ -661,6 +663,8 @@ export type FlowPanelDataFrameAdapterResult = DataFrameAdapterResult
|
|
|
661
663
|
export type InstrumentRunRowsAdapterResult = Array<Record<string, unknown>>
|
|
662
664
|
export type InstrumentRunColumnsAdapterResult = DataFrameColumn<Record<string, unknown>>[]
|
|
663
665
|
export type InstrumentRunDataFrameAdapterResult = DataFrameAdapterResult
|
|
666
|
+
export type InstrumentRunStepsAdapterResult = ProtocolStep[]
|
|
667
|
+
export type InstrumentRunScheduleEventsAdapterResult = ScheduleEvent[]
|
|
664
668
|
export type QpcrRowsAdapterResult = Array<Record<string, unknown>>
|
|
665
669
|
export type QpcrColumnsAdapterResult = DataFrameColumn<Record<string, unknown>>[]
|
|
666
670
|
export type QpcrDataFrameAdapterResult = DataFrameAdapterResult
|