From e1fe304dd3c8db6e0a2a560e432061ac4350afdb Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Wed, 25 Feb 2026 13:58:55 -0300 Subject: [PATCH] feat(i18n): migrate API Manager page (1008 lines, 43+ strings) - ApiManagerPageClient: full migration including stats cards, table headers, modals, permissions system, validation messages, and usage tips - PermissionsModal: search, model selection, allow/restrict toggles - Expanded apiManager namespace from 18 to 67 keys in en.json and pt-BR.json --- .../api-manager/ApiManagerPageClient.tsx | 129 +++++++++--------- src/i18n/messages/en.json | 57 +++++++- src/i18n/messages/pt-BR.json | 57 +++++++- 3 files changed, 168 insertions(+), 75 deletions(-) diff --git a/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx b/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx index 42f1e84c25..d8ab6b7f3f 100644 --- a/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx +++ b/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx @@ -3,6 +3,7 @@ import { useState, useEffect, useMemo, useCallback, memo } from "react"; import { Card, Button, Input, Modal, CardSkeleton } from "@/shared/components"; import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard"; +import { useTranslations } from "next-intl"; // Constants for validation const MAX_KEY_NAME_LENGTH = 100; @@ -70,6 +71,8 @@ interface Model { type ProviderGroup = [provider: string, models: Model[]]; export default function ApiManagerPageClient() { + const t = useTranslations("apiManager"); + const tc = useTranslations("common"); const [keys, setKeys] = useState([]); const [allModels, setAllModels] = useState([]); const [loading, setLoading] = useState(true); @@ -193,7 +196,7 @@ export default function ApiManagerPageClient() { return; } - if (!confirm("Delete this API key?")) return; + if (!confirm(t("deleteConfirm"))) return; setIsSubmitting(true); clearError(); @@ -332,7 +335,7 @@ export default function ApiManagerPageClient() {

{keys.length}

-

Total Keys

+

{t("totalKeys")}

@@ -348,7 +351,7 @@ export default function ApiManagerPageClient() { .length }

-

Restricted

+

{t("restricted")}

@@ -361,7 +364,7 @@ export default function ApiManagerPageClient() {

{Object.values(usageStats).reduce((sum, s) => sum + s.totalRequests, 0)}

-

Total Requests

+

{t("totalRequests")}

@@ -374,7 +377,7 @@ export default function ApiManagerPageClient() {

{allModels.length}

-

Models Available

+

{t("modelsAvailable")}

@@ -385,13 +388,11 @@ export default function ApiManagerPageClient() {
-

API Key Management

-

- Create and manage API keys for authenticating requests to your endpoint -

+

{t("keyManagement")}

+

{t("keyManagementDesc")}

@@ -404,42 +405,40 @@ export default function ApiManagerPageClient() { vpn_key
-

Registered Keys

+

{t("registeredKeys")}

- {keys.length} {keys.length === 1 ? "key" : "keys"} registered + {keys.length}{" "} + {keys.length === 1 + ? t("keyRegistered", { count: keys.length }) + : t("keysRegistered", { count: keys.length })}

-

- Each key isolates usage tracking and can be revoked independently. Keys are masked after - creation for security. -

+

{t("keysSecurityNote")}

{keys.length === 0 ? (
vpn_key
-

No API keys yet

-

- Create your first API key to authenticate requests to your endpoint -

+

{t("noKeys")}

+

{t("noKeysDesc")}

) : (
{/* Table Header */}
-
Name
-
Key
-
Permissions
-
Usage
-
Created
-
Actions
+
{t("name")}
+
{t("key")}
+
{t("permissions")}
+
{t("usage")}
+
{t("created")}
+
{t("actions")}
{/* Table Rows */} @@ -489,21 +488,21 @@ export default function ApiManagerPageClient() { className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-green-500/10 text-green-600 dark:text-green-400 text-xs font-medium hover:bg-green-500/20 transition-colors" > lock_open - All models + {t("allModels")} )}
{stats?.totalRequests ?? 0}{" "} - reqs + {t("reqs")} {stats?.lastUsed ? ( Last: {new Date(stats.lastUsed).toLocaleDateString()} ) : ( - Never used + {t("neverUsed")} )}
@@ -539,28 +538,23 @@ export default function ApiManagerPageClient() { lightbulb
-

Usage Tips

+

{t("usageTips")}

  • check - - Use API keys in the{" "} - Authorization{" "} - header as{" "} - Bearer YOUR_KEY - + {t("tipAuth")}
  • check - Keys are only shown once during creation — store them securely + {t("tipSecure")}
  • check - Create separate keys for different clients or environments + {t("tipSeparate")}
  • check - Restrict keys to specific models for better security and cost control + {t("tipRestrict")}
@@ -570,7 +564,7 @@ export default function ApiManagerPageClient() { {/* Add Key Modal */} { setShowAddModal(false); setNewKeyName(""); @@ -578,16 +572,16 @@ export default function ApiManagerPageClient() { >
- + setNewKeyName(e.target.value)} - placeholder="e.g., Production Key, Development Key" + placeholder={t("keyNamePlaceholder")} autoFocus /> -

- Choose a descriptive name to identify this key's purpose -

+

{t("keyNameDesc")}

{/* Created Key Modal */} - setCreatedKey(null)}> + setCreatedKey(null)}>
@@ -617,11 +611,9 @@ export default function ApiManagerPageClient() {

- Key created successfully! -

-

- Copy and store this key now — it won't be shown again. + {t("keyCreatedSuccess")}

+

{t("keyCreatedNote")}

@@ -632,7 +624,7 @@ export default function ApiManagerPageClient() { icon={copied === "created_key" ? "check" : "content_copy"} onClick={() => copy(createdKey, "created_key")} > - {copied === "created_key" ? "Copied!" : "Copy"} + {copied === "created_key" ? tc("copied") : tc("copy")}
@@ -823,9 +818,7 @@ const PermissionsModal = memo(function PermissionsModal({ allowAll ? "text-green-700 dark:text-green-300" : "text-amber-700 dark:text-amber-300" }`} > - {allowAll - ? "This key can access all available models." - : `This key can access ${selectedCount} of ${totalModels} models.`} + {allowAll ? t("allowAllDesc") : t("restrictDesc", { selectedCount, totalModels })}

@@ -833,19 +826,21 @@ const PermissionsModal = memo(function PermissionsModal({ {!allowAll && selectedCount > 0 && (
- {selectedCount} selected + + {t("selectedCount", { count: selectedCount })} +
@@ -877,7 +872,7 @@ const PermissionsModal = memo(function PermissionsModal({ onSearchChange(e.target.value)} - placeholder="Search models by name or provider..." + placeholder={t("searchModels")} icon="search" /> {searchModel && ( @@ -894,7 +889,7 @@ const PermissionsModal = memo(function PermissionsModal({ {modelsByProvider.length === 0 ? (
search_off -

No models found

+

{t("noModelsFound")}

) : ( modelsByProvider.map(([provider, models]) => { @@ -995,10 +990,10 @@ const PermissionsModal = memo(function PermissionsModal({ {/* Actions */}
diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 3aeac2d4dc..3ff00d2d01 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -152,8 +152,8 @@ "key": "Key", "revokeKey": "Revoke Key", "revokeConfirm": "Are you sure you want to revoke this API key?", - "noKeys": "No API keys created yet", - "noKeysDesc": "Create an API key to authenticate with OmniRoute", + "noKeys": "No API keys yet", + "noKeysDesc": "Create your first API key to authenticate requests to your endpoint", "keyLabel": "Key Label", "permissions": "Permissions", "expiresAt": "Expires", @@ -162,10 +162,59 @@ "showKey": "Show Key", "hideKey": "Hide Key", "copyKey": "Copy API Key", - "allModels": "All Models", + "allModels": "All models", "selectedModels": "Selected Models", "readOnly": "Read Only", - "fullAccess": "Full Access" + "fullAccess": "Full Access", + "keyManagement": "API Key Management", + "keyManagementDesc": "Create and manage API keys for authenticating requests to your endpoint", + "totalKeys": "Total Keys", + "restricted": "Restricted", + "totalRequests": "Total Requests", + "modelsAvailable": "Models Available", + "registeredKeys": "Registered Keys", + "keysRegistered": "{count} keys registered", + "keyRegistered": "{count} key registered", + "keysSecurityNote": "Each key isolates usage tracking and can be revoked independently. Keys are masked after creation for security.", + "createFirstKey": "Create Your First Key", + "name": "Name", + "usage": "Usage", + "created": "Created", + "actions": "Actions", + "reqs": "reqs", + "neverUsed": "Never used", + "deleteConfirm": "Delete this API key?", + "usageTips": "Usage Tips", + "tipAuth": "Use API keys in the Authorization header as Bearer YOUR_KEY", + "tipSecure": "Keys are only shown once during creation \u2014 store them securely", + "tipSeparate": "Create separate keys for different clients or environments", + "tipRestrict": "Restrict keys to specific models for better security and cost control", + "keyName": "Key Name", + "keyNamePlaceholder": "e.g., Production Key, Development Key", + "keyNameDesc": "Choose a descriptive name to identify this key's purpose", + "keyCreated": "API Key Created", + "keyCreatedSuccess": "Key created successfully!", + "keyCreatedNote": "Copy and store this key now \u2014 it won't be shown again.", + "done": "Done", + "savePermissions": "Save Permissions", + "allowAll": "Allow All", + "restrict": "Restrict", + "allowAllInfo": "This key can access all available models.", + "restrictInfo": "This key can access {selected} of {total} models.", + "selected": "{count} selected", + "all": "All", + "clear": "Clear", + "searchModels": "Search models by name or provider...", + "noModelsFound": "No models found", + "keyNameRequired": "Key name is required", + "keyNameTooLong": "Key name must be {max} characters or less", + "keyNameInvalid": "Key name can only contain letters, numbers, spaces, hyphens, and underscores", + "model": "{count} model", + "models": "{count} models", + "permissionsTitle": "Permissions: {name}", + "allowAllDesc": "This key can access all available models.", + "restrictDesc": "This key can access {selectedCount} of {totalModels} models.", + "selectedCount": "{count} selected" }, "auditLog": { "title": "Audit Log", diff --git a/src/i18n/messages/pt-BR.json b/src/i18n/messages/pt-BR.json index e3e1b04be8..017e68c758 100644 --- a/src/i18n/messages/pt-BR.json +++ b/src/i18n/messages/pt-BR.json @@ -152,8 +152,8 @@ "key": "Chave", "revokeKey": "Revogar Chave", "revokeConfirm": "Tem certeza que deseja revogar esta chave de API?", - "noKeys": "Nenhuma chave de API criada", - "noKeysDesc": "Crie uma chave de API para autenticar com o OmniRoute", + "noKeys": "Nenhuma chave de API ainda", + "noKeysDesc": "Crie sua primeira chave de API para autenticar requisições ao seu endpoint", "keyLabel": "Rótulo da Chave", "permissions": "Permissões", "expiresAt": "Expira", @@ -162,10 +162,59 @@ "showKey": "Mostrar Chave", "hideKey": "Ocultar Chave", "copyKey": "Copiar Chave de API", - "allModels": "Todos os Modelos", + "allModels": "Todos os modelos", "selectedModels": "Modelos Selecionados", "readOnly": "Somente Leitura", - "fullAccess": "Acesso Total" + "fullAccess": "Acesso Total", + "keyManagement": "Gerenciamento de Chaves de API", + "keyManagementDesc": "Crie e gerencie chaves de API para autenticar requisições ao seu endpoint", + "totalKeys": "Total de Chaves", + "restricted": "Restrita", + "totalRequests": "Total de Requisições", + "modelsAvailable": "Modelos Disponíveis", + "registeredKeys": "Chaves Registradas", + "keysRegistered": "{count} chaves registradas", + "keyRegistered": "{count} chave registrada", + "keysSecurityNote": "Cada chave isola o rastreamento de uso e pode ser revogada independentemente. As chaves são mascaradas após a criação por segurança.", + "createFirstKey": "Crie Sua Primeira Chave", + "name": "Nome", + "usage": "Uso", + "created": "Criado", + "actions": "Ações", + "reqs": "reqs", + "neverUsed": "Nunca usada", + "deleteConfirm": "Excluir esta chave de API?", + "usageTips": "Dicas de Uso", + "tipAuth": "Use chaves de API no cabeçalho Authorization como Bearer SUA_CHAVE", + "tipSecure": "As chaves são mostradas apenas uma vez durante a criação \u2014 armazene-as com segurança", + "tipSeparate": "Crie chaves separadas para diferentes clientes ou ambientes", + "tipRestrict": "Restrinja chaves a modelos específicos para maior segurança e controle de custos", + "keyName": "Nome da Chave", + "keyNamePlaceholder": "ex: Chave de Produção, Chave de Desenvolvimento", + "keyNameDesc": "Escolha um nome descritivo para identificar o propósito desta chave", + "keyCreated": "Chave de API Criada", + "keyCreatedSuccess": "Chave criada com sucesso!", + "keyCreatedNote": "Copie e armazene esta chave agora \u2014 ela não será mostrada novamente.", + "done": "Pronto", + "savePermissions": "Salvar Permissões", + "allowAll": "Permitir Tudo", + "restrict": "Restringir", + "allowAllInfo": "Esta chave pode acessar todos os modelos disponíveis.", + "restrictInfo": "Esta chave pode acessar {selected} de {total} modelos.", + "selected": "{count} selecionados", + "all": "Todos", + "clear": "Limpar", + "searchModels": "Buscar modelos por nome ou provedor...", + "noModelsFound": "Nenhum modelo encontrado", + "keyNameRequired": "Nome da chave é obrigatório", + "keyNameTooLong": "Nome da chave deve ter {max} caracteres ou menos", + "keyNameInvalid": "Nome da chave pode conter apenas letras, números, espaços, hífens e sublinhados", + "model": "{count} modelo", + "models": "{count} modelos", + "permissionsTitle": "Permissões: {name}", + "allowAllDesc": "Esta chave pode acessar todos os modelos disponíveis.", + "restrictDesc": "Esta chave pode acessar {selectedCount} de {totalModels} modelos.", + "selectedCount": "{count} selecionados" }, "auditLog": { "title": "Log de Auditoria",