mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-07-12 17:35:21 +02:00
Revert "feat(i18n): update zh-Hans translation"
This commit is contained in:
@@ -33,7 +33,6 @@ import {
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import copy from "copy-to-clipboard";
|
||||
import { CodeEditor } from "@/components/shared/code-editor";
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string().min(1, "Name is required"),
|
||||
@@ -80,7 +79,6 @@ const REFILL_INTERVAL_OPTIONS = [
|
||||
];
|
||||
|
||||
export const AddApiKey = () => {
|
||||
const { t } = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [showSuccessModal, setShowSuccessModal] = useState(false);
|
||||
const [newApiKey, setNewApiKey] = useState("");
|
||||
@@ -97,7 +95,7 @@ export const AddApiKey = () => {
|
||||
void refetch();
|
||||
},
|
||||
onError: () => {
|
||||
toast.error(t("settings.api.errorGeneratingApiKey"));
|
||||
toast.error("Failed to generate API key");
|
||||
},
|
||||
});
|
||||
|
||||
@@ -142,13 +140,14 @@ export const AddApiKey = () => {
|
||||
<>
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button>{t("settings.api.generateNewKey")}</Button>
|
||||
<Button>Generate New Key</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("settings.api.generateApiKey")}</DialogTitle>
|
||||
<DialogTitle>Generate API Key</DialogTitle>
|
||||
<DialogDescription>
|
||||
{t("settings.api.createNewApiKeyDescription")}
|
||||
Create a new API key for accessing the API. You can set an
|
||||
expiration date and a custom prefix for better organization.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Form {...form}>
|
||||
@@ -158,12 +157,9 @@ export const AddApiKey = () => {
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("settings.api.name")}</FormLabel>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t("settings.api.namePlaceholder")}
|
||||
{...field}
|
||||
/>
|
||||
<Input placeholder="My API Key" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -174,12 +170,9 @@ export const AddApiKey = () => {
|
||||
name="prefix"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("settings.api.prefix")}</FormLabel>
|
||||
<FormLabel>Prefix</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t("settings.api.prefixPlaceholder")}
|
||||
{...field}
|
||||
/>
|
||||
<Input placeholder="my_app" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -190,7 +183,7 @@ export const AddApiKey = () => {
|
||||
name="expiresIn"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("settings.api.expiration")}</FormLabel>
|
||||
<FormLabel>Expiration</FormLabel>
|
||||
<Select
|
||||
value={field.value?.toString() || "0"}
|
||||
onValueChange={(value) =>
|
||||
@@ -199,17 +192,13 @@ export const AddApiKey = () => {
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t("settings.api.selectExpirationTime")}
|
||||
/>
|
||||
<SelectValue placeholder="Select expiration time" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{EXPIRATION_OPTIONS.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{t(
|
||||
`settings.api.expirationOptions.${option.label}`,
|
||||
)}
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -223,13 +212,11 @@ export const AddApiKey = () => {
|
||||
name="organizationId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("settings.api.organization")}</FormLabel>
|
||||
<FormLabel>Organization</FormLabel>
|
||||
<Select value={field.value} onValueChange={field.onChange}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t("settings.api.selectOrganization")}
|
||||
/>
|
||||
<SelectValue placeholder="Select organization" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
@@ -247,20 +234,16 @@ export const AddApiKey = () => {
|
||||
|
||||
{/* Rate Limiting Section */}
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<h3 className="text-lg font-medium">
|
||||
{t("settings.api.rateLimiting")}
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium">Rate Limiting</h3>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rateLimitEnabled"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel>
|
||||
{t("settings.api.enableRateLimiting")}
|
||||
</FormLabel>
|
||||
<FormLabel>Enable Rate Limiting</FormLabel>
|
||||
<FormDescription>
|
||||
{t("settings.api.limitRequestsDescription")}
|
||||
Limit the number of requests within a time window
|
||||
</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
@@ -280,7 +263,7 @@ export const AddApiKey = () => {
|
||||
name="rateLimitTimeWindow"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("settings.api.timeWindow")}</FormLabel>
|
||||
<FormLabel>Time Window</FormLabel>
|
||||
<Select
|
||||
value={field.value?.toString()}
|
||||
onValueChange={(value) =>
|
||||
@@ -289,11 +272,7 @@ export const AddApiKey = () => {
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t(
|
||||
"settings.api.selectTimeWindow",
|
||||
)}
|
||||
/>
|
||||
<SelectValue placeholder="Select time window" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
@@ -302,15 +281,13 @@ export const AddApiKey = () => {
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
>
|
||||
{t(
|
||||
`settings.api.timeWindowOptions.${option.label}`,
|
||||
)}
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormDescription>
|
||||
{t("settings.api.timeWindowDescription")}
|
||||
The duration in which requests are counted
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -321,13 +298,11 @@ export const AddApiKey = () => {
|
||||
name="rateLimitMax"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("settings.api.maxRequests")}</FormLabel>
|
||||
<FormLabel>Maximum Requests</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder={t(
|
||||
"settings.api.maxRequestsPlaceholder",
|
||||
)}
|
||||
placeholder="100"
|
||||
value={field.value?.toString() ?? ""}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
@@ -339,7 +314,8 @@ export const AddApiKey = () => {
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t("settings.api.maxRequestsDescription")}
|
||||
Maximum number of requests allowed within the time
|
||||
window
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -351,23 +327,17 @@ export const AddApiKey = () => {
|
||||
|
||||
{/* Request Limiting Section */}
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<h3 className="text-lg font-medium">
|
||||
{t("settings.api.requestLimiting")}
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium">Request Limiting</h3>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="remaining"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("settings.api.totalRequestLimit")}
|
||||
</FormLabel>
|
||||
<FormLabel>Total Request Limit</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder={t(
|
||||
"settings.api.totalRequestLimitPlaceholder",
|
||||
)}
|
||||
placeholder="Leave empty for unlimited"
|
||||
value={field.value?.toString() ?? ""}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
@@ -379,7 +349,8 @@ export const AddApiKey = () => {
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t("settings.api.totalRequestLimitDescription")}
|
||||
Total number of requests allowed (leave empty for
|
||||
unlimited)
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -391,13 +362,11 @@ export const AddApiKey = () => {
|
||||
name="refillAmount"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("settings.api.refillAmount")}</FormLabel>
|
||||
<FormLabel>Refill Amount</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder={t(
|
||||
"settings.api.refillAmountPlaceholder",
|
||||
)}
|
||||
placeholder="Amount to refill"
|
||||
value={field.value?.toString() ?? ""}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
@@ -409,7 +378,7 @@ export const AddApiKey = () => {
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t("settings.api.refillAmountDescription")}
|
||||
Number of requests to add on each refill
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -421,7 +390,7 @@ export const AddApiKey = () => {
|
||||
name="refillInterval"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("settings.api.refillInterval")}</FormLabel>
|
||||
<FormLabel>Refill Interval</FormLabel>
|
||||
<Select
|
||||
value={field.value?.toString()}
|
||||
onValueChange={(value) =>
|
||||
@@ -430,25 +399,19 @@ export const AddApiKey = () => {
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t(
|
||||
"settings.api.selectRefillInterval",
|
||||
)}
|
||||
/>
|
||||
<SelectValue placeholder="Select refill interval" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{REFILL_INTERVAL_OPTIONS.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{t(
|
||||
`settings.api.refillIntervalOptions.${option.label}`,
|
||||
)}
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormDescription>
|
||||
{t("settings.api.refillIntervalDescription")}
|
||||
How often to refill the request limit
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -462,9 +425,9 @@ export const AddApiKey = () => {
|
||||
variant="outline"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
{t("settings.api.cancel")}
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit">{t("settings.api.generate")}</Button>
|
||||
<Button type="submit">Generate</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
@@ -474,11 +437,9 @@ export const AddApiKey = () => {
|
||||
<Dialog open={showSuccessModal} onOpenChange={setShowSuccessModal}>
|
||||
<DialogContent className="sm:max-w-xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{t("settings.api.apiKeyGeneratedSuccessfully")}
|
||||
</DialogTitle>
|
||||
<DialogTitle>API Key Generated Successfully</DialogTitle>
|
||||
<DialogDescription>
|
||||
{t("settings.api.copyApiKeyNow")}
|
||||
Please copy your API key now. You won't be able to see it again!
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="mt-4 space-y-4">
|
||||
@@ -492,16 +453,16 @@ export const AddApiKey = () => {
|
||||
<Button
|
||||
onClick={() => {
|
||||
copy(newApiKey);
|
||||
toast.success(t("settings.api.apiKeyCopied"));
|
||||
toast.success("API key copied to clipboard");
|
||||
}}
|
||||
>
|
||||
{t("settings.api.copyToClipboard")}
|
||||
Copy to Clipboard
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setShowSuccessModal(false)}
|
||||
>
|
||||
{t("settings.api.close")}
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,11 +14,8 @@ import { formatDistanceToNow } from "date-fns";
|
||||
import { DialogAction } from "@/components/shared/dialog-action";
|
||||
import { AddApiKey } from "./add-api-key";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { getDateFnsLocaleByCode } from "@/lib/languages";
|
||||
|
||||
export const ShowApiKeys = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { data, refetch } = api.user.get.useQuery();
|
||||
const { mutateAsync: deleteApiKey, isLoading: isLoadingDelete } =
|
||||
api.user.deleteApiKey.useMutation();
|
||||
@@ -31,24 +28,22 @@ export const ShowApiKeys = () => {
|
||||
<div>
|
||||
<CardTitle className="text-xl flex items-center gap-2">
|
||||
<KeyIcon className="size-5" />
|
||||
{t("settings.api.apiCliKeys")}
|
||||
API/CLI Keys
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{t("settings.api.generateAndManageKeys")}
|
||||
Generate and manage API keys to access the API/CLI
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="flex flex-row gap-2 max-sm:flex-wrap items-end">
|
||||
<span className="text-sm font-medium text-muted-foreground">
|
||||
{t("settings.api.swaggerApi")}
|
||||
Swagger API:
|
||||
</span>
|
||||
<Link
|
||||
href="/swagger"
|
||||
target="_blank"
|
||||
className="flex flex-row gap-2 items-center"
|
||||
>
|
||||
<span className="text-sm font-medium">
|
||||
{t("settings.api.view")}
|
||||
</span>
|
||||
<span className="text-sm font-medium">View</span>
|
||||
<ExternalLinkIcon className="size-4" />
|
||||
</Link>
|
||||
</div>
|
||||
@@ -67,11 +62,9 @@ export const ShowApiKeys = () => {
|
||||
<div className="flex flex-wrap gap-2 items-center text-sm text-muted-foreground">
|
||||
<span className="flex items-center gap-1">
|
||||
<Clock className="size-3.5" />
|
||||
{t("settings.api.created")}{" "}
|
||||
{formatDistanceToNow(new Date(apiKey.createdAt), {
|
||||
locale: getDateFnsLocaleByCode(i18n.language),
|
||||
})}{" "}
|
||||
{t("settings.api.ago")}
|
||||
Created{" "}
|
||||
{formatDistanceToNow(new Date(apiKey.createdAt))}{" "}
|
||||
ago
|
||||
</span>
|
||||
{apiKey.prefix && (
|
||||
<Badge
|
||||
@@ -88,17 +81,17 @@ export const ShowApiKeys = () => {
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Clock className="size-3.5" />
|
||||
{t("settings.api.expiresIn")}{" "}
|
||||
{formatDistanceToNow(new Date(apiKey.expiresAt), {
|
||||
locale: getDateFnsLocaleByCode(i18n.language),
|
||||
})}{" "}
|
||||
Expires in{" "}
|
||||
{formatDistanceToNow(
|
||||
new Date(apiKey.expiresAt),
|
||||
)}{" "}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<DialogAction
|
||||
title={t("settings.api.deleteApiKey")}
|
||||
description={t("settings.api.deleteApiKeyDescription")}
|
||||
title="Delete API Key"
|
||||
description="Are you sure you want to delete this API key? This action cannot be undone."
|
||||
type="destructive"
|
||||
onClick={async () => {
|
||||
try {
|
||||
@@ -106,12 +99,12 @@ export const ShowApiKeys = () => {
|
||||
apiKeyId: apiKey.id,
|
||||
});
|
||||
await refetch();
|
||||
toast.success(t("settings.api.apiKeyDeleted"));
|
||||
toast.success("API key deleted successfully");
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: t("settings.api.errorDeletingApiKey"),
|
||||
: "Error deleting API key",
|
||||
);
|
||||
}
|
||||
}}
|
||||
@@ -131,7 +124,7 @@ export const ShowApiKeys = () => {
|
||||
<div className="flex flex-col items-center gap-3 py-6">
|
||||
<KeyIcon className="size-8 text-muted-foreground" />
|
||||
<span className="text-base text-muted-foreground">
|
||||
{t("settings.api.noApiKeysFound")}
|
||||
No API keys found
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -26,7 +26,6 @@ import { authClient } from "@/lib/auth-client";
|
||||
import { api } from "@/utils/api";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Fingerprint, QrCode } from "lucide-react";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import QRCode from "qrcode";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -56,7 +55,6 @@ type PinForm = z.infer<typeof PinSchema>;
|
||||
|
||||
export const Enable2FA = () => {
|
||||
const utils = api.useUtils();
|
||||
const { t } = useTranslation();
|
||||
const [data, setData] = useState<TwoFactorSetupData | null>(null);
|
||||
const [backupCodes, setBackupCodes] = useState<string[]>([]);
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||
@@ -88,15 +86,13 @@ export const Enable2FA = () => {
|
||||
});
|
||||
|
||||
setStep("verify");
|
||||
toast.success(t("settings.2fa.scanQrCode"));
|
||||
toast.success("Scan the QR code with your authenticator app");
|
||||
} else {
|
||||
throw new Error("No TOTP URI received from server");
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: t("settings.2fa.errorSettingUp"),
|
||||
error instanceof Error ? error.message : "Error setting up 2FA",
|
||||
);
|
||||
passwordForm.setError("password", {
|
||||
message:
|
||||
@@ -116,9 +112,9 @@ export const Enable2FA = () => {
|
||||
if (result.error) {
|
||||
if (result.error.code === "INVALID_TWO_FACTOR_AUTHENTICATION") {
|
||||
pinForm.setError("pin", {
|
||||
message: t("settings.2fa.invalidCode"),
|
||||
message: "Invalid code. Please try again.",
|
||||
});
|
||||
toast.error(t("settings.2fa.invalidVerificationCode"));
|
||||
toast.error("Invalid verification code");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -129,14 +125,14 @@ export const Enable2FA = () => {
|
||||
throw new Error("No response received from server");
|
||||
}
|
||||
|
||||
toast.success(t("settings.2fa.success"));
|
||||
toast.success("2FA configured successfully");
|
||||
utils.user.get.invalidate();
|
||||
setIsDialogOpen(false);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
const errorMessage =
|
||||
error.message === "Failed to fetch"
|
||||
? t("settings.2fa.connectionError")
|
||||
? "Connection error. Please check your internet connection."
|
||||
: error.message;
|
||||
|
||||
pinForm.setError("pin", {
|
||||
@@ -145,9 +141,9 @@ export const Enable2FA = () => {
|
||||
toast.error(errorMessage);
|
||||
} else {
|
||||
pinForm.setError("pin", {
|
||||
message: t("settings.2fa.errorVerifyingCode"),
|
||||
message: "Error verifying code",
|
||||
});
|
||||
toast.error(t("settings.2fa.errorVerifying2faCode"));
|
||||
toast.error("Error verifying 2FA code");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -181,16 +177,16 @@ export const Enable2FA = () => {
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="ghost">
|
||||
<Fingerprint className="size-4 text-muted-foreground" />
|
||||
{t("settings.2fa.enable2fa")}
|
||||
Enable 2FA
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("settings.2fa.title")}</DialogTitle>
|
||||
<DialogTitle>2FA Setup</DialogTitle>
|
||||
<DialogDescription>
|
||||
{step === "password"
|
||||
? t("settings.2fa.enterPassword")
|
||||
: t("settings.2fa.scanQrCodeAndVerify")}
|
||||
? "Enter your password to begin 2FA setup"
|
||||
: "Scan the QR code and verify with your authenticator app"}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -206,16 +202,16 @@ export const Enable2FA = () => {
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("settings.2fa.password")}</FormLabel>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder={t("settings.2fa.enterPasswordPlaceholder")}
|
||||
placeholder="Enter your password"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t("settings.2fa.enterPasswordDescription")}
|
||||
Enter your password to enable 2FA
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -226,7 +222,7 @@ export const Enable2FA = () => {
|
||||
className="w-full"
|
||||
isLoading={isPasswordLoading}
|
||||
>
|
||||
{t("settings.2fa.continue")}
|
||||
Continue
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
@@ -243,16 +239,16 @@ export const Enable2FA = () => {
|
||||
<div className="flex flex-col items-center gap-4 p-6 border rounded-lg">
|
||||
<QrCode className="size-5 text-muted-foreground" />
|
||||
<span className="text-sm font-medium">
|
||||
{t("settings.2fa.scanQrCode")}
|
||||
Scan this QR code with your authenticator app
|
||||
</span>
|
||||
<img
|
||||
src={data.qrCodeUrl}
|
||||
alt={t("settings.2fa.qrCodeAlt")}
|
||||
alt="2FA QR Code"
|
||||
className="rounded-lg w-48 h-48"
|
||||
/>
|
||||
<div className="flex flex-col gap-2 text-center">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{t("settings.2fa.cantScanQrCode")}
|
||||
Can't scan the QR code?
|
||||
</span>
|
||||
<span className="text-xs font-mono bg-muted p-2 rounded">
|
||||
{data.secret}
|
||||
@@ -262,9 +258,7 @@ export const Enable2FA = () => {
|
||||
|
||||
{backupCodes && backupCodes.length > 0 && (
|
||||
<div className="w-full space-y-3 border rounded-lg p-4">
|
||||
<h4 className="font-medium">
|
||||
{t("settings.2fa.backupCodes")}
|
||||
</h4>
|
||||
<h4 className="font-medium">Backup Codes</h4>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{backupCodes.map((code, index) => (
|
||||
<code
|
||||
@@ -276,7 +270,9 @@ export const Enable2FA = () => {
|
||||
))}
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("settings.2fa.saveBackupCodes")}
|
||||
Save these backup codes in a secure place. You can use
|
||||
them to access your account if you lose access to your
|
||||
authenticator device.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -293,7 +289,7 @@ export const Enable2FA = () => {
|
||||
name="pin"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-col justify-center items-center">
|
||||
<FormLabel>{t("settings.2fa.verificationCode")}</FormLabel>
|
||||
<FormLabel>Verification Code</FormLabel>
|
||||
<FormControl>
|
||||
<InputOTP maxLength={6} {...field}>
|
||||
<InputOTPGroup>
|
||||
@@ -307,7 +303,7 @@ export const Enable2FA = () => {
|
||||
</InputOTP>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t("settings.2fa.enterVerificationCode")}
|
||||
Enter the 6-digit code from your authenticator app
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -319,7 +315,7 @@ export const Enable2FA = () => {
|
||||
className="w-full"
|
||||
isLoading={isPasswordLoading}
|
||||
>
|
||||
{t("settings.2fa.enable2fa")}
|
||||
Enable 2FA
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -62,7 +62,7 @@ export const ProfileForm = () => {
|
||||
isError,
|
||||
error,
|
||||
} = api.user.update.useMutation();
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation("settings");
|
||||
const [gravatarHash, setGravatarHash] = useState<string | null>(null);
|
||||
|
||||
const availableAvatars = useMemo(() => {
|
||||
@@ -170,9 +170,7 @@ export const ProfileForm = () => {
|
||||
name="currentPassword"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("settings.profile.oldPassword")}
|
||||
</FormLabel>
|
||||
<FormLabel>Current Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
@@ -191,7 +189,7 @@ export const ProfileForm = () => {
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("settings.profile.newPassword")}
|
||||
{t("settings.profile.password")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
|
||||
@@ -18,7 +18,7 @@ import { TerminalModal } from "../../web-server/terminal-modal";
|
||||
import { GPUSupportModal } from "../gpu-support-modal";
|
||||
|
||||
export const ShowDokployActions = () => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation("settings");
|
||||
const { mutateAsync: reloadServer, isLoading } =
|
||||
api.settings.reloadServer.useMutation();
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ interface Props {
|
||||
serverId?: string;
|
||||
}
|
||||
export const ShowStorageActions = ({ serverId }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation("settings");
|
||||
const { mutateAsync: cleanAll, isLoading: cleanAllIsLoading } =
|
||||
api.settings.cleanAll.useMutation();
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ interface Props {
|
||||
serverId?: string;
|
||||
}
|
||||
export const ShowTraefikActions = ({ serverId }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation("settings");
|
||||
const { mutateAsync: reloadTraefik, isLoading: reloadTraefikIsLoading } =
|
||||
api.settings.reloadTraefik.useMutation();
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export const HandleServers = ({ serverId }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation("settings");
|
||||
|
||||
const utils = api.useUtils();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
@@ -44,7 +44,7 @@ import { ShowTraefikFileSystemModal } from "./show-traefik-file-system-modal";
|
||||
import { WelcomeSuscription } from "./welcome-stripe/welcome-suscription";
|
||||
|
||||
export const ShowServers = () => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation("settings");
|
||||
const router = useRouter();
|
||||
const query = router.query;
|
||||
const { data, refetch, isLoading } = api.server.all.useQuery();
|
||||
@@ -235,7 +235,9 @@ export const ShowServers = () => {
|
||||
serverId={server.serverId}
|
||||
>
|
||||
<span>
|
||||
{t("common.enterTerminal")}
|
||||
{t(
|
||||
"settings.common.enterTerminal",
|
||||
)}
|
||||
</span>
|
||||
</TerminalModal>
|
||||
)}
|
||||
|
||||
@@ -51,7 +51,7 @@ const addServerDomain = z
|
||||
type AddServerDomain = z.infer<typeof addServerDomain>;
|
||||
|
||||
export const WebDomain = () => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation("settings");
|
||||
const { data, refetch } = api.user.get.useQuery();
|
||||
const { mutateAsync, isLoading } =
|
||||
api.settings.assignDomainServer.useMutation();
|
||||
|
||||
@@ -15,7 +15,7 @@ import { ToggleDockerCleanup } from "./servers/actions/toggle-docker-cleanup";
|
||||
import { UpdateServer } from "./web-server/update-server";
|
||||
|
||||
export const WebServer = () => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation("settings");
|
||||
const { data } = api.user.get.useQuery();
|
||||
|
||||
const { data: dokployVersion } = api.settings.getDokployVersion.useQuery();
|
||||
|
||||
@@ -52,7 +52,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const LocalServerConfig = ({ onSave }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation("settings");
|
||||
|
||||
const form = useForm<Schema>({
|
||||
defaultValues: getLocalServerData(),
|
||||
|
||||
@@ -54,7 +54,7 @@ const TraefikPortsSchema = z.object({
|
||||
type TraefikPortsForm = z.infer<typeof TraefikPortsSchema>;
|
||||
|
||||
export const ManageTraefikPorts = ({ children, serverId }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation("settings");
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const form = useForm<TraefikPortsForm>({
|
||||
|
||||
Reference in New Issue
Block a user