You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

262 lines
8.3 KiB

/**
* 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
}