Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .claude/skills/react/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ a11y, Storybook), read [`ui-components`](../ui-components/SKILL.md) instead.
native `.withOptimisticUpdate` (auto-rolls-back on settle/error); a local `useState` copy
double-sources the truth and drifts. Set `errorToast` (or `false` when the caller shows its own
toast).
- **Toasts carry a concise `title` + `description`.** A toast is a short headline plus a precise
follow-up sentence, not one crammed line — `toast({ title, description, variant })`. Shared copy
lives under the `toast` namespace as `{ title, description }` pairs (e.g.
`tToast('error.saveFailed.title')` / `tToast('error.saveFailed.description')`); feature-local toasts
follow the same shape. Omit `description` only when the title already says everything.
- **Loading = `<Skeletonize loading>` masking the real tree, never a parallel mock.** Render the
component _once_ inside
[`<Skeletonize loading>`](../../../packages/ui/src/components/feedback/skeleton-context.tsx) (from
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ interface EntityDeleteTranslations {
warningText?: string;
/** Success toast message */
successMessage: string;
/** Success toast description (optional) */
successDescription?: string;
/** Error toast message */
errorMessage: string;
}
Expand Down Expand Up @@ -80,6 +82,7 @@ export function EntityDeleteDialog<TEntity>({
await deleteMutation(entity);
toast({
title: translations.successMessage,
description: translations.successDescription,
});
onClose();
onSuccess?.();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ interface DeleteDialogTranslations {
description: string;
warningText?: string;
successMessage: string;
successDescription?: string;
errorMessage: string;
}

Expand Down Expand Up @@ -126,7 +127,8 @@ export function useDeleteDialogTranslations(
title: tEntity(keys.title),
description: tEntity(keys.description, { name: '{name}' }),
warningText: keys.warningText ? tEntity(keys.warningText) : undefined,
successMessage: tToast('success.deleted'),
successMessage: tToast('success.deleted.title'),
successDescription: tToast('success.deleted.description'),
errorMessage: tEntity(keys.errorMessage),
}),
[tEntity, tToast, keys],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,16 @@ export function AutomationRowActions({
duplicateAutomation(workflowArgs, {
onSuccess: () => {
toast({
title: tToast('success.automationDuplicated'),
title: tToast('success.automationDuplicated.title'),
description: tToast('success.automationDuplicated.description'),
variant: 'success',
});
},
onError: (error: Error) => {
console.error('Failed to duplicate automation:', error);
toast({
title: tToast('error.automationDuplicateFailed'),
title: tToast('error.automationDuplicateFailed.title'),
description: tToast('error.automationDuplicateFailed.description'),
variant: 'destructive',
});
},
Expand All @@ -78,7 +80,8 @@ export function AutomationRowActions({
newSlug: name,
});
toast({
title: tToast('success.automationRenamed'),
title: tToast('success.automationRenamed.title'),
description: tToast('success.automationRenamed.description'),
variant: 'success',
});
} catch (error: unknown) {
Expand All @@ -101,7 +104,8 @@ export function AutomationRowActions({
onError: (error: Error) => {
console.error('Failed to delete automation:', error);
toast({
title: tToast('error.automationDeleteFailed'),
title: tToast('error.automationDeleteFailed.title'),
description: tToast('error.automationDeleteFailed.description'),
variant: 'destructive',
});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,14 @@ function ProfileSection() {
try {
await updateUserName({ name });
toast({
title: tToast('success.profileUpdated'),
title: tToast('success.profileUpdated.title'),
description: tToast('success.profileUpdated.description'),
variant: 'success',
});
} catch (err) {
toast({
title: tToast('error.profileUpdateFailed'),
title: tToast('error.profileUpdateFailed.title'),
description: tToast('error.profileUpdateFailed.description'),
variant: 'destructive',
});
throw err;
Expand Down Expand Up @@ -318,7 +320,8 @@ function ChangePasswordDialog({ open, onOpenChange }: PasswordDialogProps) {
});
} catch {
toast({
title: tToast('error.passwordChangeFailed'),
title: tToast('error.passwordChangeFailed.title'),
description: tToast('error.passwordChangeFailed.description'),
variant: 'destructive',
});
return;
Expand Down Expand Up @@ -462,15 +465,17 @@ function SetPasswordDialog({ open, onOpenChange }: PasswordDialogProps) {
});

toast({
title: tToast('success.passwordSet'),
title: tToast('success.passwordSet.title'),
description: tToast('success.passwordSet.description'),
variant: 'success',
});

reset();
onOpenChange(false);
} catch {
toast({
title: tToast('error.passwordChangeFailed'),
title: tToast('error.passwordChangeFailed.title'),
description: tToast('error.passwordChangeFailed.description'),
variant: 'destructive',
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,14 @@ export function BrandingForm({
onSaved?.();
void refetchBranding();
toast({
title: tToast('success.brandingUpdated'),
title: tToast('success.brandingUpdated.title'),
description: tToast('success.brandingUpdated.description'),
variant: 'success',
});
} catch (err) {
toast({
title: tToast('error.brandingUpdateFailed'),
title: tToast('error.brandingUpdateFailed.title'),
description: tToast('error.brandingUpdateFailed.description'),
variant: 'destructive',
});
throw err;
Expand Down Expand Up @@ -207,7 +209,8 @@ export function BrandingForm({
setValue('faviconLightFilename', filename, { shouldDirty: true });
setFaviconPreviewUrl(`data:image/png;base64,${base64}`);
toast({
title: tToast('success.faviconGenerated'),
title: tToast('success.faviconGenerated.title'),
description: tToast('success.faviconGenerated.description'),
variant: 'success',
});
} catch (err) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,11 @@ export function AddMemberDialog({

toast({
title: result.isExistingUser
? tToast('success.existingUserAdded')
: tToast('success.newMemberCreated'),
? tToast('success.existingUserAdded.title')
: tToast('success.newMemberCreated.title'),
description: result.isExistingUser
? tToast('success.existingUserAdded.description')
: tToast('success.newMemberCreated.description'),
variant: 'success',
});

Expand Down Expand Up @@ -154,7 +157,8 @@ export function AddMemberDialog({
return;
}
toast({
title: tToast('error.addMemberFailed'),
title: tToast('error.addMemberFailed.title'),
description: tToast('error.addMemberFailed.description'),
variant: 'destructive',
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,13 +342,15 @@ export function OrganizationSettings({
});
await queryClient.invalidateQueries({ queryKey: ['auth', 'session'] });
toast({
title: tToast('success.organizationUpdated'),
title: tToast('success.organizationUpdated.title'),
description: tToast('success.organizationUpdated.description'),
variant: 'success',
});
} catch (error) {
console.error(error);
toast({
title: tToast('error.organizationUpdateFailed'),
title: tToast('error.organizationUpdateFailed.title'),
description: tToast('error.organizationUpdateFailed.description'),
variant: 'destructive',
});
throw error;
Expand Down
5 changes: 3 additions & 2 deletions services/platform/app/hooks/use-convex-mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ export function useConvexMutation<Func extends FunctionReference<'mutation'>>(
console.error(`Mutation failed: ${getFunctionName(func)}`, error);
if (errorToast !== false) {
toast({
title: errorToast?.title ?? t('error.generic'),
description: errorToast?.description?.(error),
title: errorToast?.title ?? t('error.generic.title'),
description:
errorToast?.description?.(error) ?? t('error.generic.description'),
variant: 'destructive',
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,18 @@ function ConfigurationPage() {
readResult && readResult.ok ? readResult.hash : undefined,
});
await refetch();
toast({ title: tToast('success.saved'), variant: 'success' });
toast({
title: tToast('success.saved.title'),
description: tToast('success.saved.description'),
variant: 'success',
});
} catch (error) {
console.error('Failed to save configuration:', error);
toast({ title: tToast('error.saveFailed'), variant: 'destructive' });
toast({
title: tToast('error.saveFailed.title'),
description: tToast('error.saveFailed.description'),
variant: 'destructive',
});
throw error;
}
},
Expand Down
6 changes: 4 additions & 2 deletions services/platform/app/routes/forced-change-password.$id.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ function ForcedChangePasswordPage() {
trigger: 'forced',
});
toast({
title: tToast('success.passwordChanged'),
title: tToast('success.passwordChanged.title'),
description: tToast('success.passwordChanged.description'),
variant: 'success',
});
void navigate({
Expand All @@ -140,7 +141,8 @@ function ForcedChangePasswordPage() {
} catch (e) {
console.error(e);
toast({
title: tToast('error.passwordChangeFailed'),
title: tToast('error.passwordChangeFailed.title'),
description: tToast('error.passwordChangeFailed.description'),
variant: 'destructive',
});
}
Expand Down
105 changes: 84 additions & 21 deletions services/platform/messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -6864,29 +6864,92 @@
},
"toast": {
"success": {
"saved": "Änderungen gespeichert",
"deleted": "Gelöscht",
"profileUpdated": "Profil aktualisiert",
"passwordChanged": "Passwort geändert",
"passwordSet": "Passwort festgelegt",
"organizationUpdated": "Organisation aktualisiert",
"newMemberCreated": "Neues Mitglied erstellt und zur Organisation hinzugefügt",
"existingUserAdded": "Bestehender Benutzer zur Organisation hinzugefügt",
"automationDuplicated": "Automatisierung dupliziert",
"automationRenamed": "Automatisierung umbenannt",
"brandingUpdated": "Branding aktualisiert",
"faviconGenerated": "Favicon aus deinem Logo erzeugt"
"saved": {
"title": "Änderungen gespeichert",
"description": "Deine Änderungen wurden gespeichert."
},
"deleted": {
"title": "Gelöscht",
"description": "Das Element wurde gelöscht."
},
"profileUpdated": {
"title": "Profil aktualisiert",
"description": "Deine Profiländerungen wurden gespeichert."
},
"passwordChanged": {
"title": "Passwort geändert",
"description": "Verwende dein neues Passwort bei der nächsten Anmeldung."
},
"passwordSet": {
"title": "Passwort festgelegt",
"description": "Du kannst dich jetzt mit deinem Passwort anmelden."
},
"organizationUpdated": {
"title": "Organisation aktualisiert",
"description": "Deine Änderungen an der Organisation wurden gespeichert."
},
"newMemberCreated": {
"title": "Mitglied hinzugefügt",
"description": "Das neue Mitglied wurde erstellt und zu deiner Organisation hinzugefügt."
},
"existingUserAdded": {
"title": "Mitglied hinzugefügt",
"description": "Der Benutzer wurde zu deiner Organisation hinzugefügt."
},
"automationDuplicated": {
"title": "Automatisierung dupliziert",
"description": "Eine Kopie der Automatisierung wurde erstellt."
},
"automationRenamed": {
"title": "Automatisierung umbenannt",
"description": "Der neue Name wurde gespeichert."
},
"brandingUpdated": {
"title": "Branding aktualisiert",
"description": "Deine Branding-Änderungen wurden gespeichert."
},
"faviconGenerated": {
"title": "Favicon erzeugt",
"description": "Wir haben aus deinem Logo ein Favicon erstellt."
}
},
"error": {
"saveFailed": "Änderungen konnten nicht gespeichert werden",
"profileUpdateFailed": "Profil konnte nicht aktualisiert werden",
"passwordChangeFailed": "Passwort konnte nicht geändert werden. Bitte überprüfe dein aktuelles Passwort und versuche es erneut.",
"organizationUpdateFailed": "Organisation konnte nicht aktualisiert werden",
"brandingUpdateFailed": "Branding konnte nicht aktualisiert werden",
"addMemberFailed": "Mitglied konnte nicht hinzugefügt werden",
"automationDuplicateFailed": "Automatisierung konnte nicht dupliziert werden",
"automationDeleteFailed": "Automatisierung konnte nicht gelöscht werden",
"generic": "Etwas ist schiefgelaufen. Bitte versuche es erneut."
"saveFailed": {
"title": "Änderungen konnten nicht gespeichert werden",
"description": "Etwas ist schiefgelaufen. Bitte versuche es erneut."
},
"profileUpdateFailed": {
"title": "Profil konnte nicht aktualisiert werden",
"description": "Etwas ist schiefgelaufen. Bitte versuche es erneut."
},
"passwordChangeFailed": {
"title": "Passwort konnte nicht geändert werden",
"description": "Bitte überprüfe dein aktuelles Passwort und versuche es erneut."
},
"organizationUpdateFailed": {
"title": "Organisation konnte nicht aktualisiert werden",
"description": "Etwas ist schiefgelaufen. Bitte versuche es erneut."
},
"brandingUpdateFailed": {
"title": "Branding konnte nicht aktualisiert werden",
"description": "Etwas ist schiefgelaufen. Bitte versuche es erneut."
},
"addMemberFailed": {
"title": "Mitglied konnte nicht hinzugefügt werden",
"description": "Etwas ist schiefgelaufen. Bitte versuche es erneut."
},
"automationDuplicateFailed": {
"title": "Automatisierung konnte nicht dupliziert werden",
"description": "Etwas ist schiefgelaufen. Bitte versuche es erneut."
},
"automationDeleteFailed": {
"title": "Automatisierung konnte nicht gelöscht werden",
"description": "Etwas ist schiefgelaufen. Bitte versuche es erneut."
},
"generic": {
"title": "Etwas ist schiefgelaufen",
"description": "Bitte versuche es erneut."
}
}
},
"todoList": {
Expand Down
Loading
Loading