fix(ui): error-handle fire-and-forget mutations in settings panels
This commit is contained in:
@@ -285,10 +285,18 @@ export const AiProviderProfilesPanel = () => {
|
||||
<Select
|
||||
value={defaultProfile?._id ?? ''}
|
||||
onValueChange={async (value) => {
|
||||
await setDefaultProfile({
|
||||
profileId: value as Id<'aiProviderProfiles'>,
|
||||
});
|
||||
toast.success('Default AI provider updated.');
|
||||
try {
|
||||
await setDefaultProfile({
|
||||
profileId: value as Id<'aiProviderProfiles'>,
|
||||
});
|
||||
toast.success('Default AI provider updated.');
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Could not update default AI provider.',
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
@@ -332,8 +340,16 @@ export const AiProviderProfilesPanel = () => {
|
||||
size='icon'
|
||||
aria-label='Remove provider'
|
||||
onClick={async () => {
|
||||
await removeProfile({ profileId: profile._id });
|
||||
toast.success('AI provider removed.');
|
||||
try {
|
||||
await removeProfile({ profileId: profile._id });
|
||||
toast.success('AI provider removed.');
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Could not remove AI provider.',
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Trash2 className='size-4' />
|
||||
|
||||
@@ -160,13 +160,17 @@ export const DotfilesManager = () => {
|
||||
|
||||
const saveSelected = async (next: string) => {
|
||||
if (!selected) return;
|
||||
await putFile({
|
||||
path: selected.path,
|
||||
content: next,
|
||||
isExecutable: selected.isExecutable,
|
||||
});
|
||||
setSavedContent(next);
|
||||
toast.success('Saved.');
|
||||
try {
|
||||
await putFile({
|
||||
path: selected.path,
|
||||
content: next,
|
||||
isExecutable: selected.isExecutable,
|
||||
});
|
||||
setSavedContent(next);
|
||||
toast.success('Saved.');
|
||||
} catch (error) {
|
||||
toast.error(error instanceof Error ? error.message : 'Save failed.');
|
||||
}
|
||||
};
|
||||
|
||||
const importAll = async (incoming: UploadFile[]) => {
|
||||
@@ -231,11 +235,15 @@ export const DotfilesManager = () => {
|
||||
|
||||
const deleteSelected = async () => {
|
||||
if (!selected) return;
|
||||
await removeFile({ fileId: selected._id as never });
|
||||
setSelected(undefined);
|
||||
setContent('');
|
||||
setSavedContent('');
|
||||
toast.success('Deleted.');
|
||||
try {
|
||||
await removeFile({ fileId: selected._id as never });
|
||||
setSelected(undefined);
|
||||
setContent('');
|
||||
setSavedContent('');
|
||||
toast.success('Deleted.');
|
||||
} catch (error) {
|
||||
toast.error(error instanceof Error ? error.message : 'Delete failed.');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -345,8 +353,12 @@ export const DotfilesManager = () => {
|
||||
<RepoPanel
|
||||
settings={settings}
|
||||
onSave={async (values) => {
|
||||
await updateEnv(values);
|
||||
toast.success('Saved.');
|
||||
try {
|
||||
await updateEnv(values);
|
||||
toast.success('Saved.');
|
||||
} catch (error) {
|
||||
toast.error(error instanceof Error ? error.message : 'Save failed.');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -223,8 +223,16 @@ export const SpoonClonePanel = ({ spoon }: { spoon: Doc<'spoons'> }) => {
|
||||
remoteName={remote.remoteName}
|
||||
url={remote.url}
|
||||
onRemove={async () => {
|
||||
await removeRemote({ remoteId: remote._id });
|
||||
toast.success('Remote removed.');
|
||||
try {
|
||||
await removeRemote({ remoteId: remote._id });
|
||||
toast.success('Remote removed.');
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Could not remove remote.',
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -130,24 +130,35 @@ export const SpoonSecretsForm = ({ spoonId }: { spoonId: Id<'spoons'> }) => {
|
||||
return;
|
||||
}
|
||||
setBulkSaving(true);
|
||||
try {
|
||||
for (const secret of parsedBulk.secrets) {
|
||||
let imported = 0;
|
||||
const failed: string[] = [];
|
||||
for (const secret of parsedBulk.secrets) {
|
||||
try {
|
||||
await createSecret({
|
||||
spoonId,
|
||||
name: secret.name,
|
||||
value: secret.value,
|
||||
description: 'Imported from .env',
|
||||
});
|
||||
imported += 1;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
failed.push(secret.name);
|
||||
}
|
||||
setBulkText('');
|
||||
setBulkDialogOpen(false);
|
||||
toast.success(`${parsedBulk.secrets.length} secrets imported.`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error('Could not import secrets.');
|
||||
} finally {
|
||||
setBulkSaving(false);
|
||||
}
|
||||
setBulkSaving(false);
|
||||
|
||||
if (imported > 0) {
|
||||
toast.success(`${imported} secret${imported === 1 ? '' : 's'} imported.`);
|
||||
}
|
||||
if (failed.length) {
|
||||
toast.error(
|
||||
`Could not import ${failed.length} secret${failed.length === 1 ? '' : 's'}: ${failed.join(', ')}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
setBulkText('');
|
||||
setBulkDialogOpen(false);
|
||||
};
|
||||
|
||||
const readEnvFile = async (file?: File) => {
|
||||
@@ -310,8 +321,16 @@ export const SpoonSecretsForm = ({ spoonId }: { spoonId: Id<'spoons'> }) => {
|
||||
size='icon'
|
||||
aria-label={`Remove ${secret.name}`}
|
||||
onClick={async () => {
|
||||
await removeSecret({ secretId: secret._id });
|
||||
toast.success('Secret removed.');
|
||||
try {
|
||||
await removeSecret({ secretId: secret._id });
|
||||
toast.success('Secret removed.');
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Could not remove secret.',
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Trash2 className='size-4' />
|
||||
|
||||
Reference in New Issue
Block a user