mirror of
https://github.com/fosrl/pangolin.git
synced 2026-01-29 06:10:47 +00:00
Merge pull request #16 from vlalx/i18n_admin_api_keys
i18n in admin/api-keys
This commit is contained in:
@@ -286,6 +286,7 @@
|
||||
"apiKeysDelete": "API-Schlüssel löschen",
|
||||
"apiKeysManage": "API-Schlüssel verwalten",
|
||||
"apiKeysDescription": "API-Schlüssel werden zur Authentifizierung mit der Integrations-API verwendet",
|
||||
"apiKeysSettings": "{apiKeyName} Einstellungen",
|
||||
"userTitle": "Alle Benutzer verwalten",
|
||||
"userDescription": "Alle Benutzer im System anzeigen und verwalten",
|
||||
"userAbount": "Über Benutzerverwaltung",
|
||||
|
||||
@@ -286,6 +286,7 @@
|
||||
"apiKeysDelete": "Delete API Key",
|
||||
"apiKeysManage": "Manage API Keys",
|
||||
"apiKeysDescription": "API keys are used to authenticate with the integration API",
|
||||
"apiKeysSettings": "{apiKeyName} Settings",
|
||||
"userTitle": "Manage All Users",
|
||||
"userDescription": "View and manage all users in the system",
|
||||
"userAbount": "About User Management",
|
||||
|
||||
@@ -286,6 +286,7 @@
|
||||
"apiKeysDelete": "Supprimer la clé API",
|
||||
"apiKeysManage": "Gérer les clés API",
|
||||
"apiKeysDescription": "Les clés API sont utilisées pour s'authentifier avec l'API d'intégration",
|
||||
"apiKeysSettings": "Paramètres de {apiKeyName}",
|
||||
"userTitle": "Gérer tous les utilisateurs",
|
||||
"userDescription": "Voir et gérer tous les utilisateurs du système",
|
||||
"userAbount": "À propos de la gestion des utilisateurs",
|
||||
|
||||
@@ -286,6 +286,7 @@
|
||||
"apiKeysDelete": "Elimina Chiave API",
|
||||
"apiKeysManage": "Gestisci Chiavi API",
|
||||
"apiKeysDescription": "Le chiavi API sono utilizzate per autenticarsi con l'API di integrazione",
|
||||
"apiKeysSettings": "Impostazioni {apiKeyName}",
|
||||
"userTitle": "Gestisci Tutti Gli Utenti",
|
||||
"userDescription": "Visualizza e gestisci tutti gli utenti del sistema",
|
||||
"userAbount": "Informazioni Sulla Gestione Utente",
|
||||
|
||||
@@ -286,6 +286,7 @@
|
||||
"apiKeysDelete": "Usuń klucz API",
|
||||
"apiKeysManage": "Zarządzaj kluczami API",
|
||||
"apiKeysDescription": "Klucze API służą do uwierzytelniania z API integracji",
|
||||
"apiKeysSettings": "Ustawienia {apiKeyName}",
|
||||
"userTitle": "Zarządzaj wszystkimi użytkownikami",
|
||||
"userDescription": "Zobacz i zarządzaj wszystkimi użytkownikami w systemie",
|
||||
"userAbount": "O zarządzaniu użytkownikami",
|
||||
|
||||
@@ -286,6 +286,7 @@
|
||||
"apiKeysDelete": "Excluir Chave API",
|
||||
"apiKeysManage": "Gerenciar Chaves API",
|
||||
"apiKeysDescription": "As chaves API são usadas para autenticar com a API de integração",
|
||||
"apiKeysSettings": "Configurações de {apiKeyName}",
|
||||
"userTitle": "Gerenciar Todos os Usuários",
|
||||
"userDescription": "Visualizar e gerenciar todos os usuários no sistema",
|
||||
"userAbount": "Sobre a Gestão de Usuário",
|
||||
|
||||
@@ -286,6 +286,7 @@
|
||||
"apiKeysDelete": "Delete API Key",
|
||||
"apiKeysManage": "Manage API Keys",
|
||||
"apiKeysDescription": "API keys are used to authenticate with the integration API",
|
||||
"apiKeysSettings": "{apiKeyName} Settings",
|
||||
"userTitle": "Manage All Users",
|
||||
"userDescription": "View and manage all users in the system",
|
||||
"userAbount": "About User Management",
|
||||
|
||||
@@ -101,7 +101,7 @@ export default function OrgApiKeysTable({
|
||||
setIsDeleteModalOpen(true);
|
||||
}}
|
||||
>
|
||||
<span className="text-red-500">Delete</span>
|
||||
<span className="text-red-500">{t('delete')}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
@@ -55,7 +55,7 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsSectionTitle title={`${apiKey?.name} Settings`} />
|
||||
<SettingsSectionTitle title={t('apiKeysSettings', {apiKeyName: apiKey?.name})} />
|
||||
|
||||
<ApiKeyProvider apiKey={apiKey}>
|
||||
<HorizontalTabs items={navItems}>{children}</HorizontalTabs>
|
||||
|
||||
@@ -32,6 +32,7 @@ import { Input } from "@app/components/ui/input";
|
||||
import { DataTablePagination } from "@app/components/DataTablePagination";
|
||||
import { Plus, Search } from "lucide-react";
|
||||
import { DataTable } from "@app/components/ui/data-table";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[];
|
||||
@@ -44,15 +45,17 @@ export function ApiKeysDataTable<TData, TValue>({
|
||||
columns,
|
||||
data
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
|
||||
const t = useTranslations();
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={data}
|
||||
title="API Keys"
|
||||
searchPlaceholder="Search API keys..."
|
||||
title={t('apiKeys')}
|
||||
searchPlaceholder={t('searchApiKeys')}
|
||||
searchColumn="name"
|
||||
onAdd={addApiKey}
|
||||
addButtonText="Generate API Key"
|
||||
addButtonText={t('apiKeysAdd')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { createApiClient } from "@app/lib/api";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import moment from "moment";
|
||||
import { ApiKeysDataTable } from "./ApiKeysDataTable";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export type ApiKeyRow = {
|
||||
id: string;
|
||||
@@ -45,14 +46,16 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) {
|
||||
|
||||
const api = createApiClient(useEnvContext());
|
||||
|
||||
const t = useTranslations();
|
||||
|
||||
const deleteSite = (apiKeyId: string) => {
|
||||
api.delete(`/api-key/${apiKeyId}`)
|
||||
.catch((e) => {
|
||||
console.error("Error deleting API key", e);
|
||||
console.error(t('apiKeysErrorDelete'), e);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error deleting API key",
|
||||
description: formatAxiosError(e, "Error deleting API key")
|
||||
title: t('apiKeysErrorDelete'),
|
||||
description: formatAxiosError(e, t('apiKeysErrorDeleteMessage'))
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
@@ -86,7 +89,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) {
|
||||
setSelected(apiKeyROw);
|
||||
}}
|
||||
>
|
||||
<span>View settings</span>
|
||||
<span>{t('viewSettings')}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
@@ -94,7 +97,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) {
|
||||
setIsDeleteModalOpen(true);
|
||||
}}
|
||||
>
|
||||
<span className="text-red-500">Delete</span>
|
||||
<span className="text-red-500">{t('delete')}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
@@ -111,7 +114,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) {
|
||||
column.toggleSorting(column.getIsSorted() === "asc")
|
||||
}
|
||||
>
|
||||
Name
|
||||
{t('name')}
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
@@ -141,7 +144,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) {
|
||||
<div className="flex items-center justify-end">
|
||||
<Link href={`/admin/api-keys/${r.id}`}>
|
||||
<Button variant={"outlinePrimary"} className="ml-2">
|
||||
Edit
|
||||
{t('edit')}
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
@@ -163,27 +166,24 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) {
|
||||
dialog={
|
||||
<div className="space-y-4">
|
||||
<p>
|
||||
Are you sure you want to remove the API key{" "}
|
||||
<b>{selected?.name || selected?.id}</b>?
|
||||
{t('apiKeysQuestionRemove', {selectedApiKey: selected?.name || selected?.id})}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>
|
||||
Once removed, the API key will no longer be
|
||||
able to be used.
|
||||
{t('apiKeysMessageRemove')}
|
||||
</b>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To confirm, please type the name of the API key
|
||||
below.
|
||||
{t('apiKeysMessageConfirm')}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
buttonText="Confirm Delete API Key"
|
||||
buttonText={t('apiKeysDeleteConfirm')}
|
||||
onConfirm={async () => deleteSite(selected!.id)}
|
||||
string={selected.name}
|
||||
title="Delete API Key"
|
||||
title={t('apiKeysDelete')}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
import { GetApiKeyResponse } from "@server/routers/apiKeys";
|
||||
import ApiKeyProvider from "@app/providers/ApiKeyProvider";
|
||||
import { HorizontalTabs } from "@app/components/HorizontalTabs";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
interface SettingsLayoutProps {
|
||||
children: React.ReactNode;
|
||||
@@ -29,6 +30,8 @@ interface SettingsLayoutProps {
|
||||
export default async function SettingsLayout(props: SettingsLayoutProps) {
|
||||
const params = await props.params;
|
||||
|
||||
const t = useTranslations();
|
||||
|
||||
const { children } = props;
|
||||
|
||||
let apiKey = null;
|
||||
@@ -45,14 +48,14 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
title: "Permissions",
|
||||
title: t('apiKeysPermissionsTitle'),
|
||||
href: "/admin/api-keys/{apiKeyId}/permissions"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsSectionTitle title={`${apiKey?.name} Settings`} />
|
||||
<SettingsSectionTitle title={t('apiKeysSettings', {apiKeyName: apiKey?.name})} />
|
||||
|
||||
<ApiKeyProvider apiKey={apiKey}>
|
||||
<HorizontalTabs items={navItems}>{children}</HorizontalTabs>
|
||||
|
||||
@@ -23,12 +23,15 @@ import { ListApiKeyActionsResponse } from "@server/routers/apiKeys";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export default function Page() {
|
||||
const { env } = useEnvContext();
|
||||
const api = createApiClient({ env });
|
||||
const { apiKeyId } = useParams();
|
||||
|
||||
const t = useTranslations();
|
||||
|
||||
const [loadingPage, setLoadingPage] = useState<boolean>(true);
|
||||
const [selectedPermissions, setSelectedPermissions] = useState<
|
||||
Record<string, boolean>
|
||||
@@ -47,10 +50,10 @@ export default function Page() {
|
||||
.catch((e) => {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error loading API key actions",
|
||||
title: t('apiKeysPermissionsErrorLoadingActions'),
|
||||
description: formatAxiosError(
|
||||
e,
|
||||
"Error loading API key actions"
|
||||
t('apiKeysPermissionsErrorLoadingActions')
|
||||
)
|
||||
});
|
||||
});
|
||||
@@ -81,18 +84,18 @@ export default function Page() {
|
||||
)
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error("Error setting permissions", e);
|
||||
console.error(t('apiKeysPermissionsErrorUpdate'), e);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error setting permissions",
|
||||
title: t('apiKeysPermissionsErrorUpdate'),
|
||||
description: formatAxiosError(e)
|
||||
});
|
||||
});
|
||||
|
||||
if (actionsRes && actionsRes.status === 200) {
|
||||
toast({
|
||||
title: "Permissions updated",
|
||||
description: "The permissions have been updated."
|
||||
title: t('apiKeysPermissionsUpdated'),
|
||||
description: t('apiKeysPermissionsUpdatedDescription')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -106,10 +109,10 @@ export default function Page() {
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
Permissions
|
||||
{t('apiKeysPermissionsTitle')}
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
Determine what this API key can do
|
||||
{t('apiKeysPermissionsGeneralSettingsDescription')}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
@@ -127,7 +130,7 @@ export default function Page() {
|
||||
loading={loadingSavePermissions}
|
||||
disabled={loadingSavePermissions}
|
||||
>
|
||||
Save Permissions
|
||||
{t('apiKeysPermissionsSave')}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
</SettingsSectionBody>
|
||||
|
||||
@@ -59,15 +59,18 @@ import CopyToClipboard from "@app/components/CopyToClipboard";
|
||||
import moment from "moment";
|
||||
import CopyTextBox from "@app/components/CopyTextBox";
|
||||
import PermissionsSelectBox from "@app/components/PermissionsSelectBox";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
const t = useTranslations();
|
||||
|
||||
const createFormSchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(2, {
|
||||
message: "Name must be at least 2 characters."
|
||||
message: t('apiKeysNameMin')
|
||||
})
|
||||
.max(255, {
|
||||
message: "Name must not be longer than 255 characters."
|
||||
message: t('apiKeysNameMax')
|
||||
})
|
||||
});
|
||||
|
||||
@@ -82,7 +85,7 @@ const copiedFormSchema = z
|
||||
return data.copied;
|
||||
},
|
||||
{
|
||||
message: "You must confirm that you have copied the API key.",
|
||||
message: t('apiKeysConfirmCopy2'),
|
||||
path: ["copied"]
|
||||
}
|
||||
);
|
||||
@@ -127,7 +130,7 @@ export default function Page() {
|
||||
.catch((e) => {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error creating API key",
|
||||
title: t('apiKeysErrorCreate'),
|
||||
description: formatAxiosError(e)
|
||||
});
|
||||
});
|
||||
@@ -148,10 +151,10 @@ export default function Page() {
|
||||
)
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error("Error setting permissions", e);
|
||||
console.error(t('apiKeysErrorSetPermission'), e);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error setting permissions",
|
||||
title: t('apiKeysErrorSetPermission'),
|
||||
description: formatAxiosError(e)
|
||||
});
|
||||
});
|
||||
@@ -184,8 +187,8 @@ export default function Page() {
|
||||
<>
|
||||
<div className="flex justify-between">
|
||||
<HeaderTitle
|
||||
title="Generate API Key"
|
||||
description="Generate a new root access API key"
|
||||
title={t('apiKeysCreate')}
|
||||
description={t('apiKeysCreateDescription')}
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -205,7 +208,7 @@ export default function Page() {
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
API Key Information
|
||||
{t('apiKeysTitle')}
|
||||
</SettingsSectionTitle>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
@@ -221,7 +224,7 @@ export default function Page() {
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
Name
|
||||
{t('name')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
@@ -242,10 +245,10 @@ export default function Page() {
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
Permissions
|
||||
{t('apiKeysGeneralSettings')}
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
Determine what this API key can do
|
||||
{t('apiKeysGeneralSettingsDescription')}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
@@ -265,14 +268,14 @@ export default function Page() {
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
Your API Key
|
||||
{t('apiKeysList')}
|
||||
</SettingsSectionTitle>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
<InfoSections cols={2}>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
Name
|
||||
{t('name')}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
<CopyToClipboard
|
||||
@@ -282,7 +285,7 @@ export default function Page() {
|
||||
</InfoSection>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
Created
|
||||
{t('created')}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{moment(
|
||||
@@ -295,17 +298,15 @@ export default function Page() {
|
||||
<Alert variant="neutral">
|
||||
<InfoIcon className="h-4 w-4" />
|
||||
<AlertTitle className="font-semibold">
|
||||
Save Your API Key
|
||||
{t('apiKeysSave')}
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
You will only be able to see this
|
||||
once. Make sure to copy it to a
|
||||
secure place.
|
||||
{t('apiKeysSaveDescription')}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<h4 className="font-semibold">
|
||||
Your API key is:
|
||||
{t('apiKeysInfo')}
|
||||
</h4>
|
||||
|
||||
<CopyTextBox
|
||||
@@ -343,8 +344,7 @@ export default function Page() {
|
||||
htmlFor="terms"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
I have copied
|
||||
the API key
|
||||
{t('apiKeysConfirmCopy')}
|
||||
</label>
|
||||
</div>
|
||||
<FormMessage />
|
||||
@@ -368,7 +368,7 @@ export default function Page() {
|
||||
router.push(`/admin/api-keys`);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
)}
|
||||
{!apiKey && (
|
||||
@@ -380,7 +380,7 @@ export default function Page() {
|
||||
form.handleSubmit(onSubmit)();
|
||||
}}
|
||||
>
|
||||
Generate
|
||||
{t('generate')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -391,7 +391,7 @@ export default function Page() {
|
||||
copiedForm.handleSubmit(onCopiedSubmit)();
|
||||
}}
|
||||
>
|
||||
Done
|
||||
{t('done')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { AxiosResponse } from "axios";
|
||||
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
||||
import { ListRootApiKeysResponse } from "@server/routers/apiKeys";
|
||||
import ApiKeysTable, { ApiKeyRow } from "./ApiKeysTable";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
type ApiKeyPageProps = {};
|
||||
|
||||
@@ -16,6 +17,7 @@ export const dynamic = "force-dynamic";
|
||||
|
||||
export default async function ApiKeysPage(props: ApiKeyPageProps) {
|
||||
let apiKeys: ListRootApiKeysResponse["apiKeys"] = [];
|
||||
const t = useTranslations();
|
||||
try {
|
||||
const res = await internal.get<AxiosResponse<ListRootApiKeysResponse>>(
|
||||
`/api-keys`,
|
||||
@@ -36,8 +38,8 @@ export default async function ApiKeysPage(props: ApiKeyPageProps) {
|
||||
return (
|
||||
<>
|
||||
<SettingsSectionTitle
|
||||
title="Manage API Keys"
|
||||
description="API keys are used to authenticate with the integration API"
|
||||
title={t('apiKeysManage')}
|
||||
description={t('apiKeysDescription')}
|
||||
/>
|
||||
|
||||
<ApiKeysTable apiKeys={rows} />
|
||||
|
||||
Reference in New Issue
Block a user