/** * Error Message Utilities * * Provides German error messages for form validation and API errors. * Uses informal "Du" form as per project requirements. */ /** * Common validation error messages (German) */ export const validationMessages = { required: 'Dieses Feld ist erforderlich', email: 'Bitte gib eine gültige E-Mail-Adresse ein', minLength: (min: number) => `Mindestens ${min} Zeichen erforderlich`, maxLength: (max: number) => `Maximal ${max} Zeichen erlaubt`, min: (min: number) => `Wert muss mindestens ${min} sein`, max: (max: number) => `Wert darf maximal ${max} sein`, pattern: 'Ungültiges Format', url: 'Bitte gib eine gültige URL ein', numeric: 'Nur Zahlen erlaubt', phoneNumber: 'Bitte gib eine gültige Telefonnummer ein', postCode: 'Bitte gib eine gültige Postleitzahl ein', dateInvalid: 'Ungültiges Datum', dateFuture: 'Datum darf nicht in der Zukunft liegen', datePast: 'Datum darf nicht in der Vergangenheit liegen', dateOfBirthTooYoung: 'Du musst mindestens 18 Jahre alt sein', passwordWeak: 'Passwort muss mindestens 8 Zeichen, einen Großbuchstaben, einen Kleinbuchstaben und eine Zahl enthalten', passwordMismatch: 'Passwörter stimmen nicht überein', terms: 'Bitte akzeptiere die AGB', privacy: 'Bitte akzeptiere die Datenschutzerklärung', } /** * Field-specific error messages */ export const fieldMessages = { salutation: { required: 'Bitte wähle eine Anrede', }, firstName: { required: 'Bitte gib deinen Vornamen ein', minLength: 'Vorname muss mindestens 2 Zeichen lang sein', }, lastName: { required: 'Bitte gib deinen Nachnamen ein', minLength: 'Nachname muss mindestens 2 Zeichen lang sein', }, email: { required: 'Bitte gib deine E-Mail-Adresse ein', invalid: 'Bitte gib eine gültige E-Mail-Adresse ein', }, password: { required: 'Bitte gib ein Passwort ein', weak: 'Passwort muss mindestens 8 Zeichen, einen Großbuchstaben, einen Kleinbuchstaben und eine Zahl enthalten', }, dateOfBirth: { required: 'Bitte gib dein Geburtsdatum ein', invalid: 'Ungültiges Datum', tooYoung: 'Du musst mindestens 18 Jahre alt sein', future: 'Geburtsdatum darf nicht in der Zukunft liegen', }, street: { required: 'Bitte gib deine Straße und Hausnummer ein', minLength: 'Straße muss mindestens 3 Zeichen lang sein', }, postCode: { required: 'Bitte gib deine Postleitzahl ein', invalid: 'Ungültige Postleitzahl', }, city: { required: 'Bitte gib deinen Ort ein', minLength: 'Ort muss mindestens 2 Zeichen lang sein', }, countryCode: { required: 'Bitte wähle ein Land', }, phone: { invalid: 'Bitte gib eine gültige Telefonnummer ein', }, quantity: { min: 'Menge muss mindestens 1 sein', max: (max: number) => `Maximal ${max} Stück verfügbar`, }, } /** * API error messages */ export const apiErrorMessages = { // Authentication errors auth: { invalidCredentials: 'E-Mail oder Passwort ist falsch', emailAlreadyExists: 'Diese E-Mail-Adresse wird bereits verwendet', sessionExpired: 'Deine Sitzung ist abgelaufen. Bitte melde dich erneut an', unauthorized: 'Du musst angemeldet sein, um diese Aktion durchzuführen', forbidden: 'Du hast keine Berechtigung für diese Aktion', }, // Product errors products: { notFound: 'Produkt nicht gefunden', unavailable: 'Produkt ist nicht verfügbar', outOfStock: 'Produkt ist nicht mehr auf Lager', insufficientStock: 'Nicht genügend auf Lager', }, // Cart errors cart: { notFound: 'Warenkorb nicht gefunden', itemNotFound: 'Artikel nicht im Warenkorb gefunden', empty: 'Dein Warenkorb ist leer', invalidQuantity: 'Ungültige Menge', addFailed: 'Artikel konnte nicht hinzugefügt werden', updateFailed: 'Artikel konnte nicht aktualisiert werden', removeFailed: 'Artikel konnte nicht entfernt werden', }, // Order errors orders: { notFound: 'Bestellung nicht gefunden', createFailed: 'Bestellung konnte nicht erstellt werden', paymentFailed: 'Zahlung fehlgeschlagen', invalidAddress: 'Ungültige Lieferadresse', submissionFailed: 'Bestellung konnte nicht übermittelt werden', }, // Generic errors generic: { networkError: 'Netzwerkfehler. Bitte überprüfe deine Internetverbindung', serverError: 'Ein Serverfehler ist aufgetreten. Bitte versuche es später erneut', validationError: 'Bitte überprüfe deine Eingaben', unknownError: 'Ein unbekannter Fehler ist aufgetreten', }, } /** * Format API error response to user-friendly message * * @param error - Error object from API call * @returns User-friendly error message in German * * @example * try { * await $fetch('/api/cart/items', { method: 'POST', body: { productId, quantity } }) * } catch (error) { * const message = formatApiError(error) * toast.error(message) * } */ export function formatApiError(error: unknown): string { // Handle FetchError from Nuxt $fetch if (error && typeof error === 'object' && 'statusCode' in error) { const statusCode = (error as { statusCode: number }).statusCode const data = (error as { data?: { message?: string } }).data // Use custom message from API if available if (data?.message) { return data.message } // Map status codes to generic messages switch (statusCode) { case 400: return apiErrorMessages.generic.validationError case 401: return apiErrorMessages.auth.unauthorized case 403: return apiErrorMessages.auth.forbidden case 404: return 'Ressource nicht gefunden' case 409: return 'Konflikt: Diese Aktion kann nicht durchgeführt werden' case 422: return apiErrorMessages.generic.validationError case 429: return 'Zu viele Anfragen. Bitte warte einen Moment' case 500: return apiErrorMessages.generic.serverError case 503: return 'Service vorübergehend nicht verfügbar' default: return apiErrorMessages.generic.unknownError } } // Handle network errors if (error instanceof TypeError && error.message.includes('fetch')) { return apiErrorMessages.generic.networkError } // Handle Error objects with message if (error instanceof Error) { return error.message } // Fallback for unknown error types return apiErrorMessages.generic.unknownError } /** * Get validation error message for a specific field and error type * * @param field - Field name * @param errorType - Zod error type (e.g., 'required', 'invalid_string', 'too_small') * @param params - Additional parameters (e.g., min, max values) * @returns User-friendly validation error message * * @example * const message = getValidationMessage('email', 'invalid_string') * // => "Bitte gib eine gültige E-Mail-Adresse ein" */ export function getValidationMessage( field: string, errorType: string, params?: { minimum?: number; maximum?: number } ): string { // Check field-specific messages first const fieldKey = field as keyof typeof fieldMessages if (fieldKey in fieldMessages) { const fieldMessage = fieldMessages[fieldKey] // Map Zod error types to field-specific messages if (errorType === 'invalid_type' || errorType === 'required') { return fieldMessage.required || validationMessages.required } if (errorType === 'invalid_string' || errorType === 'invalid_email') { return 'invalid' in fieldMessage ? fieldMessage.invalid : validationMessages.email } if (errorType === 'too_small' && 'minLength' in fieldMessage) { return fieldMessage.minLength } if (errorType === 'too_small' && params?.minimum) { return validationMessages.minLength(params.minimum) } if (errorType === 'too_big' && params?.maximum) { return validationMessages.maxLength(params.maximum) } } // Fallback to generic messages if (errorType === 'invalid_type' || errorType === 'required') { return validationMessages.required } if (errorType === 'invalid_string' || errorType === 'invalid_email') { return validationMessages.email } if (errorType === 'too_small' && params?.minimum) { return validationMessages.minLength(params.minimum) } if (errorType === 'too_big' && params?.maximum) { return validationMessages.maxLength(params.maximum) } return validationMessages.required }