i18n(frontend): translate all German UI strings to English
Replace German labels, button text, toast messages, table headers, tooltips, and placeholder strings across 7 files: - WorkflowEditor: buttons, toasts, node labels - Tenants: buttons, toasts, dialog text, table headers - Admin: widget layout description - OrderDetail: column headers (Baureihe→Series, Ebene→Level, Lagertyp→Bearing Type) - ExcelSpreadsheet: column label definitions - Upload: series/duplicate warning strings - TemplateEditor: ALL_FIELD_DEFS default labels API field names (baureihe, ebene1, produkt_baureihe etc.) unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -66,34 +66,34 @@ export default function TenantsPage() {
|
||||
const createMut = useMutation({
|
||||
mutationFn: (data: TenantCreate) => createTenant(data),
|
||||
onSuccess: () => {
|
||||
toast.success('Tenant erstellt')
|
||||
toast.success('Tenant created')
|
||||
qc.invalidateQueries({ queryKey: ['tenants'] })
|
||||
setShowCreate(false)
|
||||
setCreateForm({ name: '', slug: '', is_active: true })
|
||||
setSlugEdited(false)
|
||||
},
|
||||
onError: (e: any) => toast.error(e.response?.data?.detail || 'Fehler beim Erstellen'),
|
||||
onError: (e: any) => toast.error(e.response?.data?.detail || 'Failed to create'),
|
||||
})
|
||||
|
||||
const updateMut = useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: TenantUpdate }) => updateTenant(id, data),
|
||||
onSuccess: () => {
|
||||
toast.success('Tenant aktualisiert')
|
||||
toast.success('Tenant updated')
|
||||
qc.invalidateQueries({ queryKey: ['tenants'] })
|
||||
setEditingTenant(null)
|
||||
},
|
||||
onError: (e: any) => toast.error(e.response?.data?.detail || 'Fehler beim Aktualisieren'),
|
||||
onError: (e: any) => toast.error(e.response?.data?.detail || 'Failed to update'),
|
||||
})
|
||||
|
||||
const deleteMut = useMutation({
|
||||
mutationFn: (id: string) => deleteTenant(id),
|
||||
onSuccess: (_data, id) => {
|
||||
toast.success('Tenant gelöscht')
|
||||
toast.success('Tenant deleted')
|
||||
qc.invalidateQueries({ queryKey: ['tenants'] })
|
||||
if (activeTenantId === id) setActiveTenantId(null)
|
||||
setDeletingId(null)
|
||||
},
|
||||
onError: (e: any) => toast.error(e.response?.data?.detail || 'Fehler beim Löschen'),
|
||||
onError: (e: any) => toast.error(e.response?.data?.detail || 'Failed to delete'),
|
||||
})
|
||||
|
||||
// Auto-generate slug from name unless manually edited
|
||||
@@ -123,7 +123,7 @@ export default function TenantsPage() {
|
||||
<Building2 size={24} className="text-accent" />
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-content">Tenants</h1>
|
||||
<p className="text-sm text-content-muted">Mandanten verwalten und Kontext wählen</p>
|
||||
<p className="text-sm text-content-muted">Manage tenants and select context</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@@ -131,14 +131,14 @@ export default function TenantsPage() {
|
||||
className="flex items-center gap-2 px-4 py-2 bg-accent text-accent-text rounded-md text-sm font-medium hover:bg-accent-hover transition-colors"
|
||||
>
|
||||
<Plus size={16} />
|
||||
Neuer Tenant
|
||||
New Tenant
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Tenant Selector */}
|
||||
<div className="mb-6 p-4 bg-surface border border-border-default rounded-lg">
|
||||
<p className="text-xs font-semibold text-content-muted uppercase tracking-wider mb-2">
|
||||
Admin Cross-Tenant-Ansicht
|
||||
Admin Cross-Tenant View
|
||||
</p>
|
||||
<div className="relative inline-block">
|
||||
<button
|
||||
@@ -147,7 +147,7 @@ export default function TenantsPage() {
|
||||
>
|
||||
<Building2 size={14} className="text-content-muted shrink-0" />
|
||||
<span className="flex-1 text-left truncate">
|
||||
{activeTenant ? activeTenant.name : 'Alle Tenants / Admin-Ansicht'}
|
||||
{activeTenant ? activeTenant.name : 'All Tenants / Admin View'}
|
||||
</span>
|
||||
<ChevronDown size={14} className="text-content-muted shrink-0" />
|
||||
</button>
|
||||
@@ -159,7 +159,7 @@ export default function TenantsPage() {
|
||||
>
|
||||
{activeTenantId === null && <Check size={14} className="text-accent shrink-0" />}
|
||||
{activeTenantId !== null && <span className="w-[14px] shrink-0" />}
|
||||
<span>Alle Tenants / Admin-Ansicht</span>
|
||||
<span>All Tenants / Admin View</span>
|
||||
</button>
|
||||
{tenants.map((t) => (
|
||||
<button
|
||||
@@ -171,7 +171,7 @@ export default function TenantsPage() {
|
||||
{activeTenantId !== t.id && <span className="w-[14px] shrink-0" />}
|
||||
<span className="flex-1 truncate">{t.name}</span>
|
||||
{!t.is_active && (
|
||||
<span className="text-xs px-1.5 py-0.5 rounded bg-surface-muted text-content-muted">inaktiv</span>
|
||||
<span className="text-xs px-1.5 py-0.5 rounded bg-surface-muted text-content-muted">inactive</span>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
@@ -180,7 +180,7 @@ export default function TenantsPage() {
|
||||
</div>
|
||||
{activeTenant && (
|
||||
<p className="mt-2 text-xs text-content-muted">
|
||||
API-Requests werden mit Header <code className="font-mono bg-surface-alt px-1 rounded">X-Tenant-ID: {activeTenant.id}</code> gesendet.
|
||||
API requests are sent with header <code className="font-mono bg-surface-alt px-1 rounded">X-Tenant-ID: {activeTenant.id}</code>.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -194,22 +194,22 @@ export default function TenantsPage() {
|
||||
<th className="px-4 py-3 text-left font-semibold text-content-secondary">Slug</th>
|
||||
<th className="px-4 py-3 text-left font-semibold text-content-secondary">Status</th>
|
||||
<th className="px-4 py-3 text-left font-semibold text-content-secondary">
|
||||
<span className="flex items-center gap-1"><Users size={13} /> Nutzer</span>
|
||||
<span className="flex items-center gap-1"><Users size={13} /> Users</span>
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left font-semibold text-content-secondary">Erstellt</th>
|
||||
<th className="px-4 py-3 text-left font-semibold text-content-secondary">Created</th>
|
||||
<th className="px-4 py-3" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{isLoading && (
|
||||
<tr>
|
||||
<td colSpan={6} className="px-4 py-8 text-center text-content-muted">Lade Tenants…</td>
|
||||
<td colSpan={6} className="px-4 py-8 text-center text-content-muted">Loading tenants…</td>
|
||||
</tr>
|
||||
)}
|
||||
{!isLoading && tenants.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={6} className="px-4 py-8 text-center text-content-muted">
|
||||
Noch keine Tenants vorhanden.
|
||||
No tenants yet.
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
@@ -218,7 +218,7 @@ export default function TenantsPage() {
|
||||
<td className="px-4 py-3 font-medium text-content">
|
||||
<div className="flex items-center gap-2">
|
||||
{activeTenantId === tenant.id && (
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-accent shrink-0" title="Aktiver Kontext" />
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-accent shrink-0" title="Active context" />
|
||||
)}
|
||||
{tenant.name}
|
||||
</div>
|
||||
@@ -227,11 +227,11 @@ export default function TenantsPage() {
|
||||
<td className="px-4 py-3">
|
||||
{tenant.is_active ? (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-status-success-bg text-status-success-text">
|
||||
aktiv
|
||||
active
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-surface-muted text-content-muted">
|
||||
inaktiv
|
||||
inactive
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
@@ -244,14 +244,14 @@ export default function TenantsPage() {
|
||||
<button
|
||||
onClick={() => openEdit(tenant)}
|
||||
className="p-1.5 rounded hover:bg-surface-alt text-content-muted hover:text-content transition-colors"
|
||||
title="Bearbeiten"
|
||||
title="Edit"
|
||||
>
|
||||
<Pencil size={15} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setDeletingId(tenant.id)}
|
||||
className="p-1.5 rounded hover:bg-status-error-bg text-content-muted hover:text-status-error-text transition-colors"
|
||||
title="Löschen"
|
||||
title="Delete"
|
||||
>
|
||||
<Trash2 size={15} />
|
||||
</button>
|
||||
@@ -268,7 +268,7 @@ export default function TenantsPage() {
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
|
||||
<div className="bg-surface rounded-xl shadow-xl w-full max-w-md mx-4 p-6">
|
||||
<div className="flex items-center justify-between mb-5">
|
||||
<h2 className="text-lg font-semibold text-content">Neuer Tenant</h2>
|
||||
<h2 className="text-lg font-semibold text-content">New Tenant</h2>
|
||||
<button
|
||||
onClick={() => setShowCreate(false)}
|
||||
className="p-1.5 rounded hover:bg-surface-alt text-content-muted transition-colors"
|
||||
@@ -284,7 +284,7 @@ export default function TenantsPage() {
|
||||
type="text"
|
||||
value={createForm.name}
|
||||
onChange={(e) => handleCreateNameChange(e.target.value)}
|
||||
placeholder="z.B. Schaeffler GmbH"
|
||||
placeholder="e.g. Schaeffler GmbH"
|
||||
className="w-full px-3 py-2 rounded-md border border-border-default bg-surface text-content text-sm focus:outline-none focus:ring-2 focus:ring-accent/50"
|
||||
/>
|
||||
</div>
|
||||
@@ -292,13 +292,13 @@ export default function TenantsPage() {
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-content-secondary mb-1">
|
||||
Slug *
|
||||
<span className="text-xs font-normal text-content-muted ml-1">(URL-Kennung, automatisch generiert)</span>
|
||||
<span className="text-xs font-normal text-content-muted ml-1">(URL identifier, auto-generated)</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={createForm.slug}
|
||||
onChange={(e) => handleCreateSlugChange(e.target.value)}
|
||||
placeholder="z.B. schaeffler-gmbh"
|
||||
placeholder="e.g. schaeffler-gmbh"
|
||||
className="w-full px-3 py-2 rounded-md border border-border-default bg-surface text-content text-sm font-mono focus:outline-none focus:ring-2 focus:ring-accent/50"
|
||||
/>
|
||||
</div>
|
||||
@@ -313,7 +313,7 @@ export default function TenantsPage() {
|
||||
/>
|
||||
<div className="w-9 h-5 bg-surface-muted rounded-full peer peer-checked:bg-accent transition-colors after:content-[''] after:absolute after:top-0.5 after:left-0.5 after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:after:translate-x-4" />
|
||||
</label>
|
||||
<span className="text-sm text-content-secondary">Aktiv</span>
|
||||
<span className="text-sm text-content-secondary">Active</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -322,14 +322,14 @@ export default function TenantsPage() {
|
||||
onClick={() => setShowCreate(false)}
|
||||
className="px-4 py-2 text-sm rounded-md border border-border-default text-content-secondary hover:bg-surface-alt transition-colors"
|
||||
>
|
||||
Abbrechen
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={() => createMut.mutate(createForm)}
|
||||
disabled={!createForm.name.trim() || !createForm.slug.trim() || createMut.isPending}
|
||||
className="px-4 py-2 text-sm rounded-md bg-accent text-accent-text font-medium hover:bg-accent-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{createMut.isPending ? 'Erstelle…' : 'Erstellen'}
|
||||
{createMut.isPending ? 'Creating…' : 'Create'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -341,7 +341,7 @@ export default function TenantsPage() {
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
|
||||
<div className="bg-surface rounded-xl shadow-xl w-full max-w-md mx-4 p-6">
|
||||
<div className="flex items-center justify-between mb-5">
|
||||
<h2 className="text-lg font-semibold text-content">Tenant bearbeiten</h2>
|
||||
<h2 className="text-lg font-semibold text-content">Edit Tenant</h2>
|
||||
<button
|
||||
onClick={() => setEditingTenant(null)}
|
||||
className="p-1.5 rounded hover:bg-surface-alt text-content-muted transition-colors"
|
||||
@@ -381,7 +381,7 @@ export default function TenantsPage() {
|
||||
/>
|
||||
<div className="w-9 h-5 bg-surface-muted rounded-full peer peer-checked:bg-accent transition-colors after:content-[''] after:absolute after:top-0.5 after:left-0.5 after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:after:translate-x-4" />
|
||||
</label>
|
||||
<span className="text-sm text-content-secondary">Aktiv</span>
|
||||
<span className="text-sm text-content-secondary">Active</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -390,14 +390,14 @@ export default function TenantsPage() {
|
||||
onClick={() => setEditingTenant(null)}
|
||||
className="px-4 py-2 text-sm rounded-md border border-border-default text-content-secondary hover:bg-surface-alt transition-colors"
|
||||
>
|
||||
Abbrechen
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={() => updateMut.mutate({ id: editingTenant.id, data: editForm })}
|
||||
disabled={updateMut.isPending}
|
||||
className="px-4 py-2 text-sm rounded-md bg-accent text-accent-text font-medium hover:bg-accent-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{updateMut.isPending ? 'Speichere…' : 'Speichern'}
|
||||
{updateMut.isPending ? 'Saving…' : 'Save'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -413,9 +413,9 @@ export default function TenantsPage() {
|
||||
<Trash2 size={16} className="text-status-error-text" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-base font-semibold text-content">Tenant löschen?</h2>
|
||||
<h2 className="text-base font-semibold text-content">Delete tenant?</h2>
|
||||
<p className="text-sm text-content-muted mt-1">
|
||||
Diese Aktion kann nicht rückgängig gemacht werden.
|
||||
This action cannot be undone.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -424,14 +424,14 @@ export default function TenantsPage() {
|
||||
onClick={() => setDeletingId(null)}
|
||||
className="px-4 py-2 text-sm rounded-md border border-border-default text-content-secondary hover:bg-surface-alt transition-colors"
|
||||
>
|
||||
Abbrechen
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={() => deleteMut.mutate(deletingId)}
|
||||
disabled={deleteMut.isPending}
|
||||
className="px-4 py-2 text-sm rounded-md bg-status-error-bg text-status-error-text font-medium hover:opacity-80 disabled:opacity-50 disabled:cursor-not-allowed transition-opacity"
|
||||
>
|
||||
{deleteMut.isPending ? 'Lösche…' : 'Löschen'}
|
||||
{deleteMut.isPending ? 'Deleting…' : 'Delete'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user