Skip to content
Merged
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
46 changes: 1 addition & 45 deletions frontend/src/features/tenants/components/EditTenantDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ import { cn } from '@/shared/lib/utils'
import { Button } from '@/shared/components/ui/button'
import { Input } from '@/shared/components/ui/input'
import { tenantsHttpService } from '../services/tenants-http.service'
import type { Tenant, TenantStatus } from '../types/tenant.types'
import type { Tenant } from '../types/tenant.types'
import { Modal } from './Modal'
import { Field } from './Field'
import { tenantError } from './tenant-error'
import { ReactivateTenantDialog } from './ReactivateTenantDialog'

const STATUSES: TenantStatus[] = ['ACTIVE', 'SUSPENDED']

export function EditTenantDialog({
tenant,
Expand All @@ -22,27 +19,10 @@ export function EditTenantDialog({
tenant: Tenant
onClose: () => void
onSaved: () => void
}) {
if (tenant.status === 'TERMINATED') {
return <ReactivateTenantDialog tenant={tenant} onClose={onClose} onSaved={onSaved} />
}

return <EditTenantForm tenant={tenant} onClose={onClose} onSaved={onSaved} />
}

function EditTenantForm({
tenant,
onClose,
onSaved,
}: {
tenant: Tenant
onClose: () => void
onSaved: () => void
}) {
const { t } = useTranslation()
const [name, setName] = useState(tenant.name)
const [domain, setDomain] = useState(tenant.domain)
const [status, setStatus] = useState<TenantStatus>(tenant.status)
const [capped, setCapped] = useState(tenant.limits.maxAIRequests != null)
const [maxAI, setMaxAI] = useState(String(tenant.limits.maxAIRequests ?? ''))
const [busy, setBusy] = useState(false)
Expand All @@ -58,7 +38,6 @@ function EditTenantForm({
await tenantsHttpService.update(tenant.id, {
name: name.trim(),
domain: domain.trim().toLowerCase(),
status,
// Clearing the cap sends an explicit null: omitting the field would
// mean "leave it as it is", which is the opposite of the intent.
maxAIRequests: capped ? parsedMax : null,
Expand Down Expand Up @@ -96,29 +75,6 @@ function EditTenantForm({
<Input value={domain} onChange={(e) => setDomain(e.target.value)} className="font-mono" />
</Field>

<Field label={t('tenants.fields.status')}>
<div className="flex gap-2">
{STATUSES.map((s) => (
<button
key={s}
type="button"
onClick={() => setStatus(s)}
className={cn(
'flex-1 rounded-md border px-3 py-2 text-xs font-medium transition-colors',
status === s ? 'border-primary/40 bg-primary/5 text-foreground' : 'border-border text-muted-foreground hover:bg-muted/40'
)}
>
{t(`tenants.status.${s}`, { defaultValue: s })}
</button>
))}
</div>
{status === 'SUSPENDED' && (
<p className="text-[11px] text-amber-600 dark:text-amber-300">
{t('tenants.fields.suspendedWarning')}
</p>
)}
</Field>

<Field label={t('tenants.fields.aiLimit')} hint={t('tenants.fields.aiLimitHint')}>
<button
type="button"
Expand Down
32 changes: 21 additions & 11 deletions frontend/src/features/tenants/components/TenantCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,15 @@ export function TenantCard({
tenant,
readable,
onEdit,
onTerminate,
onActivate,
onDeactivate,
onDelete,
}: {
tenant: Tenant
readable: boolean
onEdit: () => void
onTerminate: () => void
onActivate: () => void
onDeactivate: () => void
onDelete: () => void
}) {
const { t } = useTranslation()
Expand Down Expand Up @@ -244,25 +246,33 @@ export function TenantCard({
>
<Pencil size={13} />
</button>
{!terminated ? (
{tenant.status === 'ACTIVE' ? (
<button
type="button"
onClick={onTerminate}
title={t('tenants.card.terminate')}
className="flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-red-500/10 hover:text-red-500"
onClick={onDeactivate}
title={t('tenants.card.deactivate')}
className="flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-amber-500/10 hover:text-amber-600"
>
<Trash2 size={13} />
<Power size={13} />
</button>
) : (
<button
type="button"
onClick={onDelete}
title={t('tenants.card.deletePermanent')}
className="flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground"
onClick={onActivate}
title={t('tenants.card.activate')}
className="flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-emerald-500/10 hover:text-emerald-600"
>
<Trash2 size={13} />
<Power size={13} />
</button>
)}
<button
type="button"
onClick={onDelete}
title={t('tenants.card.deletePermanent')}
className="flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground"
>
<Trash2 size={13} />
</button>
</div>
</div>

Expand Down
25 changes: 19 additions & 6 deletions frontend/src/features/tenants/pages/TenantsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { TenantCard } from '../components/TenantCard'
import { CreateTenantDialog } from '../components/CreateTenantDialog'
import { EditTenantDialog } from '../components/EditTenantDialog'
import { TerminateDialog } from '../components/TerminateDialog'
import { ReactivateTenantDialog } from '../components/ReactivateTenantDialog'
import { PermanentDeleteDialog } from '../components/PermanentDeleteDialog'

/* ─────────────────────────────────────────────────────────────────────────
Expand All @@ -34,7 +35,8 @@ export function TenantsPage() {
const [query, setQuery] = useState('')
const [creating, setCreating] = useState(false)
const [editing, setEditing] = useState<Tenant | null>(null)
const [terminating, setTerminating] = useState<Tenant | null>(null)
const [deactivating, setDeactivating] = useState<Tenant | null>(null)
const [activating, setActivating] = useState<Tenant | null>(null)
const [deletingPermanently, setDeletingPermanently] = useState<Tenant | null>(null)

const load = useCallback(async () => {
Expand Down Expand Up @@ -128,7 +130,8 @@ export function TenantsPage() {
tenant={tenant}
readable={canReadTenant(tenant)}
onEdit={() => setEditing(tenant)}
onTerminate={() => setTerminating(tenant)}
onActivate={() => setActivating(tenant)}
onDeactivate={() => setDeactivating(tenant)}
onDelete={() => setDeletingPermanently(tenant)}
/>
))}
Expand Down Expand Up @@ -157,12 +160,22 @@ export function TenantsPage() {
}}
/>
)}
{terminating && (
{deactivating && (
<TerminateDialog
tenant={terminating}
onClose={() => setTerminating(null)}
tenant={deactivating}
onClose={() => setDeactivating(null)}
onDone={() => {
setTerminating(null)
setDeactivating(null)
void load()
}}
/>
)}
{activating && (
<ReactivateTenantDialog
tenant={activating}
onClose={() => setActivating(null)}
onSaved={() => {
setActivating(null)
void load()
}}
/>
Expand Down
13 changes: 7 additions & 6 deletions frontend/src/shared/i18n/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -5428,7 +5428,8 @@
},
"card": {
"edit": "Bearbeiten",
"terminate": "Beenden",
"activate": "Aktivieren",
"deactivate": "Deaktivieren",
"deletePermanent": "Endgültig löschen",
"noAccess": "Dieser Mandant hat Ihnen keinen Zugriff gewährt.",
"loadingStats": "Wird gelesen…",
Expand Down Expand Up @@ -5464,15 +5465,15 @@
"submit": "Änderungen speichern"
},
"terminate": {
"title": "Diesen Mandanten beenden?",
"body": "Niemand aus {{name}} kann sich mehr anmelden. Die Daten bleiben erhalten, ein Betreiber kann es also zurücknehmen — aber der Kunde ist ab sofort offline.",
"title": "Diesen Mandanten deaktivieren?",
"body": "Niemand aus {{name}} kann sich mehr anmelden. Die Daten bleiben erhalten, es ist also umkehrbar — aber der Kunde ist ab sofort offline.",
"confirmLabel": "Zum Bestätigen {{name}} eingeben",
"submit": "Beenden"
"submit": "Deaktivieren"
},
"reactivate": {
"title": "Diesen Mandanten reaktivieren?",
"title": "Diesen Mandanten aktivieren?",
"body": "{{name}} kann sich wieder anmelden. Die aufbewahrten Daten werden unverändert wiederhergestellt.",
"submit": "Reaktivieren"
"submit": "Aktivieren"
},
"deletePermanent": {
"title": "Diesen Mandanten für immer löschen?",
Expand Down
13 changes: 7 additions & 6 deletions frontend/src/shared/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -5408,7 +5408,8 @@
},
"card": {
"edit": "Edit",
"terminate": "Terminate",
"activate": "Activate",
"deactivate": "Deactivate",
"deletePermanent": "Delete permanently",
"noAccess": "This tenant has not granted you access.",
"loadingStats": "Reading…",
Expand Down Expand Up @@ -5444,15 +5445,15 @@
"submit": "Save changes"
},
"terminate": {
"title": "Terminate this tenant?",
"body": "Nobody in {{name}} will be able to sign in. Their data is kept, so this is reversible by an operator, but it takes the customer offline now.",
"title": "Deactivate this tenant?",
"body": "Nobody in {{name}} will be able to sign in. Their data is kept, so this is reversible, but it takes the customer offline now.",
"confirmLabel": "Type {{name}} to confirm",
"submit": "Terminate"
"submit": "Deactivate"
},
"reactivate": {
"title": "Reactivate this tenant?",
"title": "Activate this tenant?",
"body": "{{name}} will be able to sign in again. Their preserved data is restored as-is.",
"submit": "Reactivate"
"submit": "Activate"
},
"deletePermanent": {
"title": "Delete this tenant forever?",
Expand Down
13 changes: 7 additions & 6 deletions frontend/src/shared/i18n/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -5390,7 +5390,8 @@
},
"card": {
"edit": "Editar",
"terminate": "Terminar",
"activate": "Activar",
"deactivate": "Desactivar",
"deletePermanent": "Eliminar permanentemente",
"noAccess": "Este tenant no te ha dado acceso.",
"loadingStats": "Leyendo…",
Expand Down Expand Up @@ -5426,15 +5427,15 @@
"submit": "Guardar cambios"
},
"terminate": {
"title": "¿Terminar este tenant?",
"body": "Nadie de {{name}} podrá iniciar sesión. Sus datos se conservan, así que un operador puede revertirlo, pero deja al cliente fuera ahora mismo.",
"title": "¿Desactivar este tenant?",
"body": "Nadie de {{name}} podrá iniciar sesión. Sus datos se conservan, así que es reversible, pero deja al cliente fuera ahora mismo.",
"confirmLabel": "Escribe {{name}} para confirmar",
"submit": "Terminar"
"submit": "Desactivar"
},
"reactivate": {
"title": "¿Reactivar este tenant?",
"title": "¿Activar este tenant?",
"body": "{{name}} podrá iniciar sesión de nuevo. Sus datos conservados se restauran tal cual.",
"submit": "Reactivar"
"submit": "Activar"
},
"deletePermanent": {
"title": "¿Eliminar este tenant para siempre?",
Expand Down
13 changes: 7 additions & 6 deletions frontend/src/shared/i18n/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -5428,7 +5428,8 @@
},
"card": {
"edit": "Modifier",
"terminate": "Résilier",
"activate": "Activer",
"deactivate": "Désactiver",
"deletePermanent": "Supprimer définitivement",
"noAccess": "Ce locataire ne vous a pas donné accès.",
"loadingStats": "Lecture…",
Expand Down Expand Up @@ -5464,15 +5465,15 @@
"submit": "Enregistrer"
},
"terminate": {
"title": "Résilier ce locataire ?",
"body": "Plus personne chez {{name}} ne pourra se connecter. Les données sont conservées, un opérateur peut donc revenir en arrière, mais le client est hors ligne dès maintenant.",
"title": "Désactiver ce locataire ?",
"body": "Plus personne chez {{name}} ne pourra se connecter. Les données sont conservées, c'est donc réversible, mais le client est hors ligne dès maintenant.",
"confirmLabel": "Saisissez {{name}} pour confirmer",
"submit": "Résilier"
"submit": "Désactiver"
},
"reactivate": {
"title": "Réactiver ce locataire ?",
"title": "Activer ce locataire ?",
"body": "{{name}} pourra se reconnecter. Les données conservées sont restaurées telles quelles.",
"submit": "Réactiver"
"submit": "Activer"
},
"deletePermanent": {
"title": "Supprimer ce locataire pour toujours ?",
Expand Down
13 changes: 7 additions & 6 deletions frontend/src/shared/i18n/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -5428,7 +5428,8 @@
},
"card": {
"edit": "Modifica",
"terminate": "Termina",
"activate": "Attiva",
"deactivate": "Disattiva",
"deletePermanent": "Elimina definitivamente",
"noAccess": "Questo tenant non ti ha dato accesso.",
"loadingStats": "Lettura…",
Expand Down Expand Up @@ -5464,15 +5465,15 @@
"submit": "Salva modifiche"
},
"terminate": {
"title": "Terminare questo tenant?",
"body": "Nessuno in {{name}} potrà più accedere. I dati restano, quindi un operatore può tornare indietro, ma il cliente resta fuori da subito.",
"title": "Disattivare questo tenant?",
"body": "Nessuno in {{name}} potrà più accedere. I dati restano, quindi è reversibile, ma il cliente resta fuori da subito.",
"confirmLabel": "Scrivi {{name}} per confermare",
"submit": "Termina"
"submit": "Disattiva"
},
"reactivate": {
"title": "Riattivare questo tenant?",
"title": "Attivare questo tenant?",
"body": "{{name}} potrà accedere di nuovo. I dati conservati vengono ripristinati così come sono.",
"submit": "Riattiva"
"submit": "Attiva"
},
"deletePermanent": {
"title": "Eliminare questo tenant per sempre?",
Expand Down
13 changes: 7 additions & 6 deletions frontend/src/shared/i18n/locales/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -5390,7 +5390,8 @@
},
"card": {
"edit": "Editar",
"terminate": "Encerrar",
"activate": "Ativar",
"deactivate": "Desativar",
"deletePermanent": "Excluir permanentemente",
"noAccess": "Este tenant não concedeu acesso a você.",
"loadingStats": "Lendo…",
Expand Down Expand Up @@ -5426,15 +5427,15 @@
"submit": "Salvar alterações"
},
"terminate": {
"title": "Encerrar este tenant?",
"body": "Ninguém em {{name}} conseguirá entrar. Os dados são mantidos, então um operador pode reverter, mas o cliente fica fora agora.",
"title": "Desativar este tenant?",
"body": "Ninguém em {{name}} conseguirá entrar. Os dados são mantidos, então é reversível, mas o cliente fica fora agora.",
"confirmLabel": "Digite {{name}} para confirmar",
"submit": "Encerrar"
"submit": "Desativar"
},
"reactivate": {
"title": "Reativar este tenant?",
"title": "Ativar este tenant?",
"body": "{{name}} poderá entrar novamente. Os dados preservados são restaurados como estavam.",
"submit": "Reativar"
"submit": "Ativar"
},
"deletePermanent": {
"title": "Excluir este tenant para sempre?",
Expand Down
Loading
Loading