@morscherlab/mint-sdk 1.0.0-rc.1 → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/dist/__tests__/components/LcmsSequenceTable.test.d.ts +1 -0
- package/dist/__tests__/components/ProgressBar.test.d.ts +1 -0
- package/dist/__tests__/components/RackEditor.test.d.ts +1 -0
- package/dist/__tests__/components/SequenceProgressBar.test.d.ts +1 -0
- package/dist/__tests__/composables/useExperimentSamples.test.d.ts +1 -0
- package/dist/__tests__/utils/instrument.test.d.ts +1 -0
- package/dist/__tests__/utils/lcms.test.d.ts +1 -0
- package/dist/__tests__/utils/permissions.test.d.ts +1 -0
- package/dist/__tests__/utils/rack.test.d.ts +1 -0
- package/dist/{auth-CBG3bWEc.js → auth-B7g4J4ZF.js} +99 -5
- package/dist/auth-B7g4J4ZF.js.map +1 -0
- package/dist/components/AutoGroupModal.vue.d.ts +1 -1
- package/dist/components/BaseCheckbox.vue.d.ts +1 -1
- package/dist/components/BaseToggle.vue.d.ts +2 -2
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +1 -1
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -1
- package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +1 -1
- package/dist/components/DoseDesignWorkspaceView.vue.d.ts +1 -1
- package/dist/components/FormulaInput.vue.d.ts +1 -1
- package/dist/components/InstrumentAlertLog.vue.d.ts +22 -0
- package/dist/components/InstrumentStateBadge.vue.d.ts +11 -0
- package/dist/components/InstrumentStatusCard.vue.d.ts +13 -0
- package/dist/components/LcmsSequenceTable.vue.d.ts +26 -0
- package/dist/components/ProgressBar.vue.d.ts +1 -0
- package/dist/components/RackEditor.vue.d.ts +41 -3
- package/dist/components/ReagentList.vue.d.ts +1 -1
- package/dist/components/SampleSelector.vue.d.ts +5 -2
- package/dist/components/SegmentedControl.vue.d.ts +2 -0
- package/dist/components/SequenceInput.vue.d.ts +1 -1
- package/dist/components/SequenceProgressBar.vue.d.ts +15 -0
- package/dist/components/SettingsModal.vue.d.ts +3 -1
- package/dist/components/TagsInput.vue.d.ts +1 -1
- package/dist/components/WellPlate.vue.d.ts +42 -3
- package/dist/components/index.d.ts +5 -0
- package/dist/components/index.js +3 -3
- package/dist/{components-5KSfsVqf.js → components-BhK-dW99.js} +2091 -1051
- package/dist/components-BhK-dW99.js.map +1 -0
- package/dist/composables/experimentDesignData.d.ts +17 -0
- package/dist/composables/index.d.ts +2 -0
- package/dist/composables/index.js +4 -4
- package/dist/composables/useControlSchema.d.ts +11 -0
- package/dist/composables/useExperimentData.d.ts +11 -3
- package/dist/composables/useExperimentSamples.d.ts +42 -0
- package/dist/composables/usePlatformContext.d.ts +54 -0
- package/dist/{composables-D4Myb30a.js → composables-Bg7CFuNz.js} +5 -3
- package/dist/composables-Bg7CFuNz.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +168 -6
- package/dist/index.js.map +1 -0
- package/dist/install.js +2 -2
- package/dist/instrument.d.ts +7 -0
- package/dist/lcms.d.ts +27 -0
- package/dist/permissions.d.ts +46 -0
- package/dist/stores/auth.d.ts +74 -2
- package/dist/stores/index.js +1 -1
- package/dist/styles.css +3316 -1216
- package/dist/templates/builders.d.ts +7 -3
- package/dist/templates/index.d.ts +2 -2
- package/dist/templates/index.js +2 -2
- package/dist/templates/presets.d.ts +12 -0
- package/dist/templates/types.d.ts +16 -1
- package/dist/{templates-BSlxwV2c.js → templates-BorLR_7p.js} +313 -3
- package/dist/templates-BorLR_7p.js.map +1 -0
- package/dist/types/auth.d.ts +2 -0
- package/dist/types/components.d.ts +32 -3
- package/dist/types/form-builder.d.ts +2 -1
- package/dist/types/index.d.ts +4 -1
- package/dist/types/instrument.d.ts +56 -0
- package/dist/types/platform.d.ts +3 -0
- package/dist/{useExperimentData-BbbdI5xT.js → useProtocolTemplates-n6AJqSqv.js} +534 -359
- package/dist/useProtocolTemplates-n6AJqSqv.js.map +1 -0
- package/dist/utils/rack.d.ts +47 -0
- package/package.json +1 -1
- package/src/__tests__/components/AppTopBar.test.ts +15 -0
- package/src/__tests__/components/BaseTabs.test.ts +15 -0
- package/src/__tests__/components/LcmsSequenceTable.test.ts +57 -0
- package/src/__tests__/components/ProgressBar.test.ts +18 -0
- package/src/__tests__/components/RackEditor.test.ts +125 -0
- package/src/__tests__/components/SampleSelector.test.ts +25 -0
- package/src/__tests__/components/SegmentedControl.test.ts +45 -0
- package/src/__tests__/components/SequenceProgressBar.test.ts +39 -0
- package/src/__tests__/components/SettingsModal.test.ts +83 -2
- package/src/__tests__/composables/useControlSchema.test.ts +4 -0
- package/src/__tests__/composables/useExperimentData.test.ts +23 -0
- package/src/__tests__/composables/useExperimentSamples.test.ts +91 -0
- package/src/__tests__/templates/templates.test.ts +86 -0
- package/src/__tests__/utils/instrument.test.ts +47 -0
- package/src/__tests__/utils/lcms.test.ts +73 -0
- package/src/__tests__/utils/permissions.test.ts +50 -0
- package/src/__tests__/utils/rack.test.ts +120 -0
- package/src/components/AppTopBar.vue +1 -0
- package/src/components/BaseTabs.vue +22 -1
- package/src/components/InstrumentAlertLog.vue +191 -0
- package/src/components/InstrumentStateBadge.vue +50 -0
- package/src/components/InstrumentStatusCard.vue +188 -0
- package/src/components/LcmsSequenceTable.vue +191 -0
- package/src/components/ProgressBar.vue +3 -0
- package/src/components/RackEditor.vue +73 -2
- package/src/components/SampleSelector.vue +28 -9
- package/src/components/SegmentedControl.story.vue +17 -0
- package/src/components/SegmentedControl.vue +14 -3
- package/src/components/SequenceProgressBar.vue +71 -0
- package/src/components/SettingsModal.vue +42 -2
- package/src/components/WellPlate.vue +142 -21
- package/src/components/index.ts +5 -0
- package/src/components/internal/WellEditPopupInternal.vue +1 -0
- package/src/composables/experimentDesignData.ts +182 -0
- package/src/composables/index.ts +14 -0
- package/src/composables/useAuth.ts +4 -0
- package/src/composables/useAutoGroup.ts +5 -1
- package/src/composables/useControlSchema.ts +21 -0
- package/src/composables/useExperimentData.ts +57 -16
- package/src/composables/useExperimentSamples.ts +142 -0
- package/src/index.ts +27 -0
- package/src/instrument.ts +90 -0
- package/src/lcms.ts +108 -0
- package/src/permissions.ts +143 -0
- package/src/stores/auth.ts +31 -3
- package/src/styles/components/instrument-monitor.css +478 -0
- package/src/styles/components/lcms-sequence-table.css +189 -0
- package/src/styles/components/sequence-progress-bar.css +63 -0
- package/src/styles/components/tabs.css +9 -0
- package/src/styles/components/well-edit-popup.css +7 -1
- package/src/styles/components/well-plate.css +5 -0
- package/src/styles/index.css +3 -0
- package/src/templates/builders.ts +201 -0
- package/src/templates/controlSchemas.ts +68 -0
- package/src/templates/index.ts +2 -0
- package/src/templates/presets.ts +23 -0
- package/src/templates/types.ts +17 -0
- package/src/types/auth.ts +3 -0
- package/src/types/components.ts +45 -3
- package/src/types/form-builder.ts +2 -1
- package/src/types/index.ts +35 -0
- package/src/types/instrument.ts +61 -0
- package/src/types/platform.ts +4 -0
- package/src/utils/rack.ts +209 -0
- package/dist/auth-CBG3bWEc.js.map +0 -1
- package/dist/components-5KSfsVqf.js.map +0 -1
- package/dist/composables-D4Myb30a.js.map +0 -1
- package/dist/templates-BSlxwV2c.js.map +0 -1
- package/dist/useExperimentData-BbbdI5xT.js.map +0 -1
package/src/types/components.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { UserSummary } from './platform'
|
|
2
|
+
import type { AccessControlled, AccessAudience, AccessAudienceInput } from '../permissions'
|
|
2
3
|
|
|
3
4
|
// Container types
|
|
4
5
|
export type ContainerDirection = 'row' | 'column'
|
|
@@ -29,7 +30,7 @@ export interface Toast {
|
|
|
29
30
|
export interface TabItem {
|
|
30
31
|
id: string
|
|
31
32
|
label: string
|
|
32
|
-
icon?: string
|
|
33
|
+
icon?: string | string[]
|
|
33
34
|
disabled?: boolean
|
|
34
35
|
badge?: string | number
|
|
35
36
|
}
|
|
@@ -121,6 +122,8 @@ export interface TopBarSettingsConfig {
|
|
|
121
122
|
values?: Record<string, unknown>
|
|
122
123
|
/** Dynamic enhancements (validators, dynamic options, callbacks). Forwarded to SettingsModal. */
|
|
123
124
|
enhancements?: import('./form-builder').FormEnhancements
|
|
125
|
+
/** Optional user type override for permission-filtered settings content. Defaults to SDK auth/platform context. */
|
|
126
|
+
userType?: SettingsUserType
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
// TopBar pill-nav items (centered top-level navigation)
|
|
@@ -232,6 +235,29 @@ export interface WellEditData {
|
|
|
232
235
|
// Fields shown in the edit popup
|
|
233
236
|
export type WellEditField = 'label' | 'sampleType' | 'injectionVolume' | 'injectionCount' | 'customMethod'
|
|
234
237
|
|
|
238
|
+
// External sample drop payloads for WellPlate / RackEditor
|
|
239
|
+
export interface WellSampleDropData {
|
|
240
|
+
id?: string
|
|
241
|
+
sampleName?: string
|
|
242
|
+
label?: string
|
|
243
|
+
sampleType?: string
|
|
244
|
+
injectionVolume?: number
|
|
245
|
+
injectionCount?: number
|
|
246
|
+
customMethod?: string | null
|
|
247
|
+
metadata?: Record<string, unknown>
|
|
248
|
+
raw?: unknown
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface WellSampleDropContext {
|
|
252
|
+
wellId: string
|
|
253
|
+
event: DragEvent
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export type WellSampleDropParser = (
|
|
257
|
+
event: DragEvent,
|
|
258
|
+
context: WellSampleDropContext
|
|
259
|
+
) => WellSampleDropData | null | undefined
|
|
260
|
+
|
|
235
261
|
// Legend item for sample type display
|
|
236
262
|
export interface WellLegendItem {
|
|
237
263
|
type: string
|
|
@@ -265,6 +291,18 @@ export interface Rack {
|
|
|
265
291
|
wells: Record<string, Partial<Well>>
|
|
266
292
|
}
|
|
267
293
|
|
|
294
|
+
export interface RackSampleDropContext {
|
|
295
|
+
rack: Rack
|
|
296
|
+
rackId: string
|
|
297
|
+
wellId: string
|
|
298
|
+
event: DragEvent
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export type RackSampleDropMapper = (
|
|
302
|
+
data: WellSampleDropData,
|
|
303
|
+
context: RackSampleDropContext
|
|
304
|
+
) => WellEditData | null | undefined
|
|
305
|
+
|
|
268
306
|
// Sample Legend types
|
|
269
307
|
export interface SampleType {
|
|
270
308
|
id: string
|
|
@@ -531,7 +569,7 @@ export type TooltipPosition = 'top' | 'bottom' | 'left' | 'right'
|
|
|
531
569
|
export type ConfirmVariant = 'danger' | 'warning' | 'info'
|
|
532
570
|
|
|
533
571
|
// SettingsModal types
|
|
534
|
-
export interface SettingsTab {
|
|
572
|
+
export interface SettingsTab extends AccessControlled {
|
|
535
573
|
id: string
|
|
536
574
|
label: string
|
|
537
575
|
/**
|
|
@@ -553,7 +591,7 @@ export type SettingsModalLayout = 'horizontal' | 'vertical'
|
|
|
553
591
|
* SDK form components (BaseInput / BaseSelect / NumberInput / Toggle / …)
|
|
554
592
|
* via the same registry that powers FormBuilder.
|
|
555
593
|
*/
|
|
556
|
-
export interface SettingsGroup {
|
|
594
|
+
export interface SettingsGroup extends AccessControlled {
|
|
557
595
|
id: string
|
|
558
596
|
label: string
|
|
559
597
|
description?: string
|
|
@@ -574,6 +612,10 @@ export interface SettingsModalSchema {
|
|
|
574
612
|
groups: SettingsGroup[]
|
|
575
613
|
}
|
|
576
614
|
|
|
615
|
+
export type SettingsUserType = Exclude<AccessAudience, 'all'>
|
|
616
|
+
export type SettingsAudience = AccessAudience
|
|
617
|
+
export type SettingsAudienceInput = AccessAudienceInput
|
|
618
|
+
|
|
577
619
|
// ScientificNumber types
|
|
578
620
|
export type NumberNotation = 'auto' | 'scientific' | 'engineering' | 'compact'
|
|
579
621
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FieldRules } from '../composables/useForm'
|
|
2
2
|
import type { OptionPrimitive, SelectOption } from './components'
|
|
3
|
+
import type { AccessControlled } from '../permissions'
|
|
3
4
|
|
|
4
5
|
// ---------------------------------------------------------------------------
|
|
5
6
|
// Field types
|
|
@@ -71,7 +72,7 @@ export interface FieldValidation {
|
|
|
71
72
|
// Field schema
|
|
72
73
|
// ---------------------------------------------------------------------------
|
|
73
74
|
|
|
74
|
-
export interface FormFieldSchema {
|
|
75
|
+
export interface FormFieldSchema extends AccessControlled {
|
|
75
76
|
name: string
|
|
76
77
|
label: string
|
|
77
78
|
type: FormFieldType
|
package/src/types/index.ts
CHANGED
|
@@ -43,11 +43,16 @@ export type {
|
|
|
43
43
|
WellExtendedData,
|
|
44
44
|
WellEditData,
|
|
45
45
|
WellEditField,
|
|
46
|
+
WellSampleDropData,
|
|
47
|
+
WellSampleDropContext,
|
|
48
|
+
WellSampleDropParser,
|
|
46
49
|
WellLegendItem,
|
|
47
50
|
PlateCondition,
|
|
48
51
|
ColumnCondition,
|
|
49
52
|
RowCondition,
|
|
50
53
|
Rack,
|
|
54
|
+
RackSampleDropContext,
|
|
55
|
+
RackSampleDropMapper,
|
|
51
56
|
// Sample Legend types
|
|
52
57
|
SampleType,
|
|
53
58
|
// Plate Map Editor types
|
|
@@ -111,6 +116,9 @@ export type {
|
|
|
111
116
|
SettingsModalLayout,
|
|
112
117
|
SettingsGroup,
|
|
113
118
|
SettingsModalSchema,
|
|
119
|
+
SettingsUserType,
|
|
120
|
+
SettingsAudience,
|
|
121
|
+
SettingsAudienceInput,
|
|
114
122
|
// ScientificNumber types
|
|
115
123
|
NumberNotation,
|
|
116
124
|
// UnitInput types
|
|
@@ -166,6 +174,33 @@ export type {
|
|
|
166
174
|
TreeNode,
|
|
167
175
|
} from './components'
|
|
168
176
|
|
|
177
|
+
export type {
|
|
178
|
+
InstrumentAlert,
|
|
179
|
+
InstrumentAlertBody,
|
|
180
|
+
InstrumentAlertLevel,
|
|
181
|
+
InstrumentState,
|
|
182
|
+
InstrumentStatus,
|
|
183
|
+
SampleInfo,
|
|
184
|
+
SampleInfo as InstrumentSampleInfo,
|
|
185
|
+
SequenceProgress,
|
|
186
|
+
} from './instrument'
|
|
187
|
+
|
|
188
|
+
export type {
|
|
189
|
+
LcmsContainerType,
|
|
190
|
+
LcmsPolarity,
|
|
191
|
+
LcmsSequenceItem,
|
|
192
|
+
LcmsSequenceTableColumn,
|
|
193
|
+
} from '../lcms'
|
|
194
|
+
|
|
195
|
+
export type {
|
|
196
|
+
AccessAudience,
|
|
197
|
+
AccessAudienceInput,
|
|
198
|
+
AccessPolicy,
|
|
199
|
+
AccessControlled,
|
|
200
|
+
PermissionUser,
|
|
201
|
+
RoleInfo,
|
|
202
|
+
} from '../permissions'
|
|
203
|
+
|
|
169
204
|
// FormBuilder types
|
|
170
205
|
export type {
|
|
171
206
|
FormFieldType,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export type InstrumentState = 'idle' | 'running' | 'standby' | 'connected' | 'disconnected' | 'error'
|
|
2
|
+
export type InstrumentAlertLevel = 'info' | 'warning' | 'critical'
|
|
3
|
+
|
|
4
|
+
export interface SampleInfo {
|
|
5
|
+
file_name: string
|
|
6
|
+
sample_id?: string | null
|
|
7
|
+
sample_name?: string | null
|
|
8
|
+
vial_position?: string | null
|
|
9
|
+
injection_volume?: number | null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SequenceProgress {
|
|
13
|
+
sequence_name?: string | null
|
|
14
|
+
current_sample: number
|
|
15
|
+
total_samples: number
|
|
16
|
+
elapsed_seconds?: number
|
|
17
|
+
estimated_remaining_seconds?: number | null
|
|
18
|
+
avg_sample_seconds?: number | null
|
|
19
|
+
current_sample_name?: string | null
|
|
20
|
+
current_method?: string | null
|
|
21
|
+
estimated_finish_time?: string | Date | null
|
|
22
|
+
sample_durations?: readonly number[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface InstrumentStatus {
|
|
26
|
+
instrument_id: string
|
|
27
|
+
instrument_name?: string | null
|
|
28
|
+
monitor_id?: string | null
|
|
29
|
+
state: InstrumentState
|
|
30
|
+
active_method?: string | null
|
|
31
|
+
current_sample?: SampleInfo | null
|
|
32
|
+
sequence_progress?: SequenceProgress | null
|
|
33
|
+
timestamp?: string | Date
|
|
34
|
+
last_seen?: string | Date | null
|
|
35
|
+
connected?: boolean
|
|
36
|
+
sequence_file?: string | null
|
|
37
|
+
sequence_row_index?: number | null
|
|
38
|
+
current_raw_file?: string | null
|
|
39
|
+
method_elapsed_pct?: number | null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface InstrumentAlertBody {
|
|
43
|
+
source?: string | null
|
|
44
|
+
event?: string | null
|
|
45
|
+
detail: string
|
|
46
|
+
code?: number | null
|
|
47
|
+
raw_file?: string | null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface InstrumentAlert {
|
|
51
|
+
id?: string
|
|
52
|
+
instrument_id: string
|
|
53
|
+
instrument_name?: string | null
|
|
54
|
+
level: InstrumentAlertLevel
|
|
55
|
+
rule?: string
|
|
56
|
+
message?: string | null
|
|
57
|
+
body?: InstrumentAlertBody | null
|
|
58
|
+
context?: Record<string, unknown>
|
|
59
|
+
timestamp?: string | Date
|
|
60
|
+
acknowledged?: boolean
|
|
61
|
+
}
|
package/src/types/platform.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { RoleInfo } from '../permissions'
|
|
2
|
+
|
|
1
3
|
// Plugin information provided by the platform
|
|
2
4
|
export interface PluginInfo {
|
|
3
5
|
id: string
|
|
@@ -75,6 +77,8 @@ export interface PlatformContext {
|
|
|
75
77
|
id: string
|
|
76
78
|
username: string
|
|
77
79
|
role: string
|
|
80
|
+
role_obj?: RoleInfo | null
|
|
81
|
+
roleObj?: RoleInfo | null
|
|
78
82
|
shortname?: string
|
|
79
83
|
}
|
|
80
84
|
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import type { Rack, SlotPosition, Well, WellEditData, WellPlateFormat } from '../types'
|
|
2
|
+
|
|
3
|
+
export type LcmsPlateType = '96well' | '54vial'
|
|
4
|
+
export type LcmsControlSampleType = 'blank' | 'qc' | 'iqc'
|
|
5
|
+
|
|
6
|
+
export interface LcmsPlateCell {
|
|
7
|
+
row: string
|
|
8
|
+
column: number
|
|
9
|
+
sample_name: string
|
|
10
|
+
sample_type?: string | null
|
|
11
|
+
injection_volume?: number | null
|
|
12
|
+
injection_count?: number | null
|
|
13
|
+
custom_method?: string | null
|
|
14
|
+
slot?: SlotPosition | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface LcmsPlateCellsToRackOptions {
|
|
18
|
+
rackId?: string
|
|
19
|
+
rackName?: string
|
|
20
|
+
format?: WellPlateFormat
|
|
21
|
+
plateType?: LcmsPlateType
|
|
22
|
+
slot?: SlotPosition
|
|
23
|
+
injectionVolume?: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface LcmsPlateCellsToRacksOptions extends Omit<LcmsPlateCellsToRackOptions, 'rackId' | 'rackName' | 'slot'> {
|
|
27
|
+
defaultSlot?: SlotPosition
|
|
28
|
+
rackId?: (slot: SlotPosition, index: number) => string
|
|
29
|
+
rackName?: (slot: SlotPosition, index: number) => string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const LCMS_DEFAULT_CONTROL_POSITIONS: Record<LcmsPlateType, Record<LcmsControlSampleType, { row: string; column: number }>> = {
|
|
33
|
+
'96well': {
|
|
34
|
+
blank: { row: 'H', column: 12 },
|
|
35
|
+
qc: { row: 'H', column: 11 },
|
|
36
|
+
iqc: { row: 'H', column: 10 },
|
|
37
|
+
},
|
|
38
|
+
'54vial': {
|
|
39
|
+
blank: { row: 'F', column: 9 },
|
|
40
|
+
qc: { row: 'F', column: 8 },
|
|
41
|
+
iqc: { row: 'F', column: 7 },
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const SLOT_ORDER: SlotPosition[] = ['R', 'G', 'B', 'Y']
|
|
46
|
+
|
|
47
|
+
export function lcmsPlateTypeToRackFormat(plateType: LcmsPlateType): WellPlateFormat {
|
|
48
|
+
return plateType === '96well' ? 96 : 54
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function rackFormatToLcmsPlateType(format: WellPlateFormat): LcmsPlateType {
|
|
52
|
+
if (format === 96) return '96well'
|
|
53
|
+
if (format === 54) return '54vial'
|
|
54
|
+
throw new Error(`LCMS plate cells support only 54-vial racks and 96-well plates; received format ${format}.`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function lcmsWellId(row: string, column: number): string {
|
|
58
|
+
return `${row.toUpperCase()}${column}`
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function parseLcmsWellId(wellId: string): { row: string; column: number } {
|
|
62
|
+
const match = /^([A-Za-z]+)(\d+)$/.exec(wellId.trim())
|
|
63
|
+
if (!match) {
|
|
64
|
+
throw new Error(`Invalid well id "${wellId}". Expected a row letter followed by a column number, e.g. A1.`)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
row: match[1].toUpperCase(),
|
|
69
|
+
column: Number(match[2]),
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function lcmsPlateCellsToRack(cells: LcmsPlateCell[], options: LcmsPlateCellsToRackOptions = {}): Rack {
|
|
74
|
+
const format = options.format ?? (options.plateType ? lcmsPlateTypeToRackFormat(options.plateType) : inferRackFormat(cells))
|
|
75
|
+
const slot = options.slot ?? cells.find(cell => cell.slot)?.slot ?? 'R'
|
|
76
|
+
const injectionVolume = options.injectionVolume ?? inferInjectionVolume(cells) ?? 5
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
id: options.rackId ?? `rack-${slot}`,
|
|
80
|
+
name: options.rackName ?? `Rack ${slot}`,
|
|
81
|
+
format,
|
|
82
|
+
slot,
|
|
83
|
+
injectionVolume,
|
|
84
|
+
wells: Object.fromEntries(
|
|
85
|
+
cells
|
|
86
|
+
.filter(cell => cell.sample_name.trim())
|
|
87
|
+
.map((cell) => {
|
|
88
|
+
const wellId = lcmsWellId(cell.row, cell.column)
|
|
89
|
+
const well: Partial<Well> = {
|
|
90
|
+
id: wellId,
|
|
91
|
+
row: rowIndex(cell.row),
|
|
92
|
+
col: cell.column - 1,
|
|
93
|
+
state: 'filled',
|
|
94
|
+
sampleType: cell.sample_type ?? 'sample',
|
|
95
|
+
metadata: {
|
|
96
|
+
label: cell.sample_name.trim(),
|
|
97
|
+
injectionVolume: cell.injection_volume ?? injectionVolume,
|
|
98
|
+
injectionCount: cell.injection_count ?? 1,
|
|
99
|
+
customMethod: cell.custom_method ?? null,
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
return [wellId, well]
|
|
103
|
+
}),
|
|
104
|
+
),
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function lcmsPlateCellsToRacks(cells: LcmsPlateCell[], options: LcmsPlateCellsToRacksOptions = {}): Rack[] {
|
|
109
|
+
const bySlot = new Map<SlotPosition, LcmsPlateCell[]>()
|
|
110
|
+
const defaultSlot = options.defaultSlot ?? 'R'
|
|
111
|
+
|
|
112
|
+
for (const cell of cells) {
|
|
113
|
+
const slot = cell.slot ?? defaultSlot
|
|
114
|
+
const slotCells = bySlot.get(slot) ?? []
|
|
115
|
+
slotCells.push(cell)
|
|
116
|
+
bySlot.set(slot, slotCells)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return Array.from(bySlot.entries())
|
|
120
|
+
.sort(([a], [b]) => SLOT_ORDER.indexOf(a) - SLOT_ORDER.indexOf(b))
|
|
121
|
+
.map(([slot, slotCells], index) => lcmsPlateCellsToRack(slotCells, {
|
|
122
|
+
...options,
|
|
123
|
+
slot,
|
|
124
|
+
rackId: options.rackId?.(slot, index) ?? `rack-${slot}`,
|
|
125
|
+
rackName: options.rackName?.(slot, index) ?? `Rack ${slot}`,
|
|
126
|
+
}))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function rackToLcmsPlateCells(rack: Rack): LcmsPlateCell[] {
|
|
130
|
+
return Object.entries(rack.wells)
|
|
131
|
+
.map(([wellId, well]) => wellToLcmsPlateCell(rack, wellId, well))
|
|
132
|
+
.filter((cell): cell is LcmsPlateCell => cell !== null)
|
|
133
|
+
.sort(comparePlateCells)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function racksToLcmsPlateCells(racks: Rack[]): LcmsPlateCell[] {
|
|
137
|
+
return racks.flatMap(rackToLcmsPlateCells)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function getLcmsDefaultControlWellId(plateType: LcmsPlateType, sampleType: LcmsControlSampleType): string {
|
|
141
|
+
const position = LCMS_DEFAULT_CONTROL_POSITIONS[plateType][sampleType]
|
|
142
|
+
return lcmsWellId(position.row, position.column)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function createLcmsControlWellEditData(
|
|
146
|
+
sampleType: LcmsControlSampleType,
|
|
147
|
+
options: {
|
|
148
|
+
wellId?: string
|
|
149
|
+
plateType?: LcmsPlateType
|
|
150
|
+
injectionVolume?: number
|
|
151
|
+
} = {},
|
|
152
|
+
): WellEditData {
|
|
153
|
+
const plateType = options.plateType ?? '54vial'
|
|
154
|
+
return {
|
|
155
|
+
wellId: options.wellId ?? getLcmsDefaultControlWellId(plateType, sampleType),
|
|
156
|
+
label: sampleType === 'blank' ? 'Blank' : sampleType.toUpperCase(),
|
|
157
|
+
sampleType,
|
|
158
|
+
injectionVolume: options.injectionVolume ?? 5,
|
|
159
|
+
injectionCount: 1,
|
|
160
|
+
customMethod: '',
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function wellToLcmsPlateCell(rack: Rack, wellId: string, well: Partial<Well>): LcmsPlateCell | null {
|
|
165
|
+
const metadata = well.metadata ?? {}
|
|
166
|
+
const label = getString(metadata.label)
|
|
167
|
+
if (!label) return null
|
|
168
|
+
|
|
169
|
+
const parsed = parseLcmsWellId(well.id ?? wellId)
|
|
170
|
+
return {
|
|
171
|
+
row: parsed.row,
|
|
172
|
+
column: parsed.column,
|
|
173
|
+
sample_name: label,
|
|
174
|
+
sample_type: well.sampleType ?? null,
|
|
175
|
+
injection_volume: getNumber(metadata.injectionVolume) ?? rack.injectionVolume,
|
|
176
|
+
injection_count: getNumber(metadata.injectionCount) ?? 1,
|
|
177
|
+
custom_method: getNullableString(metadata.customMethod),
|
|
178
|
+
slot: rack.slot,
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function inferRackFormat(cells: LcmsPlateCell[]): WellPlateFormat {
|
|
183
|
+
return cells.some(cell => rowIndex(cell.row) > 5 || cell.column > 9) ? 96 : 54
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function inferInjectionVolume(cells: LcmsPlateCell[]): number | undefined {
|
|
187
|
+
return cells.find(cell => typeof cell.injection_volume === 'number')?.injection_volume ?? undefined
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function rowIndex(row: string): number {
|
|
191
|
+
return row.toUpperCase().charCodeAt(0) - 65
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function getString(value: unknown): string | undefined {
|
|
195
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function getNullableString(value: unknown): string | null {
|
|
199
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function getNumber(value: unknown): number | undefined {
|
|
203
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function comparePlateCells(a: LcmsPlateCell, b: LcmsPlateCell): number {
|
|
207
|
+
const rowDiff = rowIndex(a.row) - rowIndex(b.row)
|
|
208
|
+
return rowDiff === 0 ? a.column - b.column : rowDiff
|
|
209
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-CBG3bWEc.js","names":[],"sources":["../src/stores/settings.ts","../src/stores/auth.ts"],"sourcesContent":["import { defineStore } from 'pinia'\nimport { ref, watch } from 'vue'\nimport type { ThemeMode, ColorPalette, TableDensity } from '../types'\n\ndeclare global {\n interface ImportMetaEnv {\n readonly VITE_API_PREFIX?: string\n }\n\n interface ImportMeta {\n readonly env: ImportMetaEnv\n }\n}\n\nexport interface SettingsState {\n serverHost: string\n serverPort: number\n requestTimeout: number\n wsAutoReconnect: boolean\n wsReconnectInterval: number\n theme: ThemeMode\n colorPalette: ColorPalette\n tableDensity: TableDensity\n}\n\nconst STORAGE_KEY = 'mint-settings'\nconst LEGACY_STORAGE_KEY = 'mld-settings'\n\nfunction getDefaultServerHost(): string {\n if (typeof window !== 'undefined' && window.location.hostname !== 'localhost') {\n return window.location.hostname\n }\n return 'localhost'\n}\n\nfunction getDefaultServerPort(): number {\n if (typeof window !== 'undefined') {\n if (window.location.port) {\n return parseInt(window.location.port, 10)\n }\n // Standard ports: 443 for HTTPS, 80 for HTTP (browser omits from location.port)\n return window.location.protocol === 'https:' ? 443 : 80\n }\n return 8000\n}\n\nconst defaultSettings: SettingsState = {\n serverHost: getDefaultServerHost(),\n serverPort: getDefaultServerPort(),\n requestTimeout: 120000,\n wsAutoReconnect: true,\n wsReconnectInterval: 5000,\n theme: 'system',\n colorPalette: 'default',\n tableDensity: 'normal',\n}\n\nexport const colorPalettes: Record<ColorPalette, { name: string; hues: [number, number] }> = {\n default: { name: 'Default (Cyan-Pink)', hues: [180, 320] },\n colorblind: { name: 'Colorblind-friendly', hues: [45, 260] },\n viridis: { name: 'Viridis', hues: [280, 80] },\n pastel: { name: 'Pastel', hues: [200, 340] },\n}\n\nfunction loadSettings(): SettingsState {\n try {\n const stored = localStorage.getItem(STORAGE_KEY)\n if (stored) {\n const parsed = JSON.parse(stored)\n return { ...defaultSettings, ...parsed }\n }\n\n const legacyStored = localStorage.getItem(LEGACY_STORAGE_KEY)\n if (legacyStored) {\n const parsed = JSON.parse(legacyStored)\n const migrated = { ...defaultSettings, ...parsed }\n localStorage.setItem(STORAGE_KEY, JSON.stringify(migrated))\n localStorage.removeItem(LEGACY_STORAGE_KEY)\n return migrated\n }\n } catch (e) {\n console.warn('Failed to load settings from localStorage:', e)\n }\n return { ...defaultSettings }\n}\n\nfunction saveSettings(settings: SettingsState): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(settings))\n } catch (e) {\n console.warn('Failed to save settings to localStorage:', e)\n }\n}\n\nfunction getSystemPrefersDark(): boolean {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false\n return window.matchMedia('(prefers-color-scheme: dark)').matches\n}\n\nexport const useSettingsStore = defineStore('mint-settings', () => {\n // State\n const serverHost = ref(defaultSettings.serverHost)\n const serverPort = ref(defaultSettings.serverPort)\n const requestTimeout = ref(defaultSettings.requestTimeout)\n const wsAutoReconnect = ref(defaultSettings.wsAutoReconnect)\n const wsReconnectInterval = ref(defaultSettings.wsReconnectInterval)\n const theme = ref<ThemeMode>(defaultSettings.theme)\n const systemPrefersDark = ref(getSystemPrefersDark())\n const colorPalette = ref<ColorPalette>(defaultSettings.colorPalette)\n const tableDensity = ref<TableDensity>(defaultSettings.tableDensity)\n\n // API prefix - can be configured via env variable VITE_API_PREFIX\n const apiPrefix: string = (import.meta.env?.VITE_API_PREFIX as string | undefined) ?? '/api'\n\n function _isSameOrigin(): { same: boolean; currentHost: string; currentPort: string } {\n const currentHost = typeof window !== 'undefined' ? window.location.hostname : 'localhost'\n const currentPort = typeof window !== 'undefined' ? window.location.port : '8000'\n const effectivePort = currentPort || (window.location.protocol === 'https:' ? '443' : '80')\n const same =\n (serverHost.value === currentHost && String(serverPort.value) === effectivePort) ||\n (serverHost.value === 'localhost' && currentHost !== 'localhost')\n return { same, currentHost, currentPort }\n }\n\n function getApiBaseUrl(): string {\n const { same } = _isSameOrigin()\n if (same) return apiPrefix\n return `http://${serverHost.value}:${serverPort.value}${apiPrefix}`\n }\n\n function getWsBaseUrl(): string {\n const { same, currentHost, currentPort } = _isSameOrigin()\n const protocol = typeof window !== 'undefined' && window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n if (same) return `${protocol}//${currentHost}${currentPort ? ':' + currentPort : ''}${apiPrefix}`\n return `ws://${serverHost.value}:${serverPort.value}${apiPrefix}`\n }\n\n let _initialized = false\n\n function initialize() {\n if (_initialized) return\n _initialized = true\n\n const loaded = loadSettings()\n serverHost.value = loaded.serverHost\n serverPort.value = loaded.serverPort\n requestTimeout.value = loaded.requestTimeout\n wsAutoReconnect.value = loaded.wsAutoReconnect\n wsReconnectInterval.value = loaded.wsReconnectInterval\n theme.value = loaded.theme\n colorPalette.value = loaded.colorPalette\n tableDensity.value = loaded.tableDensity\n\n applyTheme()\n }\n\n function applyTheme() {\n if (typeof document === 'undefined') return\n const dark = theme.value === 'system' ? systemPrefersDark.value : theme.value === 'dark'\n document.documentElement.classList.toggle('dark', dark)\n }\n\n watch(theme, () => {\n applyTheme()\n persistSettings()\n })\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => {\n systemPrefersDark.value = event.matches\n if (theme.value === 'system') {\n applyTheme()\n }\n })\n }\n\n function persistSettings() {\n saveSettings({\n serverHost: serverHost.value,\n serverPort: serverPort.value,\n requestTimeout: requestTimeout.value,\n wsAutoReconnect: wsAutoReconnect.value,\n wsReconnectInterval: wsReconnectInterval.value,\n theme: theme.value,\n colorPalette: colorPalette.value,\n tableDensity: tableDensity.value,\n })\n }\n\n watch([serverHost, serverPort, requestTimeout, wsAutoReconnect, wsReconnectInterval, colorPalette, tableDensity], () => {\n persistSettings()\n })\n\n function resetToDefaults() {\n serverHost.value = defaultSettings.serverHost\n serverPort.value = defaultSettings.serverPort\n requestTimeout.value = defaultSettings.requestTimeout\n wsAutoReconnect.value = defaultSettings.wsAutoReconnect\n wsReconnectInterval.value = defaultSettings.wsReconnectInterval\n theme.value = defaultSettings.theme\n colorPalette.value = defaultSettings.colorPalette\n tableDensity.value = defaultSettings.tableDensity\n }\n\n function getPaletteHues(): [number, number] {\n return colorPalettes[colorPalette.value].hues\n }\n\n function isDark(): boolean {\n return theme.value === 'system' ? systemPrefersDark.value : theme.value === 'dark'\n }\n\n return {\n serverHost,\n serverPort,\n requestTimeout,\n wsAutoReconnect,\n wsReconnectInterval,\n theme,\n systemPrefersDark,\n colorPalette,\n tableDensity,\n initialize,\n applyTheme,\n persistSettings,\n resetToDefaults,\n getPaletteHues,\n isDark,\n getApiBaseUrl,\n getWsBaseUrl,\n }\n})\n","import { defineStore } from 'pinia'\nimport { ref, computed } from 'vue'\nimport type { AuthConfig, UserInfo } from '../types'\n\nconst AUTH_TOKEN_KEY = 'mint-auth-token'\nconst AUTH_EXPIRES_KEY = 'mint-auth-expires'\n\nfunction getLocalStorage(): Storage | null {\n try {\n const storage = globalThis.localStorage\n return typeof storage?.getItem === 'function' ? storage : null\n } catch {\n return null\n }\n}\n\nfunction readStoredItem(key: string): string | null {\n try {\n return getLocalStorage()?.getItem(key) ?? null\n } catch {\n return null\n }\n}\n\nfunction writeStoredItem(key: string, value: string): void {\n try {\n getLocalStorage()?.setItem(key, value)\n } catch {\n // Keep auth usable in-memory when browser storage is blocked.\n }\n}\n\nfunction removeStoredItem(key: string): void {\n try {\n getLocalStorage()?.removeItem(key)\n } catch {\n // Keep auth cleanup usable in-memory when browser storage is blocked.\n }\n}\n\nexport const useAuthStore = defineStore('mint-auth', () => {\n // State\n const token = ref<string | null>(null)\n const tokenExpires = ref<Date | null>(null)\n const username = ref<string | null>(null)\n const userInfo = ref<UserInfo | null>(null)\n const authConfig = ref<AuthConfig>({\n authRequired: true,\n passkeyEnabled: false,\n passkeyRegistered: false,\n registrationEnabled: false,\n databaseMode: 'none',\n })\n const isInitialized = ref(false)\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n\n // Computed\n const isAuthenticated = computed(() => {\n if (!authConfig.value.authRequired) {\n return true\n }\n if (!token.value) {\n return false\n }\n if (tokenExpires.value && tokenExpires.value < new Date()) {\n return false\n }\n return true\n })\n\n const needsAuth = computed(() => {\n return authConfig.value.authRequired && !isAuthenticated.value\n })\n\n const isAdmin = computed(() => {\n return userInfo.value?.role === 'admin'\n })\n\n const canRegister = computed(() => {\n return authConfig.value.registrationEnabled\n })\n\n // Actions\n function initialize() {\n const storedToken = readStoredItem(AUTH_TOKEN_KEY)\n const storedExpires = readStoredItem(AUTH_EXPIRES_KEY)\n\n if (storedToken) {\n token.value = storedToken\n\n if (storedExpires) {\n const expires = new Date(storedExpires)\n if (expires > new Date()) {\n tokenExpires.value = expires\n } else {\n clearToken()\n }\n }\n }\n\n isInitialized.value = true\n }\n\n function setToken(accessToken: string, expiresIn: number) {\n token.value = accessToken\n const expires = new Date(Date.now() + expiresIn * 1000)\n tokenExpires.value = expires\n\n writeStoredItem(AUTH_TOKEN_KEY, accessToken)\n writeStoredItem(AUTH_EXPIRES_KEY, expires.toISOString())\n }\n\n function clearToken() {\n token.value = null\n tokenExpires.value = null\n username.value = null\n userInfo.value = null\n\n removeStoredItem(AUTH_TOKEN_KEY)\n removeStoredItem(AUTH_EXPIRES_KEY)\n }\n\n function setUserInfo(info: UserInfo) {\n userInfo.value = info\n username.value = info.username\n }\n\n function setAuthConfig(config: AuthConfig) {\n authConfig.value = config\n }\n\n function setUsername(name: string) {\n username.value = name\n }\n\n function setError(message: string | null) {\n error.value = message\n }\n\n function setLoading(loading: boolean) {\n isLoading.value = loading\n }\n\n function logout() {\n clearToken()\n }\n\n return {\n // State\n token,\n tokenExpires,\n username,\n userInfo,\n authConfig,\n isInitialized,\n isLoading,\n error,\n\n // Computed\n isAuthenticated,\n needsAuth,\n isAdmin,\n canRegister,\n\n // Actions\n initialize,\n setToken,\n clearToken,\n setAuthConfig,\n setUsername,\n setUserInfo,\n setError,\n setLoading,\n logout,\n }\n})\n"],"mappings":"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"}
|