Skip to content

Commit 3910c1f

Browse files
committed
Import cross-type presets on selection
1 parent 06c9825 commit 3910c1f

4 files changed

Lines changed: 54 additions & 17 deletions

File tree

client/src/pages/printing/filamentQrCodeExportDialog.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ interface FilamentQRCodeExportDialogProps {
2525
filamentIds: number[];
2626
}
2727

28+
// Adapt filament records into the generic QR export dialog and keep export presets isolated from spool presets.
2829
const FilamentQRCodeExportDialog = ({ filamentIds }: FilamentQRCodeExportDialogProps) => {
2930
const t = useTranslate();
3031
const currentPresetType = "filament";
@@ -104,6 +105,7 @@ const FilamentQRCodeExportDialog = ({ filamentIds }: FilamentQRCodeExportDialogP
104105
const currentPresets = localCurrentPresets ?? remoteFilamentPresets;
105106
const otherPresets = remoteSpoolPresets ?? [];
106107

108+
// Keep edits local until the user explicitly saves so imported spool presets can be tried without immediate persistence.
107109
const savePresetsRemote = async () => {
108110
if (!localCurrentPresets) return;
109111
await setRemoteFilamentPresets(localCurrentPresets);
@@ -121,6 +123,7 @@ const FilamentQRCodeExportDialog = ({ filamentIds }: FilamentQRCodeExportDialogP
121123

122124
const promotePresetToCurrentType = (preset: SpoolQRCodePrintSettings): SpoolQRCodePrintSettings | undefined => {
123125
if (!currentPresets) return;
126+
// Imported spool presets become filament-owned copies immediately so later edits never touch the source preset.
124127
const baseName = (preset.labelSettings.printSettings?.name ?? defaultPresetName).trim() || defaultPresetName;
125128
const promotedName = getNextPresetName(`${baseName} ${importedPresetSuffix}`, currentPresets);
126129
const promotedPreset: SpoolQRCodePrintSettings = {
@@ -132,6 +135,7 @@ const FilamentQRCodeExportDialog = ({ filamentIds }: FilamentQRCodeExportDialogP
132135
return promotedPreset;
133136
};
134137

138+
// New presets derive from the currently selected settings object so export variants start from what the user sees.
135139
const addNewPreset = () => {
136140
if (!currentPresets) return;
137141
const newId = uuidv4();
@@ -143,6 +147,7 @@ const FilamentQRCodeExportDialog = ({ filamentIds }: FilamentQRCodeExportDialogP
143147
setSelectedPresetState(toPresetValue(currentPresetType, newId));
144148
return newPreset;
145149
};
150+
// Duplicates get a fresh id so the cloned export preset can diverge from its source immediately.
146151
const duplicateCurrentPreset = () => {
147152
if (!currentPresets) return;
148153
const newPreset = {
@@ -153,6 +158,7 @@ const FilamentQRCodeExportDialog = ({ filamentIds }: FilamentQRCodeExportDialogP
153158
setLocalCurrentPresets([...currentPresets, newPreset]);
154159
setSelectedPresetState(toPresetValue(currentPresetType, newPreset.labelSettings.printSettings.id));
155160
};
161+
// Edits to a spool-derived preset first promote it into the filament bucket before any persistence is possible.
156162
const updateCurrentPreset = (newSettings: SpoolQRCodePrintSettings) => {
157163
if (!currentPresets) return;
158164
const parsed = parsePresetValue(selectedPresetState);
@@ -186,6 +192,7 @@ const FilamentQRCodeExportDialog = ({ filamentIds }: FilamentQRCodeExportDialogP
186192

187193
let curPreset: SpoolQRCodePrintSettings;
188194
if (currentPresets === undefined) {
195+
// Use a temporary preset while settings load so the export dialog can render immediately.
189196
curPreset = {
190197
labelSettings: {
191198
printSettings: {
@@ -196,6 +203,7 @@ const FilamentQRCodeExportDialog = ({ filamentIds }: FilamentQRCodeExportDialogP
196203
};
197204
} else {
198205
if (currentPresets.length === 0) {
206+
// Seed the filament bucket with one editable preset the first time export settings are opened.
199207
const defaultId = uuidv4();
200208
const defaultPreset = buildNewPreset(defaultId, defaultPresetName);
201209
setLocalCurrentPresets([defaultPreset]);
@@ -291,6 +299,7 @@ const FilamentQRCodeExportDialog = ({ filamentIds }: FilamentQRCodeExportDialogP
291299
});
292300
}
293301

302+
// Expose both filament and vendor placeholders because the same tag picker drives label text and export filenames.
294303
const templateTags = [...filamentTags, ...vendorTags];
295304

296305
return (

client/src/pages/printing/filamentQrCodePrintingDialog.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ interface FilamentQRCodePrintingDialogProps {
2424
filamentIds: number[];
2525
}
2626

27+
// Adapt filament records into the generic QR print dialog and keep filament presets isolated from spool defaults.
2728
const FilamentQRCodePrintingDialog = ({ filamentIds }: FilamentQRCodePrintingDialogProps) => {
2829
const t = useTranslate();
2930
const currentPresetType = "filament";
@@ -96,11 +97,13 @@ const FilamentQRCodePrintingDialog = ({ filamentIds }: FilamentQRCodePrintingDia
9697
const currentPresets = localCurrentPresets ?? remoteCurrentPresets;
9798
const otherPresets = remoteOtherPresets ?? [];
9899

100+
// Keep edits local until the user explicitly saves so partially edited presets do not overwrite stored defaults.
99101
const savePresetsRemote = async () => {
100102
if (!localCurrentPresets) return;
101103
await setRemotePresets(localCurrentPresets);
102104
};
103105

106+
// New presets need an id immediately so the selector can switch to them before they are persisted.
104107
const addNewPreset = () => {
105108
if (!currentPresets) return;
106109
const newId = uuidv4();
@@ -109,6 +112,7 @@ const FilamentQRCodePrintingDialog = ({ filamentIds }: FilamentQRCodePrintingDia
109112
setSelectedPresetState(toPresetValue(currentPresetType, newId));
110113
return newPreset;
111114
};
115+
// Imported spool presets become filament-owned copies immediately so later edits never touch the source preset.
112116
const promotePresetToCurrentType = (preset: SpoolQRCodePrintSettings): SpoolQRCodePrintSettings | undefined => {
113117
if (!currentPresets) return;
114118
const baseName = (preset.labelSettings.printSettings?.name ?? defaultPresetName).trim() || defaultPresetName;
@@ -118,6 +122,7 @@ const FilamentQRCodePrintingDialog = ({ filamentIds }: FilamentQRCodePrintingDia
118122
setSelectedPresetState(toPresetValue(currentPresetType, promotedPreset.labelSettings.printSettings.id));
119123
return promotedPreset;
120124
};
125+
// Duplicates get a fresh id so later edits do not mutate the original preset in place.
121126
const duplicateCurrentPreset = () => {
122127
if (!currentPresets) return;
123128
const newPreset = {
@@ -128,6 +133,7 @@ const FilamentQRCodePrintingDialog = ({ filamentIds }: FilamentQRCodePrintingDia
128133
setLocalCurrentPresets([...currentPresets, newPreset]);
129134
setSelectedPresetState(toPresetValue(currentPresetType, newPreset.labelSettings.printSettings.id));
130135
};
136+
// Replace only the active preset inside the working copy shown by this dialog.
131137
const updateCurrentPreset = (newSettings: SpoolQRCodePrintSettings) => {
132138
if (!currentPresets) return;
133139
const parsed = parsePresetValue(selectedPresetState);
@@ -141,6 +147,7 @@ const FilamentQRCodePrintingDialog = ({ filamentIds }: FilamentQRCodePrintingDia
141147
),
142148
);
143149
};
150+
// Clearing the selection lets the fallback logic choose the next valid preset on the next render.
144151
const deleteCurrentPreset = () => {
145152
if (!currentPresets) return;
146153
const parsed = parsePresetValue(selectedPresetState);
@@ -151,6 +158,7 @@ const FilamentQRCodePrintingDialog = ({ filamentIds }: FilamentQRCodePrintingDia
151158

152159
let curPreset: SpoolQRCodePrintSettings;
153160
if (currentPresets === undefined) {
161+
// Use a temporary preset while settings are still loading so the dialog can render immediately.
154162
curPreset = {
155163
labelSettings: {
156164
printSettings: {
@@ -161,6 +169,7 @@ const FilamentQRCodePrintingDialog = ({ filamentIds }: FilamentQRCodePrintingDia
161169
};
162170
} else {
163171
if (currentPresets.length === 0) {
172+
// First-time users still need one editable preset so the dialog never opens into an empty state.
164173
const newSetting = addNewPreset();
165174
if (!newSetting) {
166175
console.error("Error adding new setting, this should never happen");
@@ -171,6 +180,7 @@ const FilamentQRCodePrintingDialog = ({ filamentIds }: FilamentQRCodePrintingDia
171180
} else {
172181
const parsedSelectedPreset = parsePresetValue(selectedPresetState);
173182
if (!parsedSelectedPreset) {
183+
// Default to the first saved preset until the user picks a different one.
174184
curPreset = currentPresets[0];
175185
setSelectedPresetState(toPresetValue(currentPresetType, currentPresets[0].labelSettings.printSettings.id));
176186
} else if (parsedSelectedPreset.type === otherPresetType) {
@@ -244,6 +254,7 @@ const FilamentQRCodePrintingDialog = ({ filamentIds }: FilamentQRCodePrintingDia
244254
});
245255
}
246256

257+
// Expose both filament and vendor placeholders because the same tag picker drives preview text and label templates.
247258
const templateTags = [...filamentTags, ...vendorTags];
248259

249260
return (

client/src/pages/printing/spoolQrCodeExportDialog.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ interface SpoolQRCodeExportDialog {
2525
spoolIds: number[];
2626
}
2727

28+
// Adapt spool records into the generic QR export dialog and keep export presets isolated from filament presets.
2829
const SpoolQRCodeExportDialog = ({ spoolIds }: SpoolQRCodeExportDialog) => {
2930
const t = useTranslate();
3031
const currentPresetType = "spool";
@@ -104,6 +105,7 @@ const SpoolQRCodeExportDialog = ({ spoolIds }: SpoolQRCodeExportDialog) => {
104105
const currentPresets = localCurrentPresets ?? remoteSpoolPresets;
105106
const otherPresets = remoteFilamentPresets ?? [];
106107

108+
// Keep edits local until the user explicitly saves so imported filament presets can be tried without persistence.
107109
const savePresetsRemote = async () => {
108110
if (!localCurrentPresets) return;
109111
await setRemoteSpoolPresets(localCurrentPresets);
@@ -121,6 +123,7 @@ const SpoolQRCodeExportDialog = ({ spoolIds }: SpoolQRCodeExportDialog) => {
121123

122124
const promotePresetToCurrentType = (preset: SpoolQRCodePrintSettings): SpoolQRCodePrintSettings | undefined => {
123125
if (!currentPresets) return;
126+
// Imported filament presets become spool-owned copies immediately so later edits never touch the source preset.
124127
const baseName = (preset.labelSettings.printSettings?.name ?? defaultPresetName).trim() || defaultPresetName;
125128
const promotedName = getNextPresetName(`${baseName} ${importedPresetSuffix}`, currentPresets);
126129
const promotedPreset: SpoolQRCodePrintSettings = {
@@ -132,6 +135,7 @@ const SpoolQRCodeExportDialog = ({ spoolIds }: SpoolQRCodeExportDialog) => {
132135
return promotedPreset;
133136
};
134137

138+
// New presets derive from the currently selected settings object so export variants start from what the user sees.
135139
const addNewPreset = () => {
136140
if (!currentPresets) return;
137141
const newId = uuidv4();
@@ -143,6 +147,7 @@ const SpoolQRCodeExportDialog = ({ spoolIds }: SpoolQRCodeExportDialog) => {
143147
setSelectedPresetState(toPresetValue(currentPresetType, newId));
144148
return newPreset;
145149
};
150+
// Duplicates get a fresh id so the cloned export preset can diverge from its source immediately.
146151
const duplicateCurrentPreset = () => {
147152
if (!currentPresets) return;
148153
const newPreset = {
@@ -153,6 +158,7 @@ const SpoolQRCodeExportDialog = ({ spoolIds }: SpoolQRCodeExportDialog) => {
153158
setLocalCurrentPresets([...currentPresets, newPreset]);
154159
setSelectedPresetState(toPresetValue(currentPresetType, newPreset.labelSettings.printSettings.id));
155160
};
161+
// Edits to a filament-derived preset first promote it into the spool bucket before any persistence is possible.
156162
const updateCurrentPreset = (newSettings: SpoolQRCodePrintSettings) => {
157163
if (!currentPresets) return;
158164
const parsed = parsePresetValue(selectedPresetState);
@@ -186,6 +192,7 @@ const SpoolQRCodeExportDialog = ({ spoolIds }: SpoolQRCodeExportDialog) => {
186192

187193
let curPreset: SpoolQRCodePrintSettings;
188194
if (currentPresets === undefined) {
195+
// Use a temporary preset while settings load so the export dialog can render immediately.
189196
curPreset = {
190197
labelSettings: {
191198
printSettings: {
@@ -196,6 +203,7 @@ const SpoolQRCodeExportDialog = ({ spoolIds }: SpoolQRCodeExportDialog) => {
196203
};
197204
} else {
198205
if (currentPresets.length === 0) {
206+
// Seed the spool bucket with one editable preset the first time export settings are opened.
199207
const defaultId = uuidv4();
200208
const defaultPreset = buildNewPreset(defaultId, defaultPresetName);
201209
setLocalCurrentPresets([defaultPreset]);
@@ -314,6 +322,7 @@ Spool Weight: {filament.spool_weight} g
314322
});
315323
}
316324

325+
// Expose spool, filament, and vendor placeholders because the same tag picker drives label text and export filenames.
317326
const templateTags = [...spoolTags, ...filamentTags, ...vendorTags];
318327

319328
return (

client/src/pages/printing/spoolQrCodePrintingDialog.tsx

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ interface SpoolQRCodePrintingDialog {
2424
spoolIds: number[];
2525
}
2626

27+
// Adapt spool records into the generic QR print dialog while keeping spool presets isolated from export-specific buckets.
2728
const SpoolQRCodePrintingDialog = ({ spoolIds }: SpoolQRCodePrintingDialog) => {
2829
const t = useTranslate();
2930
const currentPresetType = "spool";
@@ -83,11 +84,10 @@ const SpoolQRCodePrintingDialog = ({ spoolIds }: SpoolQRCodePrintingDialog) => {
8384
})
8485
.filter((item) => item !== null) as ISpool[];
8586

86-
// Selected preset state
87+
// Selected preset state.
8788
const [selectedPresetState, setSelectedPresetState] = useSavedState<string | undefined>("selectedPreset", undefined);
8889

89-
// Keep a local copy of the settings which is what's actually displayed. Use the remote state only for saving.
90-
// This decouples the debounce stuff from the UI
90+
// Keep edits local until the user explicitly saves so partially edited presets do not overwrite stored defaults.
9191
const [localCurrentPresets, setLocalCurrentPresets] = useState<SpoolQRCodePrintSettings[] | undefined>();
9292
const remoteCurrentPresets = useGetPrintPresets();
9393
const remoteOtherPresets = useGetPrintPresets("print_presets_filament");
@@ -96,12 +96,13 @@ const SpoolQRCodePrintingDialog = ({ spoolIds }: SpoolQRCodePrintingDialog) => {
9696
const currentPresets = localCurrentPresets ?? remoteCurrentPresets;
9797
const otherPresets = remoteOtherPresets ?? [];
9898

99-
const savePresetsRemote = () => {
99+
const savePresetsRemote = async () => {
100100
if (!localCurrentPresets) return;
101-
setRemotePresets(localCurrentPresets);
101+
await setRemotePresets(localCurrentPresets);
102102
};
103103

104-
// Functions to update settings
104+
// Functions to update settings.
105+
// New presets need an id immediately so the selector can switch to them before they are persisted.
105106
const addNewPreset = () => {
106107
if (!currentPresets) return;
107108
const newId = uuidv4();
@@ -110,6 +111,7 @@ const SpoolQRCodePrintingDialog = ({ spoolIds }: SpoolQRCodePrintingDialog) => {
110111
setSelectedPresetState(toPresetValue(currentPresetType, newId));
111112
return newPreset;
112113
};
114+
// Imported filament presets become spool-owned copies immediately so later edits never touch the source preset.
113115
const promotePresetToCurrentType = (preset: SpoolQRCodePrintSettings): SpoolQRCodePrintSettings | undefined => {
114116
if (!currentPresets) return;
115117
const baseName = (preset.labelSettings.printSettings?.name ?? defaultPresetName).trim() || defaultPresetName;
@@ -119,6 +121,7 @@ const SpoolQRCodePrintingDialog = ({ spoolIds }: SpoolQRCodePrintingDialog) => {
119121
setSelectedPresetState(toPresetValue(currentPresetType, promotedPreset.labelSettings.printSettings.id));
120122
return promotedPreset;
121123
};
124+
// Duplicates get a fresh id so later edits do not mutate the original preset in place.
122125
const duplicateCurrentPreset = () => {
123126
if (!currentPresets) return;
124127
const newPreset = {
@@ -129,6 +132,7 @@ const SpoolQRCodePrintingDialog = ({ spoolIds }: SpoolQRCodePrintingDialog) => {
129132
setLocalCurrentPresets([...currentPresets, newPreset]);
130133
setSelectedPresetState(toPresetValue(currentPresetType, newPreset.labelSettings.printSettings.id));
131134
};
135+
// Replace only the active preset inside the working copy shown by this dialog.
132136
const updateCurrentPreset = (newSettings: SpoolQRCodePrintSettings) => {
133137
if (!currentPresets) return;
134138
const parsed = parsePresetValue(selectedPresetState);
@@ -142,6 +146,7 @@ const SpoolQRCodePrintingDialog = ({ spoolIds }: SpoolQRCodePrintingDialog) => {
142146
),
143147
);
144148
};
149+
// Clearing the selection lets the fallback logic choose the next valid preset on the next render.
145150
const deleteCurrentPreset = () => {
146151
if (!currentPresets) return;
147152
const parsed = parsePresetValue(selectedPresetState);
@@ -150,10 +155,10 @@ const SpoolQRCodePrintingDialog = ({ spoolIds }: SpoolQRCodePrintingDialog) => {
150155
setSelectedPresetState(undefined);
151156
};
152157

153-
// Initialize presets
158+
// Initialize presets.
154159
let curPreset: SpoolQRCodePrintSettings;
155160
if (currentPresets === undefined) {
156-
// DB not loaded yet, use a temporary one
161+
// Use a temporary preset while settings are still loading so the dialog can render immediately.
157162
curPreset = {
158163
labelSettings: {
159164
printSettings: {
@@ -163,36 +168,34 @@ const SpoolQRCodePrintingDialog = ({ spoolIds }: SpoolQRCodePrintingDialog) => {
163168
},
164169
};
165170
} else {
166-
// DB is loaded, find the selected setting
167171
if (currentPresets.length === 0) {
168-
// DB loaded, but no settings found, add a new one and select it
172+
// First-time users still need one editable preset so the dialog never opens into an empty state.
169173
const newSetting = addNewPreset();
170174
if (!newSetting) {
171175
console.error("Error adding new setting, this should never happen");
172176
return;
173177
}
174178

175-
// Mutate the allPrintSettings list so that the rest of the UI will work fine
179+
// The rest of this render expects the working list to already contain the preset it should show.
176180
currentPresets.push(newSetting);
177181
curPreset = newSetting;
178182
} else {
179183
const parsedSelectedPreset = parsePresetValue(selectedPresetState);
180184
if (!parsedSelectedPreset) {
181-
// No setting has been selected, select the first one
185+
// Default to the first saved preset until the user picks a different one.
182186
curPreset = currentPresets[0];
183187
setSelectedPresetState(toPresetValue(currentPresetType, currentPresets[0].labelSettings.printSettings.id));
184188
} else if (parsedSelectedPreset.type === otherPresetType) {
185189
curPreset = currentPresets[0];
186190
setSelectedPresetState(toPresetValue(currentPresetType, currentPresets[0].labelSettings.printSettings.id));
187191
} else {
188-
// A setting has been selected, find it
189192
const foundSetting = currentPresets.find(
190193
(settings) => settings.labelSettings.printSettings.id === parsedSelectedPreset.id,
191194
);
192195
if (foundSetting) {
193196
curPreset = foundSetting;
194197
} else {
195-
// Selected setting not found, reset to first available preset.
198+
// Fall back to the first saved preset when the remembered selection no longer exists.
196199
curPreset = currentPresets[0];
197200
setSelectedPresetState(toPresetValue(currentPresetType, currentPresets[0].labelSettings.printSettings.id));
198201
}
@@ -277,6 +280,7 @@ Spool Weight: {filament.spool_weight} g
277280
});
278281
}
279282

283+
// Expose spool, filament, and vendor placeholders because the same tag picker drives preview text and labels.
280284
const templateTags = [...spoolTags, ...filamentTags, ...vendorTags];
281285

282286
return (
@@ -439,9 +443,13 @@ Spool Weight: {filament.spool_weight} g
439443
type="primary"
440444
size="large"
441445
icon={<SaveOutlined />}
442-
onClick={() => {
443-
savePresetsRemote();
444-
messageApi.success(t("notifications.saveSuccessful"));
446+
onClick={async () => {
447+
try {
448+
await savePresetsRemote();
449+
messageApi.success(t("notifications.saveSuccessful"));
450+
} catch (error) {
451+
messageApi.error(error instanceof Error ? error.message : "Save failed");
452+
}
445453
}}
446454
>
447455
{t("printing.generic.saveSetting")}

0 commit comments

Comments
 (0)