@morscherlab/mint-sdk 1.0.0-beta.7 → 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__/composables/useProtocolTemplates.test.d.ts +1 -0
- package/dist/__tests__/stores/settings.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-QQj2kkze.js → auth-B7g4J4ZF.js} +148 -24
- 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 +8 -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-DihbSJjU.js → components-BhK-dW99.js} +2135 -1075
- 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-BcgZ6diz.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 +3186 -1070
- 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-Cyt0Suwf.js → templates-BorLR_7p.js} +324 -10
- 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-CM6Y0u5L.js → useProtocolTemplates-n6AJqSqv.js} +627 -380
- 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/GroupAssigner.test.ts +18 -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/useApi.test.ts +45 -0
- package/src/__tests__/composables/useAuth.test.ts +20 -0
- 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__/composables/useProtocolTemplates.test.ts +64 -0
- package/src/__tests__/stores/settings.test.ts +78 -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/AppAvatarMenu.vue +6 -3
- package/src/components/AppTopBar.vue +16 -10
- package/src/components/AuditTrail.vue +1 -1
- package/src/components/BaseTabs.vue +22 -1
- package/src/components/Calendar.vue +6 -2
- package/src/components/ConcentrationInput.vue +3 -2
- package/src/components/GroupAssigner.vue +8 -3
- 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/NumberInput.vue +5 -3
- package/src/components/ProgressBar.vue +3 -0
- package/src/components/RackEditor.vue +73 -2
- package/src/components/SampleHierarchyTree.vue +3 -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 +49 -2
- package/src/components/UnitInput.vue +6 -2
- package/src/components/WellPlate.vue +145 -24
- 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/useApi.ts +113 -16
- package/src/composables/useAuth.ts +4 -0
- package/src/composables/useAutoGroup.ts +18 -9
- package/src/composables/useControlSchema.ts +21 -0
- package/src/composables/useExperimentData.ts +57 -16
- package/src/composables/useExperimentSamples.ts +142 -0
- package/src/composables/useProtocolTemplates.ts +13 -1
- package/src/composables/useRackEditor.ts +3 -2
- 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 +79 -26
- package/src/stores/settings.ts +10 -0
- 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/settings-modal.css +9 -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-QQj2kkze.js.map +0 -1
- package/dist/components-DihbSJjU.js.map +0 -1
- package/dist/composables-BcgZ6diz.js.map +0 -1
- package/dist/templates-Cyt0Suwf.js.map +0 -1
- package/dist/useExperimentData-CM6Y0u5L.js.map +0 -1
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-QQj2kkze.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'\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 } 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\nconst hasLocalStorage = typeof localStorage !== 'undefined' && typeof localStorage.getItem === 'function'\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 if (hasLocalStorage) {\n const storedToken = localStorage.getItem(AUTH_TOKEN_KEY)\n const storedExpires = localStorage.getItem(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\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 if (hasLocalStorage) {\n localStorage.setItem(AUTH_TOKEN_KEY, accessToken)\n localStorage.setItem(AUTH_EXPIRES_KEY, expires.toISOString())\n }\n }\n\n function clearToken() {\n token.value = null\n tokenExpires.value = null\n username.value = null\n userInfo.value = null\n\n if (hasLocalStorage) {\n localStorage.removeItem(AUTH_TOKEN_KEY)\n localStorage.removeItem(AUTH_EXPIRES_KEY)\n }\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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"}
|