Merge pull request #637 from the-luap/fix/i18n-activity-types-comprehensive
fix(i18n): sweep activity-type translations + Events / API Tokens / Webhooks settings tabs
This commit is contained in:
@@ -183,7 +183,7 @@ export const ApiTokensTab: React.FC = () => {
|
||||
<tr className="text-left text-neutral-500 dark:text-neutral-400 border-b border-neutral-200 dark:border-neutral-700">
|
||||
<th className="py-2 pr-3">{t('settings.apiTokens.name', 'Name')}</th>
|
||||
<th className="py-2 pr-3">{t('settings.apiTokens.scopes', 'Scopes')}</th>
|
||||
<th className="py-2 pr-3">Preview</th>
|
||||
<th className="py-2 pr-3">{t('settings.apiTokens.preview', 'Preview')}</th>
|
||||
<th className="py-2 pr-3">{t('settings.apiTokens.lastUsed', 'Last used')}</th>
|
||||
<th className="py-2 pr-3">{t('settings.apiTokens.created', 'Created')}</th>
|
||||
<th className="py-2 pr-3">{t('settings.apiTokens.status', 'Status')}</th>
|
||||
@@ -228,7 +228,7 @@ export const ApiTokensTab: React.FC = () => {
|
||||
variant="ghost"
|
||||
leftIcon={<Trash2 className="w-4 h-4" />}
|
||||
onClick={() => {
|
||||
if (confirm(t('settings.apiTokens.confirmRevoke', `Revoke "${token.name}"? Existing integrations using this token will start getting 401.`))) {
|
||||
if (confirm(t('settings.apiTokens.confirmRevoke', { name: token.name, defaultValue: `Revoke "${token.name}"? Existing integrations using this token will start getting 401.` }))) {
|
||||
revokeMutation.mutate(token.id);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -67,7 +67,7 @@ export const WebhooksTab: React.FC = () => {
|
||||
try {
|
||||
parsedFilter = JSON.parse(trimmed);
|
||||
} catch {
|
||||
setFilterError('Filter must be valid JSON');
|
||||
setFilterError(t('settings.webhooks.filterError', 'Filter must be valid JSON'));
|
||||
throw new Error('Invalid filter JSON');
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ export const WebhooksTab: React.FC = () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['admin-webhooks'] });
|
||||
},
|
||||
onError: (err: any) => {
|
||||
toast.error(err?.response?.data?.errors?.[0]?.msg || err?.response?.data?.error || 'Failed to create webhook');
|
||||
toast.error(err?.response?.data?.errors?.[0]?.msg || err?.response?.data?.error || t('settings.webhooks.createError', 'Failed to create webhook'));
|
||||
},
|
||||
});
|
||||
|
||||
@@ -97,16 +97,16 @@ export const WebhooksTab: React.FC = () => {
|
||||
mutationFn: async ({ id, active }: { id: number; active: boolean }) =>
|
||||
api.put(`/admin/webhooks/${id}`, { active }),
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['admin-webhooks'] }),
|
||||
onError: () => toast.error('Failed to update webhook'),
|
||||
onError: () => toast.error(t('settings.webhooks.updateError', 'Failed to update webhook')),
|
||||
});
|
||||
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: async (id: number) => api.delete(`/admin/webhooks/${id}`),
|
||||
onSuccess: () => {
|
||||
toast.success('Webhook deleted');
|
||||
toast.success(t('settings.webhooks.deletedToast', 'Webhook deleted'));
|
||||
queryClient.invalidateQueries({ queryKey: ['admin-webhooks'] });
|
||||
},
|
||||
onError: () => toast.error('Failed to delete webhook'),
|
||||
onError: () => toast.error(t('settings.webhooks.deleteError', 'Failed to delete webhook')),
|
||||
});
|
||||
|
||||
const toggleEvent = (e: WebhookEventType) => {
|
||||
@@ -165,16 +165,16 @@ export const WebhooksTab: React.FC = () => {
|
||||
onClick={async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(justCreatedSecret);
|
||||
toast.success('Copied');
|
||||
toast.success(t('settings.webhooks.copied', 'Copied'));
|
||||
} catch {
|
||||
toast.error('Copy failed');
|
||||
toast.error(t('settings.webhooks.copyFailed', 'Copy failed'));
|
||||
}
|
||||
}}
|
||||
>
|
||||
Copy
|
||||
{t('settings.webhooks.copyButton', 'Copy')}
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onClick={() => setJustCreatedSecret(null)}>
|
||||
Dismiss
|
||||
{t('settings.webhooks.dismiss', 'Dismiss')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -188,13 +188,13 @@ export const WebhooksTab: React.FC = () => {
|
||||
<label className="block text-sm font-medium text-neutral-700 dark:text-neutral-300 mb-1">
|
||||
{t('settings.webhooks.name', 'Name')}
|
||||
</label>
|
||||
<Input value={name} onChange={(e) => setName(e.target.value)} placeholder="e.g. n8n WhatsApp" />
|
||||
<Input value={name} onChange={(e) => setName(e.target.value)} placeholder={t('settings.webhooks.namePlaceholder', 'e.g. n8n WhatsApp')} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-neutral-700 dark:text-neutral-300 mb-1">
|
||||
{t('settings.webhooks.url', 'Receiver URL')}
|
||||
</label>
|
||||
<Input value={url} onChange={(e) => setUrl(e.target.value)} placeholder="https://n8n.example.com/webhook/picpeak" />
|
||||
<Input value={url} onChange={(e) => setUrl(e.target.value)} placeholder={t('settings.webhooks.urlPlaceholder', 'https://n8n.example.com/webhook/picpeak')} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -222,7 +222,7 @@ export const WebhooksTab: React.FC = () => {
|
||||
onClick={() => setShowAdvanced((prev) => !prev)}
|
||||
className="text-sm text-primary-600 dark:text-primary-400 hover:underline self-start"
|
||||
>
|
||||
{showAdvanced ? '− Hide advanced (filter, template)' : '+ Advanced (filter, template)'}
|
||||
{showAdvanced ? t('settings.webhooks.hideAdvanced', '− Hide advanced (filter, template)') : t('settings.webhooks.showAdvanced', '+ Advanced (filter, template)')}
|
||||
</button>
|
||||
|
||||
{showAdvanced && (
|
||||
@@ -239,7 +239,7 @@ export const WebhooksTab: React.FC = () => {
|
||||
className="w-full px-3 py-2 border border-neutral-300 dark:border-neutral-700 dark:bg-neutral-800 rounded text-sm font-mono"
|
||||
/>
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
Dot-path → expected value. All keys must match (AND). Use an array for "any of": <code>{'{"type": ["event.published", "event.archived"]}'}</code>
|
||||
{t('settings.webhooks.filterHelp', 'Dot-path → expected value. All keys must match (AND). Use an array for "any of".')} <code>{'{"type": ["event.published", "event.archived"]}'}</code>
|
||||
</p>
|
||||
{filterError && <p className="text-xs text-red-600 mt-1">{filterError}</p>}
|
||||
</div>
|
||||
@@ -251,12 +251,12 @@ export const WebhooksTab: React.FC = () => {
|
||||
<textarea
|
||||
value={template}
|
||||
onChange={(e) => setTemplate(e.target.value)}
|
||||
placeholder={'New gallery: ${data.event.event_name} → ${data.event.share_url}'}
|
||||
placeholder={t('settings.webhooks.templatePlaceholder', 'New gallery: ${data.event.event_name} → ${data.event.share_url}')}
|
||||
rows={3}
|
||||
className="w-full px-3 py-2 border border-neutral-300 dark:border-neutral-700 dark:bg-neutral-800 rounded text-sm font-mono"
|
||||
/>
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
Replaces the default JSON envelope as the request body. <code>${'{dot.path}'}</code> substitution from the payload only — no logic, no expressions.
|
||||
{t('settings.webhooks.templateHelp', 'Replaces the default JSON envelope as the request body. ${dot.path} substitution from the payload only — no logic, no expressions.')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -282,12 +282,12 @@ export const WebhooksTab: React.FC = () => {
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="text-left text-neutral-500 dark:text-neutral-400 border-b border-neutral-200 dark:border-neutral-700">
|
||||
<th className="py-2 pr-3">Name</th>
|
||||
<th className="py-2 pr-3">URL</th>
|
||||
<th className="py-2 pr-3">Events</th>
|
||||
<th className="py-2 pr-3">Last delivery</th>
|
||||
<th className="py-2 pr-3">Status</th>
|
||||
<th className="py-2 text-right">Actions</th>
|
||||
<th className="py-2 pr-3">{t('settings.webhooks.colName', 'Name')}</th>
|
||||
<th className="py-2 pr-3">{t('settings.webhooks.colUrl', 'URL')}</th>
|
||||
<th className="py-2 pr-3">{t('settings.webhooks.colEvents', 'Events')}</th>
|
||||
<th className="py-2 pr-3">{t('settings.webhooks.colLastDelivery', 'Last delivery')}</th>
|
||||
<th className="py-2 pr-3">{t('settings.webhooks.colStatus', 'Status')}</th>
|
||||
<th className="py-2 text-right">{t('settings.webhooks.colActions', 'Actions')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -300,7 +300,7 @@ export const WebhooksTab: React.FC = () => {
|
||||
<td className="py-3 pr-3 font-medium">{wh.name}</td>
|
||||
<td className="py-3 pr-3 text-xs font-mono text-neutral-600 dark:text-neutral-400 max-w-xs truncate" title={wh.url}>{wh.url}</td>
|
||||
<td className="py-3 pr-3 text-xs text-neutral-500">
|
||||
{Array.isArray(wh.events) ? wh.events.length : 0} subscribed
|
||||
{t('settings.webhooks.eventsSubscribed', { count: Array.isArray(wh.events) ? wh.events.length : 0 })}
|
||||
</td>
|
||||
<td className="py-3 pr-3 text-xs text-neutral-500">
|
||||
{lastEither === 'success' && lastSuccess && (
|
||||
@@ -325,9 +325,9 @@ export const WebhooksTab: React.FC = () => {
|
||||
? 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300'
|
||||
: 'bg-neutral-200 dark:bg-neutral-700 text-neutral-600 dark:text-neutral-400'
|
||||
}`}
|
||||
title={wh.active ? 'Click to disable' : 'Click to enable'}
|
||||
title={wh.active ? t('settings.webhooks.toggleToDisable', 'Click to disable') : t('settings.webhooks.toggleToEnable', 'Click to enable')}
|
||||
>
|
||||
{wh.active ? 'Active' : 'Disabled'}
|
||||
{wh.active ? t('settings.webhooks.statusActive', 'Active') : t('settings.webhooks.statusDisabled', 'Disabled')}
|
||||
</button>
|
||||
</td>
|
||||
<td className="py-3 text-right">
|
||||
@@ -337,19 +337,19 @@ export const WebhooksTab: React.FC = () => {
|
||||
className="inline-flex items-center gap-1 px-2 py-1 text-xs text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-neutral-100"
|
||||
>
|
||||
<Activity className="w-3.5 h-3.5" />
|
||||
Deliveries
|
||||
{t('settings.webhooks.deliveriesLink', 'Deliveries')}
|
||||
</Link>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
leftIcon={<Trash2 className="w-4 h-4" />}
|
||||
onClick={() => {
|
||||
if (confirm(`Delete "${wh.name}"? Pending deliveries are also removed.`)) {
|
||||
if (confirm(t('settings.webhooks.confirmDelete', { name: wh.name, defaultValue: `Delete "${wh.name}"? Pending deliveries are also removed.` }))) {
|
||||
deleteMutation.mutate(wh.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
{t('settings.webhooks.delete', 'Delete')}
|
||||
</Button>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -1312,7 +1312,9 @@
|
||||
"showGalleryFilterBar": "Filterleiste in Galerien anzeigen",
|
||||
"showGalleryFilterBarHelp": "Dateiname-Suche und Sortiersteuerungen über Raster-Galerien anzeigen. Deaktivieren für ein übersichtlicheres Layout.",
|
||||
"enablePhoneField": "Telefonnummer-Feld aktivieren",
|
||||
"enablePhoneFieldHelp": "Fügt ein optionales Telefonnummer-Eingabefeld zum Veranstaltungsformular hinzu. Nützlich für Folgeautomatisierungen wie WhatsApp-Zustellung via n8n. Immer optional, auch wenn aktiviert."
|
||||
"enablePhoneFieldHelp": "Fügt ein optionales Telefonnummer-Eingabefeld zum Veranstaltungsformular hinzu. Nützlich für Folgeautomatisierungen wie WhatsApp-Zustellung via n8n. Immer optional, auch wenn aktiviert.",
|
||||
"defaultFeedbackEnabled": "Gäste-Feedback standardmäßig aktivieren",
|
||||
"defaultFeedbackEnabledHelp": "Vorbelegt \"Gäste-Feedback\" beim Erstellen neuer Events. Einzelne Feedback-Optionen (Likes, Bewertungen, Kommentare) bleiben pro Event anpassbar."
|
||||
},
|
||||
"imageSecurity": {
|
||||
"title": "Bildschutz",
|
||||
@@ -1560,9 +1562,10 @@
|
||||
"statusRevoked": "Widerrufen",
|
||||
"statusExpired": "Abgelaufen",
|
||||
"statusActive": "Aktiv",
|
||||
"confirmRevoke": "Diesen Token wirklich widerrufen?",
|
||||
"confirmRevoke": "\"{{name}}\" widerrufen? Bestehende Integrationen, die dieses Token nutzen, erhalten ab sofort 401.",
|
||||
"revoke": "Widerrufen",
|
||||
"empty": "Noch keine Tokens. Generieren Sie oben einen, um zu beginnen."
|
||||
"empty": "Noch keine Tokens. Generieren Sie oben einen, um zu beginnen.",
|
||||
"preview": "Vorschau"
|
||||
},
|
||||
"webhooks": {
|
||||
"title": "Webhooks",
|
||||
@@ -1576,7 +1579,38 @@
|
||||
"template": "Vorlage (optional)",
|
||||
"create": "Webhook erstellen",
|
||||
"existing": "Vorhandene Webhooks",
|
||||
"empty": "Noch keine Webhooks. Erstellen Sie oben einen, um Ereignisbenachrichtigungen zu erhalten."
|
||||
"empty": "Noch keine Webhooks. Erstellen Sie oben einen, um Ereignisbenachrichtigungen zu erhalten.",
|
||||
"namePlaceholder": "z. B. n8n WhatsApp",
|
||||
"urlPlaceholder": "https://n8n.example.com/webhook/picpeak",
|
||||
"showAdvanced": "+ Erweitert (Filter, Vorlage)",
|
||||
"hideAdvanced": "− Erweitert ausblenden (Filter, Vorlage)",
|
||||
"filterHelp": "Dot-Pfad → erwarteter Wert. Alle Schlüssel müssen übereinstimmen (AND). Array für \"eines aus\".",
|
||||
"filterError": "Filter muss gültiges JSON sein",
|
||||
"templatePlaceholder": "Neue Galerie: ${data.event.event_name} → ${data.event.share_url}",
|
||||
"templateHelp": "Ersetzt den Standard-JSON-Umschlag als Request-Body. Nur ${dot.path}-Substitution aus der Payload — keine Logik, keine Ausdrücke.",
|
||||
"copyButton": "Kopieren",
|
||||
"dismiss": "Schließen",
|
||||
"copied": "Kopiert",
|
||||
"copyFailed": "Kopieren fehlgeschlagen",
|
||||
"colName": "Name",
|
||||
"colUrl": "URL",
|
||||
"colEvents": "Events",
|
||||
"colLastDelivery": "Letzte Zustellung",
|
||||
"colStatus": "Status",
|
||||
"colActions": "Aktionen",
|
||||
"eventsSubscribed_one": "{{count}} abonniert",
|
||||
"eventsSubscribed_other": "{{count}} abonniert",
|
||||
"statusActive": "Aktiv",
|
||||
"statusDisabled": "Deaktiviert",
|
||||
"toggleToDisable": "Klicken zum Deaktivieren",
|
||||
"toggleToEnable": "Klicken zum Aktivieren",
|
||||
"deliveriesLink": "Zustellungen",
|
||||
"delete": "Löschen",
|
||||
"confirmDelete": "\"{{name}}\" löschen? Ausstehende Zustellungen werden ebenfalls entfernt.",
|
||||
"createError": "Webhook konnte nicht erstellt werden",
|
||||
"updateError": "Webhook konnte nicht aktualisiert werden",
|
||||
"deletedToast": "Webhook gelöscht",
|
||||
"deleteError": "Webhook konnte nicht gelöscht werden"
|
||||
},
|
||||
"sectionLabel": "Einstellungsbereich",
|
||||
"navAriaLabel": "Einstellungsnavigation",
|
||||
@@ -2180,7 +2214,72 @@
|
||||
"categoryHeroUpdated": "Kategorie-Titelbild aktualisiert",
|
||||
"publicSiteResetToDefault": "Öffentliche Seite auf Standard zurückgesetzt",
|
||||
"cmsPageLogoUploaded": "CMS-Seiten-Logo hochgeladen: {{slug}}",
|
||||
"adminPasswordResetByAdmin": "Admin-Passwort durch anderen Admin zurückgesetzt: {{username}}"
|
||||
"adminPasswordResetByAdmin": "Admin-Passwort durch anderen Admin zurückgesetzt: {{username}}",
|
||||
"contractCreated": "Vertrag erstellt: {{contractNumber}}",
|
||||
"contractCreatedFromQuote": "Vertrag aus Angebot erstellt: {{contractNumber}}",
|
||||
"contractUpdated": "Vertrag aktualisiert: {{contractNumber}}",
|
||||
"contractSent": "Vertrag gesendet: {{contractNumber}}",
|
||||
"contractResentSigned": "Unterschriebener Vertrag erneut gesendet: {{contractNumber}}",
|
||||
"contractSignedByCustomer": "Vertrag vom Kunden unterschrieben: {{contractNumber}}",
|
||||
"contractSignedPdfUploaded": "Unterschriebenes Vertrag-PDF hochgeladen: {{contractNumber}}",
|
||||
"contractSignaturesRestamped": "Vertragsunterschriften neu gestempelt: {{contractNumber}}",
|
||||
"contractCancelled": "Vertrag storniert: {{contractNumber}}",
|
||||
"contractConvertedToEvent": "Vertrag in Event umgewandelt: {{contractNumber}}",
|
||||
"contractConvertedToEmptyEvent": "Vertrag in leeres Event umgewandelt: {{contractNumber}}",
|
||||
"contractConvertedToInvoices": "Vertrag in Rechnungen umgewandelt: {{contractNumber}}",
|
||||
"contractConvertedToEmptyInvoice": "Vertrag in leere Rechnung umgewandelt: {{contractNumber}}",
|
||||
"quoteCreated": "Angebot erstellt: {{quoteNumber}}",
|
||||
"quoteSent": "Angebot gesendet: {{quoteNumber}}",
|
||||
"quoteUpdated": "Angebot aktualisiert: {{quoteNumber}}",
|
||||
"quoteAcceptedByAdmin": "Angebot angenommen: {{quoteNumber}}",
|
||||
"quoteDeclinedByAdmin": "Angebot abgelehnt: {{quoteNumber}}",
|
||||
"quoteConverted": "Angebot umgewandelt: {{quoteNumber}}",
|
||||
"quoteConvertedInvoicesOnly": "Angebot in Rechnungen umgewandelt: {{quoteNumber}}",
|
||||
"invoiceCreated": "Rechnung erstellt: {{invoiceNumber}}",
|
||||
"invoiceSent": "Rechnung gesendet: {{invoiceNumber}}",
|
||||
"invoiceScheduled": "Rechnung geplant: {{invoiceNumber}}",
|
||||
"invoiceCancelled": "Rechnung storniert: {{invoiceNumber}}",
|
||||
"invoiceCancelledViaStorno": "Rechnung per Storno storniert: {{invoiceNumber}}",
|
||||
"invoiceReissued": "Rechnung neu ausgestellt: {{invoiceNumber}}",
|
||||
"invoicePaidAdminNotified": "Rechnung als bezahlt markiert: {{invoiceNumber}}",
|
||||
"invoicePaymentCheckRecorded": "Zahlungsprüfung erfasst für Rechnung: {{invoiceNumber}}",
|
||||
"invoicePaymentCheckSent": "Zahlungsprüfung gesendet für Rechnung: {{invoiceNumber}}",
|
||||
"invoiceReleasedForDelivery": "Rechnung zur Lieferung freigegeben: {{invoiceNumber}}",
|
||||
"invoiceReminderSent": "Rechnungserinnerung gesendet: {{invoiceNumber}}",
|
||||
"stornoSent": "Storno gesendet: {{invoiceNumber}}",
|
||||
"monthlyBillIssued": "Monatsrechnung für Kunden ausgestellt",
|
||||
"monthlyBillSkippedEmpty": "Monatsrechnung übersprungen (keine Einträge)",
|
||||
"monthlyBillTriggeredManually": "Monatsrechnung manuell ausgelöst",
|
||||
"monthlyBillingItemsQueued": "Monatsabrechnungsposten in Warteschlange",
|
||||
"installmentPlanUpdated": "Ratenplan aktualisiert",
|
||||
"expenseCreated": "Ausgabe erstellt",
|
||||
"expenseUpdated": "Ausgabe aktualisiert",
|
||||
"expensePaid": "Ausgabe als bezahlt markiert",
|
||||
"expenseInvoiced": "Ausgabe in Rechnung gestellt",
|
||||
"hourEntryLogged": "Stundeneintrag erfasst",
|
||||
"hourEntryUpdated": "Stundeneintrag aktualisiert",
|
||||
"hourEntryDeleted": "Stundeneintrag gelöscht",
|
||||
"hourEntryLoggedToMonthlyDraft": "Stundeneintrag in Monatsentwurf erfasst",
|
||||
"hourEntriesBilled": "Stundeneinträge an Kunden abgerechnet",
|
||||
"incomingInvoiceCaptured": "Eingangsrechnung erfasst",
|
||||
"incomingInvoiceCategorized": "Eingangsrechnung kategorisiert",
|
||||
"incomingInvoiceUpdated": "Eingangsrechnung aktualisiert",
|
||||
"incomingInvoiceRebilled": "Eingangsrechnung an Kunden weiterverrechnet",
|
||||
"incomingInvoiceSupplierPayment": "Lieferantenzahlung erfasst",
|
||||
"incomingMailConfigUpdated": "Eingangs-Mail-Konfiguration aktualisiert",
|
||||
"customerCreatedPassive": "Passiver Kunde erstellt: {{email}}",
|
||||
"adminUserActivated": "Admin-Konto aktiviert: {{username}}",
|
||||
"adminUserDeleted": "Admin-Konto gelöscht: {{username}}",
|
||||
"adminPasswordReset": "Admin-Passwort zurückgesetzt: {{username}}",
|
||||
"bulkArchiveCompleted": "Massen-Archivierung abgeschlossen: {{count}} Events archiviert",
|
||||
"emailQueueFlushed": "E-Mail-Warteschlange geleert",
|
||||
"emailResent": "Erstellungs-E-Mail erneut gesendet für {{eventName}}",
|
||||
"emailTemplateCreated": "E-Mail-Vorlage erstellt: {{template_key}}",
|
||||
"eventDuplicated": "Event dupliziert aus {{source_event_name}}",
|
||||
"feedbackDeleted": "Feedback gelöscht",
|
||||
"feedbackModerated": "Feedback moderiert",
|
||||
"feedbackSettingsUpdated": "Feedback-Einstellungen aktualisiert für {{eventName}}",
|
||||
"wordFilterAdded": "Wortfilter hinzugefügt: {{word}}"
|
||||
},
|
||||
"notificationToasts": {
|
||||
"markedAllRead": "Alle Benachrichtigungen als gelesen markiert",
|
||||
@@ -2375,7 +2474,65 @@
|
||||
"photo_uploaded": "Foto hochgeladen in {{eventName}}",
|
||||
"category_hero_updated": "Kategorie-Titelbild aktualisiert",
|
||||
"public_site_reset_to_default": "Öffentliche Seite auf Standard zurückgesetzt",
|
||||
"cms_page_logo_uploaded": "CMS-Seiten-Logo hochgeladen: {{slug}}"
|
||||
"cms_page_logo_uploaded": "CMS-Seiten-Logo hochgeladen: {{slug}}",
|
||||
"contract_created": "Vertrag erstellt: {{contractNumber}}",
|
||||
"contract_created_from_quote": "Vertrag aus Angebot erstellt: {{contractNumber}}",
|
||||
"contract_updated": "Vertrag aktualisiert: {{contractNumber}}",
|
||||
"contract_sent": "Vertrag gesendet: {{contractNumber}}",
|
||||
"contract_resent_signed": "Unterschriebener Vertrag erneut gesendet: {{contractNumber}}",
|
||||
"contract_signed_by_customer": "Vertrag vom Kunden unterschrieben: {{contractNumber}}",
|
||||
"contract_signed_pdf_uploaded": "Unterschriebenes Vertrag-PDF hochgeladen: {{contractNumber}}",
|
||||
"contract_signatures_restamped": "Vertragsunterschriften neu gestempelt: {{contractNumber}}",
|
||||
"contract_cancelled": "Vertrag storniert: {{contractNumber}}",
|
||||
"contract_converted_to_event": "Vertrag in Event umgewandelt: {{contractNumber}}",
|
||||
"contract_converted_to_empty_event": "Vertrag in leeres Event umgewandelt: {{contractNumber}}",
|
||||
"contract_converted_to_invoices": "Vertrag in Rechnungen umgewandelt: {{contractNumber}}",
|
||||
"contract_converted_to_empty_invoice": "Vertrag in leere Rechnung umgewandelt: {{contractNumber}}",
|
||||
"quote_created": "Angebot erstellt: {{quoteNumber}}",
|
||||
"quote_sent": "Angebot gesendet: {{quoteNumber}}",
|
||||
"quote_updated": "Angebot aktualisiert: {{quoteNumber}}",
|
||||
"quote_accepted_by_admin": "Angebot angenommen: {{quoteNumber}}",
|
||||
"quote_declined_by_admin": "Angebot abgelehnt: {{quoteNumber}}",
|
||||
"quote_converted": "Angebot umgewandelt: {{quoteNumber}}",
|
||||
"quote_converted_invoices_only": "Angebot in Rechnungen umgewandelt: {{quoteNumber}}",
|
||||
"invoice_created": "Rechnung erstellt: {{invoiceNumber}}",
|
||||
"invoice_sent": "Rechnung gesendet: {{invoiceNumber}}",
|
||||
"invoice_scheduled": "Rechnung geplant: {{invoiceNumber}}",
|
||||
"invoice_cancelled": "Rechnung storniert: {{invoiceNumber}}",
|
||||
"invoice_cancelled_via_storno": "Rechnung per Storno storniert: {{invoiceNumber}}",
|
||||
"invoice_reissued": "Rechnung neu ausgestellt: {{invoiceNumber}}",
|
||||
"invoice_paid_admin_notified": "Rechnung als bezahlt markiert: {{invoiceNumber}}",
|
||||
"invoice_payment_check_recorded": "Zahlungsprüfung erfasst für Rechnung: {{invoiceNumber}}",
|
||||
"invoice_payment_check_sent": "Zahlungsprüfung gesendet für Rechnung: {{invoiceNumber}}",
|
||||
"invoice_released_for_delivery": "Rechnung zur Lieferung freigegeben: {{invoiceNumber}}",
|
||||
"invoice_reminder_sent": "Rechnungserinnerung gesendet: {{invoiceNumber}}",
|
||||
"storno_sent": "Storno gesendet: {{invoiceNumber}}",
|
||||
"monthly_bill_issued": "Monatsrechnung für Kunden ausgestellt",
|
||||
"monthly_bill_skipped_empty": "Monatsrechnung übersprungen (keine Einträge)",
|
||||
"monthly_bill_triggered_manually": "Monatsrechnung manuell ausgelöst",
|
||||
"monthly_billing_items_queued": "Monatsabrechnungsposten in Warteschlange",
|
||||
"installment_plan_updated": "Ratenplan aktualisiert",
|
||||
"expense_created": "Ausgabe erstellt",
|
||||
"expense_updated": "Ausgabe aktualisiert",
|
||||
"expense_paid": "Ausgabe als bezahlt markiert",
|
||||
"expense_invoiced": "Ausgabe in Rechnung gestellt",
|
||||
"hour_entry_logged": "Stundeneintrag erfasst",
|
||||
"hour_entry_updated": "Stundeneintrag aktualisiert",
|
||||
"hour_entry_deleted": "Stundeneintrag gelöscht",
|
||||
"hour_entry_logged_to_monthly_draft": "Stundeneintrag in Monatsentwurf erfasst",
|
||||
"hour_entries_billed": "Stundeneinträge an Kunden abgerechnet",
|
||||
"incoming_invoice_captured": "Eingangsrechnung erfasst",
|
||||
"incoming_invoice_categorized": "Eingangsrechnung kategorisiert",
|
||||
"incoming_invoice_updated": "Eingangsrechnung aktualisiert",
|
||||
"incoming_invoice_rebilled": "Eingangsrechnung an Kunden weiterverrechnet",
|
||||
"incoming_invoice_supplier_payment": "Lieferantenzahlung erfasst",
|
||||
"incoming_mail_config_updated": "Eingangs-Mail-Konfiguration aktualisiert",
|
||||
"customer_created_passive": "Passiver Kunde erstellt: {{email}}",
|
||||
"admin_user_activated": "Admin-Konto aktiviert: {{username}}",
|
||||
"admin_user_deleted": "Admin-Konto gelöscht: {{username}}",
|
||||
"email_queue_flushed": "E-Mail-Warteschlange geleert",
|
||||
"email_template_created": "E-Mail-Vorlage erstellt: {{template_key}}",
|
||||
"event_duplicated": "Event dupliziert aus {{source_event_name}}"
|
||||
}
|
||||
},
|
||||
"acceptInvitation": {
|
||||
@@ -3956,10 +4113,15 @@
|
||||
"settingsSaved": "Zuordnungen gespeichert.",
|
||||
"saveDefaults": "Zuordnungen speichern",
|
||||
"accountType": {
|
||||
"asset": "Aktiven", "liability": "Passiven", "equity": "Eigenkapital", "revenue": "Ertrag", "expense": "Aufwand"
|
||||
"asset": "Aktiven",
|
||||
"liability": "Passiven",
|
||||
"equity": "Eigenkapital",
|
||||
"revenue": "Ertrag",
|
||||
"expense": "Aufwand"
|
||||
},
|
||||
"vatDirection": {
|
||||
"input": "Vorsteuer", "output": "Umsatzsteuer"
|
||||
"input": "Vorsteuer",
|
||||
"output": "Umsatzsteuer"
|
||||
},
|
||||
"defaults": {
|
||||
"title": "Standard- & Systemkonten",
|
||||
@@ -3974,20 +4136,40 @@
|
||||
"ledger_account_per_diem": "Spesenpauschalen-Konto",
|
||||
"ledger_account_rebilled_revenue": "Weiterverrechnete Spesen (Ertrag)"
|
||||
},
|
||||
"vatMap": { "title": "MWST-Code nach steuerlicher Behandlung (Kosten)" },
|
||||
"outputVatMap": { "title": "MWST-Code nach Umsatzsatz", "empty": "Fügen Sie oben Ausgangs-MWST-Codes hinzu, um pro Umsatzsatz einen Code festzulegen." },
|
||||
"categoryMap": { "title": "Aufwandkategorie → Konto" },
|
||||
"accounts": { "title": "Kontenplan" },
|
||||
"vatCodes": { "title": "MWST-Codes" },
|
||||
"vatMap": {
|
||||
"title": "MWST-Code nach steuerlicher Behandlung (Kosten)"
|
||||
},
|
||||
"outputVatMap": {
|
||||
"title": "MWST-Code nach Umsatzsatz",
|
||||
"empty": "Fügen Sie oben Ausgangs-MWST-Codes hinzu, um pro Umsatzsatz einen Code festzulegen."
|
||||
},
|
||||
"categoryMap": {
|
||||
"title": "Aufwandkategorie → Konto"
|
||||
},
|
||||
"accounts": {
|
||||
"title": "Kontenplan"
|
||||
},
|
||||
"vatCodes": {
|
||||
"title": "MWST-Codes"
|
||||
},
|
||||
"account": {
|
||||
"addTitle": "Konto hinzufügen", "editTitle": "Konto bearbeiten",
|
||||
"number": "Kontonummer", "name": "Name", "type": "Art",
|
||||
"addTitle": "Konto hinzufügen",
|
||||
"editTitle": "Konto bearbeiten",
|
||||
"number": "Kontonummer",
|
||||
"name": "Name",
|
||||
"type": "Art",
|
||||
"confirmDelete": "Dieses Konto löschen?"
|
||||
},
|
||||
"vat": {
|
||||
"addTitle": "MWST-Code hinzufügen", "editTitle": "MWST-Code bearbeiten",
|
||||
"code": "Code", "name": "Name", "rate": "Satz %", "direction": "Richtung",
|
||||
"account": "MWST-Konto", "noAccount": "— keines —", "confirmDelete": "Diesen MWST-Code löschen?",
|
||||
"addTitle": "MWST-Code hinzufügen",
|
||||
"editTitle": "MWST-Code bearbeiten",
|
||||
"code": "Code",
|
||||
"name": "Name",
|
||||
"rate": "Satz %",
|
||||
"direction": "Richtung",
|
||||
"account": "MWST-Konto",
|
||||
"noAccount": "— keines —",
|
||||
"confirmDelete": "Diesen MWST-Code löschen?",
|
||||
"legacyRate": "{{rate}}% (nicht konfiguriert)"
|
||||
},
|
||||
"export": {
|
||||
|
||||
@@ -933,7 +933,9 @@
|
||||
"showGalleryFilterBar": "Show filter bar in galleries",
|
||||
"showGalleryFilterBarHelp": "Display the search-by-filename and sort controls above grid-layout galleries. Disable for a cleaner layout.",
|
||||
"enablePhoneField": "Enable phone number field",
|
||||
"enablePhoneFieldHelp": "Adds an optional phone number input to the event form. Useful for downstream automations like WhatsApp delivery via n8n. Always optional even when enabled."
|
||||
"enablePhoneFieldHelp": "Adds an optional phone number input to the event form. Useful for downstream automations like WhatsApp delivery via n8n. Always optional even when enabled.",
|
||||
"defaultFeedbackEnabled": "Enable Guest Feedback by default",
|
||||
"defaultFeedbackEnabledHelp": "Pre-check \"Guest Feedback\" when creating new events. Individual feedback options (likes, ratings, comments) can still be customised per event."
|
||||
},
|
||||
"imageSecurity": {
|
||||
"title": "Image Protection",
|
||||
@@ -1118,9 +1120,10 @@
|
||||
"statusRevoked": "Revoked",
|
||||
"statusExpired": "Expired",
|
||||
"statusActive": "Active",
|
||||
"confirmRevoke": "Do you want to revoke this token? This action cannot be undone.",
|
||||
"confirmRevoke": "Revoke \"{{name}}\"? Existing integrations using this token will start getting 401.",
|
||||
"revoke": "Revoke",
|
||||
"empty": "No tokens yet. Generate one above to get started."
|
||||
"empty": "No tokens yet. Generate one above to get started.",
|
||||
"preview": "Preview"
|
||||
},
|
||||
"webhooks": {
|
||||
"title": "Webhooks",
|
||||
@@ -1134,7 +1137,38 @@
|
||||
"template": "Template (optional)",
|
||||
"create": "Create Webhook",
|
||||
"existing": "Existing webhooks",
|
||||
"empty": "No webhooks yet. Create one above to start receiving event notifications."
|
||||
"empty": "No webhooks yet. Create one above to start receiving event notifications.",
|
||||
"namePlaceholder": "e.g. n8n WhatsApp",
|
||||
"urlPlaceholder": "https://n8n.example.com/webhook/picpeak",
|
||||
"showAdvanced": "+ Advanced (filter, template)",
|
||||
"hideAdvanced": "− Hide advanced (filter, template)",
|
||||
"filterHelp": "Dot-path → expected value. All keys must match (AND). Use an array for \"any of\".",
|
||||
"filterError": "Filter must be valid JSON",
|
||||
"templatePlaceholder": "New gallery: ${data.event.event_name} → ${data.event.share_url}",
|
||||
"templateHelp": "Replaces the default JSON envelope as the request body. ${dot.path} substitution from the payload only — no logic, no expressions.",
|
||||
"copyButton": "Copy",
|
||||
"dismiss": "Dismiss",
|
||||
"copied": "Copied",
|
||||
"copyFailed": "Copy failed",
|
||||
"colName": "Name",
|
||||
"colUrl": "URL",
|
||||
"colEvents": "Events",
|
||||
"colLastDelivery": "Last delivery",
|
||||
"colStatus": "Status",
|
||||
"colActions": "Actions",
|
||||
"eventsSubscribed_one": "{{count}} subscribed",
|
||||
"eventsSubscribed_other": "{{count}} subscribed",
|
||||
"statusActive": "Active",
|
||||
"statusDisabled": "Disabled",
|
||||
"toggleToDisable": "Click to disable",
|
||||
"toggleToEnable": "Click to enable",
|
||||
"deliveriesLink": "Deliveries",
|
||||
"delete": "Delete",
|
||||
"confirmDelete": "Delete \"{{name}}\"? Pending deliveries are also removed.",
|
||||
"createError": "Failed to create webhook",
|
||||
"updateError": "Failed to update webhook",
|
||||
"deletedToast": "Webhook deleted",
|
||||
"deleteError": "Failed to delete webhook"
|
||||
},
|
||||
"sectionLabel": "Settings section",
|
||||
"navAriaLabel": "Settings navigation",
|
||||
@@ -1767,7 +1801,72 @@
|
||||
"categoryHeroUpdated": "Category hero photo updated",
|
||||
"publicSiteResetToDefault": "Public site reset to default",
|
||||
"cmsPageLogoUploaded": "CMS page logo uploaded: {{slug}}",
|
||||
"adminPasswordResetByAdmin": "Admin password reset by another admin: {{username}}"
|
||||
"adminPasswordResetByAdmin": "Admin password reset by another admin: {{username}}",
|
||||
"contractCreated": "Contract created: {{contractNumber}}",
|
||||
"contractCreatedFromQuote": "Contract created from quote: {{contractNumber}}",
|
||||
"contractUpdated": "Contract updated: {{contractNumber}}",
|
||||
"contractSent": "Contract sent: {{contractNumber}}",
|
||||
"contractResentSigned": "Signed contract resent: {{contractNumber}}",
|
||||
"contractSignedByCustomer": "Contract signed by customer: {{contractNumber}}",
|
||||
"contractSignedPdfUploaded": "Signed contract PDF uploaded: {{contractNumber}}",
|
||||
"contractSignaturesRestamped": "Contract signatures re-stamped: {{contractNumber}}",
|
||||
"contractCancelled": "Contract cancelled: {{contractNumber}}",
|
||||
"contractConvertedToEvent": "Contract converted to event: {{contractNumber}}",
|
||||
"contractConvertedToEmptyEvent": "Contract converted to empty event: {{contractNumber}}",
|
||||
"contractConvertedToInvoices": "Contract converted to invoices: {{contractNumber}}",
|
||||
"contractConvertedToEmptyInvoice": "Contract converted to empty invoice: {{contractNumber}}",
|
||||
"quoteCreated": "Quote created: {{quoteNumber}}",
|
||||
"quoteSent": "Quote sent: {{quoteNumber}}",
|
||||
"quoteUpdated": "Quote updated: {{quoteNumber}}",
|
||||
"quoteAcceptedByAdmin": "Quote accepted: {{quoteNumber}}",
|
||||
"quoteDeclinedByAdmin": "Quote declined: {{quoteNumber}}",
|
||||
"quoteConverted": "Quote converted: {{quoteNumber}}",
|
||||
"quoteConvertedInvoicesOnly": "Quote converted to invoices: {{quoteNumber}}",
|
||||
"invoiceCreated": "Invoice created: {{invoiceNumber}}",
|
||||
"invoiceSent": "Invoice sent: {{invoiceNumber}}",
|
||||
"invoiceScheduled": "Invoice scheduled: {{invoiceNumber}}",
|
||||
"invoiceCancelled": "Invoice cancelled: {{invoiceNumber}}",
|
||||
"invoiceCancelledViaStorno": "Invoice cancelled via Storno: {{invoiceNumber}}",
|
||||
"invoiceReissued": "Invoice reissued: {{invoiceNumber}}",
|
||||
"invoicePaidAdminNotified": "Invoice marked paid: {{invoiceNumber}}",
|
||||
"invoicePaymentCheckRecorded": "Payment-check recorded for invoice: {{invoiceNumber}}",
|
||||
"invoicePaymentCheckSent": "Payment-check sent for invoice: {{invoiceNumber}}",
|
||||
"invoiceReleasedForDelivery": "Invoice released for delivery: {{invoiceNumber}}",
|
||||
"invoiceReminderSent": "Invoice reminder sent: {{invoiceNumber}}",
|
||||
"stornoSent": "Storno sent: {{invoiceNumber}}",
|
||||
"monthlyBillIssued": "Monthly bill issued for customer",
|
||||
"monthlyBillSkippedEmpty": "Monthly bill skipped (no entries)",
|
||||
"monthlyBillTriggeredManually": "Monthly bill triggered manually",
|
||||
"monthlyBillingItemsQueued": "Monthly billing items queued",
|
||||
"installmentPlanUpdated": "Installment plan updated",
|
||||
"expenseCreated": "Expense created",
|
||||
"expenseUpdated": "Expense updated",
|
||||
"expensePaid": "Expense marked paid",
|
||||
"expenseInvoiced": "Expense invoiced",
|
||||
"hourEntryLogged": "Hour entry logged",
|
||||
"hourEntryUpdated": "Hour entry updated",
|
||||
"hourEntryDeleted": "Hour entry deleted",
|
||||
"hourEntryLoggedToMonthlyDraft": "Hour entry logged to monthly draft",
|
||||
"hourEntriesBilled": "Hour entries billed to customer",
|
||||
"incomingInvoiceCaptured": "Incoming invoice captured",
|
||||
"incomingInvoiceCategorized": "Incoming invoice categorised",
|
||||
"incomingInvoiceUpdated": "Incoming invoice updated",
|
||||
"incomingInvoiceRebilled": "Incoming invoice re-billed to customer",
|
||||
"incomingInvoiceSupplierPayment": "Supplier payment recorded",
|
||||
"incomingMailConfigUpdated": "Incoming mail configuration updated",
|
||||
"customerCreatedPassive": "Passive customer created: {{email}}",
|
||||
"adminUserActivated": "Admin user activated: {{username}}",
|
||||
"adminUserDeleted": "Admin user deleted: {{username}}",
|
||||
"adminPasswordReset": "Admin password reset: {{username}}",
|
||||
"bulkArchiveCompleted": "Bulk archive completed: {{count}} events archived",
|
||||
"emailQueueFlushed": "Email queue flushed",
|
||||
"emailResent": "Creation email resent for {{eventName}}",
|
||||
"emailTemplateCreated": "Email template created: {{template_key}}",
|
||||
"eventDuplicated": "Event duplicated from {{source_event_name}}",
|
||||
"feedbackDeleted": "Feedback deleted",
|
||||
"feedbackModerated": "Feedback moderated",
|
||||
"feedbackSettingsUpdated": "Feedback settings updated for {{eventName}}",
|
||||
"wordFilterAdded": "Word filter added: {{word}}"
|
||||
},
|
||||
"notificationToasts": {
|
||||
"markedAllRead": "All notifications marked as read",
|
||||
@@ -1964,7 +2063,65 @@
|
||||
"photo_uploaded": "Photo uploaded to {{eventName}}",
|
||||
"category_hero_updated": "Category hero photo updated",
|
||||
"public_site_reset_to_default": "Public site reset to default",
|
||||
"cms_page_logo_uploaded": "CMS page logo uploaded: {{slug}}"
|
||||
"cms_page_logo_uploaded": "CMS page logo uploaded: {{slug}}",
|
||||
"contract_created": "Contract created: {{contractNumber}}",
|
||||
"contract_created_from_quote": "Contract created from quote: {{contractNumber}}",
|
||||
"contract_updated": "Contract updated: {{contractNumber}}",
|
||||
"contract_sent": "Contract sent: {{contractNumber}}",
|
||||
"contract_resent_signed": "Signed contract resent: {{contractNumber}}",
|
||||
"contract_signed_by_customer": "Contract signed by customer: {{contractNumber}}",
|
||||
"contract_signed_pdf_uploaded": "Signed contract PDF uploaded: {{contractNumber}}",
|
||||
"contract_signatures_restamped": "Contract signatures re-stamped: {{contractNumber}}",
|
||||
"contract_cancelled": "Contract cancelled: {{contractNumber}}",
|
||||
"contract_converted_to_event": "Contract converted to event: {{contractNumber}}",
|
||||
"contract_converted_to_empty_event": "Contract converted to empty event: {{contractNumber}}",
|
||||
"contract_converted_to_invoices": "Contract converted to invoices: {{contractNumber}}",
|
||||
"contract_converted_to_empty_invoice": "Contract converted to empty invoice: {{contractNumber}}",
|
||||
"quote_created": "Quote created: {{quoteNumber}}",
|
||||
"quote_sent": "Quote sent: {{quoteNumber}}",
|
||||
"quote_updated": "Quote updated: {{quoteNumber}}",
|
||||
"quote_accepted_by_admin": "Quote accepted: {{quoteNumber}}",
|
||||
"quote_declined_by_admin": "Quote declined: {{quoteNumber}}",
|
||||
"quote_converted": "Quote converted: {{quoteNumber}}",
|
||||
"quote_converted_invoices_only": "Quote converted to invoices: {{quoteNumber}}",
|
||||
"invoice_created": "Invoice created: {{invoiceNumber}}",
|
||||
"invoice_sent": "Invoice sent: {{invoiceNumber}}",
|
||||
"invoice_scheduled": "Invoice scheduled: {{invoiceNumber}}",
|
||||
"invoice_cancelled": "Invoice cancelled: {{invoiceNumber}}",
|
||||
"invoice_cancelled_via_storno": "Invoice cancelled via Storno: {{invoiceNumber}}",
|
||||
"invoice_reissued": "Invoice reissued: {{invoiceNumber}}",
|
||||
"invoice_paid_admin_notified": "Invoice marked paid: {{invoiceNumber}}",
|
||||
"invoice_payment_check_recorded": "Payment-check recorded for invoice: {{invoiceNumber}}",
|
||||
"invoice_payment_check_sent": "Payment-check sent for invoice: {{invoiceNumber}}",
|
||||
"invoice_released_for_delivery": "Invoice released for delivery: {{invoiceNumber}}",
|
||||
"invoice_reminder_sent": "Invoice reminder sent: {{invoiceNumber}}",
|
||||
"storno_sent": "Storno sent: {{invoiceNumber}}",
|
||||
"monthly_bill_issued": "Monthly bill issued for customer",
|
||||
"monthly_bill_skipped_empty": "Monthly bill skipped (no entries)",
|
||||
"monthly_bill_triggered_manually": "Monthly bill triggered manually",
|
||||
"monthly_billing_items_queued": "Monthly billing items queued",
|
||||
"installment_plan_updated": "Installment plan updated",
|
||||
"expense_created": "Expense created",
|
||||
"expense_updated": "Expense updated",
|
||||
"expense_paid": "Expense marked paid",
|
||||
"expense_invoiced": "Expense invoiced",
|
||||
"hour_entry_logged": "Hour entry logged",
|
||||
"hour_entry_updated": "Hour entry updated",
|
||||
"hour_entry_deleted": "Hour entry deleted",
|
||||
"hour_entry_logged_to_monthly_draft": "Hour entry logged to monthly draft",
|
||||
"hour_entries_billed": "Hour entries billed to customer",
|
||||
"incoming_invoice_captured": "Incoming invoice captured",
|
||||
"incoming_invoice_categorized": "Incoming invoice categorised",
|
||||
"incoming_invoice_updated": "Incoming invoice updated",
|
||||
"incoming_invoice_rebilled": "Incoming invoice re-billed to customer",
|
||||
"incoming_invoice_supplier_payment": "Supplier payment recorded",
|
||||
"incoming_mail_config_updated": "Incoming mail configuration updated",
|
||||
"customer_created_passive": "Passive customer created: {{email}}",
|
||||
"admin_user_activated": "Admin user activated: {{username}}",
|
||||
"admin_user_deleted": "Admin user deleted: {{username}}",
|
||||
"email_queue_flushed": "Email queue flushed",
|
||||
"email_template_created": "Email template created: {{template_key}}",
|
||||
"event_duplicated": "Event duplicated from {{source_event_name}}"
|
||||
}
|
||||
},
|
||||
"acceptInvitation": {
|
||||
@@ -3956,10 +4113,15 @@
|
||||
"settingsSaved": "Mappings saved.",
|
||||
"saveDefaults": "Save mappings",
|
||||
"accountType": {
|
||||
"asset": "Asset", "liability": "Liability", "equity": "Equity", "revenue": "Revenue", "expense": "Expense"
|
||||
"asset": "Asset",
|
||||
"liability": "Liability",
|
||||
"equity": "Equity",
|
||||
"revenue": "Revenue",
|
||||
"expense": "Expense"
|
||||
},
|
||||
"vatDirection": {
|
||||
"input": "Input (Vorsteuer)", "output": "Output (Umsatzsteuer)"
|
||||
"input": "Input (Vorsteuer)",
|
||||
"output": "Output (Umsatzsteuer)"
|
||||
},
|
||||
"defaults": {
|
||||
"title": "Default & system accounts",
|
||||
@@ -3974,20 +4136,40 @@
|
||||
"ledger_account_per_diem": "Per-diem account",
|
||||
"ledger_account_rebilled_revenue": "Re-billed expenses (revenue)"
|
||||
},
|
||||
"vatMap": { "title": "VAT code by tax treatment (costs)" },
|
||||
"outputVatMap": { "title": "VAT code by revenue rate", "empty": "Add output VAT codes above to configure a code per revenue rate." },
|
||||
"categoryMap": { "title": "Expense category → account" },
|
||||
"accounts": { "title": "Chart of accounts" },
|
||||
"vatCodes": { "title": "VAT codes" },
|
||||
"vatMap": {
|
||||
"title": "VAT code by tax treatment (costs)"
|
||||
},
|
||||
"outputVatMap": {
|
||||
"title": "VAT code by revenue rate",
|
||||
"empty": "Add output VAT codes above to configure a code per revenue rate."
|
||||
},
|
||||
"categoryMap": {
|
||||
"title": "Expense category → account"
|
||||
},
|
||||
"accounts": {
|
||||
"title": "Chart of accounts"
|
||||
},
|
||||
"vatCodes": {
|
||||
"title": "VAT codes"
|
||||
},
|
||||
"account": {
|
||||
"addTitle": "Add account", "editTitle": "Edit account",
|
||||
"number": "Account number", "name": "Name", "type": "Type",
|
||||
"addTitle": "Add account",
|
||||
"editTitle": "Edit account",
|
||||
"number": "Account number",
|
||||
"name": "Name",
|
||||
"type": "Type",
|
||||
"confirmDelete": "Delete this account?"
|
||||
},
|
||||
"vat": {
|
||||
"addTitle": "Add VAT code", "editTitle": "Edit VAT code",
|
||||
"code": "Code", "name": "Name", "rate": "Rate %", "direction": "Direction",
|
||||
"account": "VAT account", "noAccount": "— none —", "confirmDelete": "Delete this VAT code?",
|
||||
"addTitle": "Add VAT code",
|
||||
"editTitle": "Edit VAT code",
|
||||
"code": "Code",
|
||||
"name": "Name",
|
||||
"rate": "Rate %",
|
||||
"direction": "Direction",
|
||||
"account": "VAT account",
|
||||
"noAccount": "— none —",
|
||||
"confirmDelete": "Delete this VAT code?",
|
||||
"legacyRate": "{{rate}}% (not configured)"
|
||||
},
|
||||
"export": {
|
||||
|
||||
@@ -445,6 +445,78 @@ export const adminService = {
|
||||
'event_type_updated': `Event type updated: ${md.name || ''}`,
|
||||
'event_type_deleted': `Event type deleted: ${md.name || ''}`,
|
||||
'event_types_reordered': 'Event types reordered',
|
||||
// CRM — Contracts.
|
||||
'contract_created': `Contract created: ${md.contractNumber || ''}`,
|
||||
'contract_created_from_quote': `Contract created from quote: ${md.contractNumber || ''}`,
|
||||
'contract_updated': `Contract updated: ${md.contractNumber || ''}`,
|
||||
'contract_sent': `Contract sent: ${md.contractNumber || ''}`,
|
||||
'contract_resent_signed': `Signed contract resent: ${md.contractNumber || ''}`,
|
||||
'contract_signed_by_customer': `Contract signed by customer: ${md.contractNumber || ''}`,
|
||||
'contract_signed_pdf_uploaded': `Signed contract PDF uploaded: ${md.contractNumber || ''}`,
|
||||
'contract_signatures_restamped': `Contract signatures re-stamped: ${md.contractNumber || ''}`,
|
||||
'contract_cancelled': `Contract cancelled: ${md.contractNumber || ''}`,
|
||||
'contract_converted_to_event': `Contract converted to event: ${md.contractNumber || ''}`,
|
||||
'contract_converted_to_empty_event': `Contract converted to empty event: ${md.contractNumber || ''}`,
|
||||
'contract_converted_to_invoices': `Contract converted to invoices: ${md.contractNumber || ''}`,
|
||||
'contract_converted_to_empty_invoice': `Contract converted to empty invoice: ${md.contractNumber || ''}`,
|
||||
// CRM — Quotes.
|
||||
'quote_created': `Quote created: ${md.quoteNumber || ''}`,
|
||||
'quote_sent': `Quote sent: ${md.quoteNumber || ''}`,
|
||||
'quote_updated': `Quote updated: ${md.quoteNumber || ''}`,
|
||||
'quote_accepted_by_admin': `Quote accepted: ${md.quoteNumber || ''}`,
|
||||
'quote_declined_by_admin': `Quote declined: ${md.quoteNumber || ''}`,
|
||||
'quote_converted': `Quote converted: ${md.quoteNumber || ''}`,
|
||||
'quote_converted_invoices_only': `Quote converted to invoices: ${md.quoteNumber || ''}`,
|
||||
// CRM — Invoices / Storno.
|
||||
'invoice_created': `Invoice created: ${md.invoiceNumber || ''}`,
|
||||
'invoice_sent': `Invoice sent: ${md.invoiceNumber || ''}`,
|
||||
'invoice_scheduled': `Invoice scheduled: ${md.invoiceNumber || ''}`,
|
||||
'invoice_cancelled': `Invoice cancelled: ${md.invoiceNumber || ''}`,
|
||||
'invoice_cancelled_via_storno': `Invoice cancelled via Storno: ${md.invoiceNumber || ''}`,
|
||||
'invoice_reissued': `Invoice reissued: ${md.invoiceNumber || ''}`,
|
||||
'invoice_paid_admin_notified': `Invoice marked paid: ${md.invoiceNumber || ''}`,
|
||||
'invoice_payment_check_recorded': `Payment-check recorded for invoice: ${md.invoiceNumber || ''}`,
|
||||
'invoice_payment_check_sent': `Payment-check sent for invoice: ${md.invoiceNumber || ''}`,
|
||||
'invoice_released_for_delivery': `Invoice released for delivery: ${md.invoiceNumber || ''}`,
|
||||
'invoice_reminder_sent': `Invoice reminder sent: ${md.invoiceNumber || ''}`,
|
||||
'storno_sent': `Storno sent: ${md.invoiceNumber || ''}`,
|
||||
// CRM — Monthly billing.
|
||||
'monthly_bill_issued': 'Monthly bill issued for customer',
|
||||
'monthly_bill_skipped_empty': 'Monthly bill skipped (no entries)',
|
||||
'monthly_bill_triggered_manually': 'Monthly bill triggered manually',
|
||||
'monthly_billing_items_queued': 'Monthly billing items queued',
|
||||
'installment_plan_updated': 'Installment plan updated',
|
||||
// Accounting — Expenses + Hours + Incoming invoices.
|
||||
'expense_created': 'Expense created',
|
||||
'expense_updated': 'Expense updated',
|
||||
'expense_paid': 'Expense marked paid',
|
||||
'expense_invoiced': 'Expense invoiced',
|
||||
'hour_entry_logged': 'Hour entry logged',
|
||||
'hour_entry_updated': 'Hour entry updated',
|
||||
'hour_entry_deleted': 'Hour entry deleted',
|
||||
'hour_entry_logged_to_monthly_draft': 'Hour entry logged to monthly draft',
|
||||
'hour_entries_billed': 'Hour entries billed to customer',
|
||||
'incoming_invoice_captured': 'Incoming invoice captured',
|
||||
'incoming_invoice_categorized': 'Incoming invoice categorised',
|
||||
'incoming_invoice_updated': 'Incoming invoice updated',
|
||||
'incoming_invoice_rebilled': 'Incoming invoice re-billed to customer',
|
||||
'incoming_invoice_supplier_payment': 'Supplier payment recorded',
|
||||
'incoming_mail_config_updated': 'Incoming mail configuration updated',
|
||||
// Customers + Admin user mgmt.
|
||||
'customer_created_passive': `Passive customer created: ${md.email || ''}`,
|
||||
'admin_user_activated': `Admin user activated: ${md.username || ''}`,
|
||||
'admin_user_deleted': `Admin user deleted: ${md.username || ''}`,
|
||||
'admin_password_reset': `Admin password reset: ${md.username || ''}`,
|
||||
// Misc / legacy.
|
||||
'bulk_archive_completed': `Bulk archive completed: ${md.count || 0} events archived`,
|
||||
'email_queue_flushed': 'Email queue flushed',
|
||||
'email_resent': `Creation email resent for ${activity.eventName || ''}`,
|
||||
'email_template_created': `Email template created: ${md.template_key || ''}`,
|
||||
'event_duplicated': `Event duplicated from ${md.source_event_name || ''}`,
|
||||
'feedback_deleted': 'Feedback deleted',
|
||||
'feedback_moderated': 'Feedback moderated',
|
||||
'feedback_settings_updated': `Feedback settings updated for ${activity.eventName || ''}`,
|
||||
'word_filter_added': `Word filter added: ${md.word || ''}`,
|
||||
};
|
||||
|
||||
return messages[activity.type] || activity.type;
|
||||
|
||||
@@ -288,12 +288,27 @@ export const notificationsService = {
|
||||
slug: notification.metadata.slug,
|
||||
});
|
||||
|
||||
default:
|
||||
// Log unknown notification types for debugging
|
||||
default: {
|
||||
// Smart fallback: try to resolve admin.notificationMessages.<camelCase>
|
||||
// directly before giving up to the systemActivity template. Most CRM
|
||||
// and accounting activity types follow a stable shape (a doc number /
|
||||
// customer email / count interpolated into a short sentence), so we
|
||||
// add their translation entries to the locale files and rely on this
|
||||
// default to pick them up — no per-type switch case needed.
|
||||
const camelCase = notification.type.replace(/_(\w)/g, (_match, c) => c.toUpperCase());
|
||||
const key = `admin.notificationMessages.${camelCase}`;
|
||||
const translated = t(key, {
|
||||
eventName: notification.eventName,
|
||||
actorName: notification.actorName,
|
||||
...notification.metadata,
|
||||
});
|
||||
if (translated && translated !== key) return translated as string;
|
||||
// Truly unknown — log and render the legacy systemActivity template.
|
||||
console.warn('Unknown notification type:', notification.type, notification);
|
||||
return notification.metadata.message || t('admin.notificationMessages.systemActivity', {
|
||||
type: notification.type.replace(/_/g, ' ')
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user