mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
update
This commit is contained in:
@@ -62,7 +62,7 @@ export function MailFilterPopover() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-6 rounded-none px-3 gap-1.5 transition-colors',
|
'h-6 rounded-none px-3 gap-1.5 transition-colors border-l-0',
|
||||||
activeCount > 0 && 'bg-primary/10 text-primary hover:bg-primary/20'
|
activeCount > 0 && 'bg-primary/10 text-primary hover:bg-primary/20'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ export function MailboxPopover() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-6 rounded-none px-3 gap-1.5 transition-colors',
|
'h-6 rounded-none px-3 gap-1.5 transition-colors border-l-0',
|
||||||
selectedMailboxIds.length > 0 && 'bg-primary/10 text-primary border-primary/20'
|
selectedMailboxIds.length > 0 && 'bg-primary/10 text-primary border-primary/20'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ export function TagFilterPopover() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-6 gap-1.5 px-3 rounded-none',
|
'h-6 gap-1.5 px-3 rounded-none border-l-0',
|
||||||
selectedTags.length > 0 &&
|
selectedTags.length > 0 &&
|
||||||
'bg-primary/10 border-primary text-primary'
|
'bg-primary/10 border-primary text-primary'
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { useCurrentUser } from '@/hooks/use-current-user'
|
|||||||
import { PermissionsDialog } from './permissions-dialog'
|
import { PermissionsDialog } from './permissions-dialog'
|
||||||
import Logo from '@/assets/logo.svg'
|
import Logo from '@/assets/logo.svg'
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
|
import LongText from '@/components/long-text'
|
||||||
|
|
||||||
|
|
||||||
export function AccountAccessList() {
|
export function AccountAccessList() {
|
||||||
@@ -70,13 +71,15 @@ export function AccountAccessList() {
|
|||||||
if (!email) return null
|
if (!email) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card key={accountId} className="group hover:bg-accent/40 transition-all h-fit">
|
<Card key={accountId} className="group hover:bg-accent/40 transition-all h-fit min-w-0">
|
||||||
<CardHeader className="flex flex-row items-center gap-3 space-y-0 pb-3">
|
<CardHeader className="flex flex-row items-center gap-3 space-y-0 pb-3">
|
||||||
<div className="flex items-center justify-center w-10 h-10 rounded-full bg-primary/10 text-primary text-sm font-bold shrink-0">
|
<div className="flex items-center justify-center w-10 h-10 rounded-full bg-primary/10 text-primary text-sm font-bold shrink-0">
|
||||||
{email.charAt(0).toUpperCase()}
|
{email.charAt(0).toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col min-w-0">
|
<div className="flex flex-col min-w-0 flex-1">
|
||||||
<CardTitle className="text-sm font-semibold truncate">{email}</CardTitle>
|
<CardTitle className="text-sm font-semibold">
|
||||||
|
<LongText className="max-w-[248px]">{email}</LongText>
|
||||||
|
</CardTitle>
|
||||||
<CardDescription className="text-[10px] font-mono">
|
<CardDescription className="text-[10px] font-mono">
|
||||||
{t('settings.profile.account.id', { id: accountId })}
|
{t('settings.profile.account.id', { id: accountId })}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
@@ -35,9 +36,9 @@ import { useQuery } from "@tanstack/react-query"
|
|||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
const formatMB = (bytes?: number) => {
|
const formatMB = (bytes?: number) => {
|
||||||
if (!bytes) return "—";
|
if (!bytes) return "—"
|
||||||
return `${(bytes / 1024 / 1024).toFixed(0)} MB`;
|
return `${(bytes / 1024 / 1024).toFixed(0)} MB`
|
||||||
};
|
}
|
||||||
|
|
||||||
function BooleanBadge({ value }: { value: boolean }) {
|
function BooleanBadge({ value }: { value: boolean }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -58,13 +59,13 @@ function SettingRow({
|
|||||||
description?: string
|
description?: string
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="py-1.5 border-b border-border/40 last:border-0">
|
<div className="py-2.5 border-b border-border/40 last:border-0">
|
||||||
<div className="grid grid-cols-[1fr_auto] items-center gap-3">
|
<div className="grid grid-cols-[1fr_auto] items-center gap-4">
|
||||||
<div className="text-sm font-medium text-foreground/80 leading-tight font-mono">{label}</div>
|
<div className="text-xs font-medium text-muted-foreground font-mono">{label}</div>
|
||||||
<div className="text-sm text-right break-all leading-tight font-medium">{value}</div>
|
<div className="text-sm text-right font-medium">{value}</div>
|
||||||
</div>
|
</div>
|
||||||
{description && (
|
{description && (
|
||||||
<div className="mt-0.5 text-[11px] leading-tight text-muted-foreground">{description}</div>
|
<div className="mt-1 text-[11px] text-muted-foreground">{description}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -82,31 +83,36 @@ function SettingsCard({
|
|||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Card className="h-full shadow-sm">
|
<Card className="h-full">
|
||||||
<CardHeader className="flex flex-row items-center gap-2 py-3 bg-muted/20">
|
<CardHeader className="flex flex-row items-center gap-3 py-4 px-5 border-b bg-muted/20">
|
||||||
<Icon className="h-4 w-4 text-primary/70" />
|
<div className="p-1.5 rounded-md bg-primary/10">
|
||||||
|
<Icon className="h-3.5 w-3.5 text-primary" />
|
||||||
|
</div>
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<CardTitle className="text-sm font-semibold">{title}</CardTitle>
|
<CardTitle className="text-sm font-semibold">{title}</CardTitle>
|
||||||
{description && <CardDescription className="text-[11px] leading-none">{description}</CardDescription>}
|
{description && (
|
||||||
|
<CardDescription className="text-[11px]">{description}</CardDescription>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="pt-2 pb-1 px-4">{children}</CardContent>
|
<CardContent className="px-5 py-1">{children}</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function PageSkeleton() {
|
function PageSkeleton() {
|
||||||
return (
|
return (
|
||||||
<div className="p-4 grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||||
{Array.from({ length: 6 }).map((_, i) => (
|
{Array.from({ length: 6 }).map((_, i) => (
|
||||||
<Card key={i}>
|
<Card key={i}>
|
||||||
<CardHeader className="py-3">
|
<CardHeader className="py-4 px-5 border-b">
|
||||||
<Skeleton className="h-4 w-40" />
|
<Skeleton className="h-4 w-40" />
|
||||||
<Skeleton className="h-3 w-56" />
|
<Skeleton className="h-3 w-56" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-2">
|
<CardContent className="px-5 py-3 space-y-3">
|
||||||
<Skeleton className="h-3 w-full" />
|
<Skeleton className="h-3 w-full" />
|
||||||
<Skeleton className="h-3 w-5/6" />
|
<Skeleton className="h-3 w-5/6" />
|
||||||
|
<Skeleton className="h-3 w-4/6" />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
@@ -121,109 +127,119 @@ export default function ServerConfigurationsPage() {
|
|||||||
queryFn: get_system_configurations,
|
queryFn: get_system_configurations,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isLoading) return <ScrollArea className="h-full"><PageSkeleton /></ScrollArea>
|
if (isError || (!isLoading && !data)) {
|
||||||
if (isError || !data) {
|
return (
|
||||||
return <div className="p-8 text-center text-sm text-destructive">{t("systemConfig.fields.loadError")}</div>
|
<div className="p-8 text-center text-sm text-destructive">
|
||||||
|
{t("systemConfig.fields.loadError")}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full max-w-6xl mx-auto">
|
<div className="w-full">
|
||||||
<ScrollArea className="h-full w-full">
|
<ScrollArea className="h-full w-full">
|
||||||
<div className="px-6 pt-6 pb-2">
|
<div className="max-w-5xl mx-auto px-6 py-8 space-y-6">
|
||||||
<div className="flex items-start gap-4 p-5 rounded-xl border bg-gradient-to-br from-secondary/50 to-background shadow-inner">
|
|
||||||
<div className="p-2 bg-primary/10 rounded-lg">
|
<div className="flex items-start gap-3 p-4 rounded-xl border bg-muted/30">
|
||||||
<InfoIcon className="h-5 w-5 text-primary" />
|
<InfoIcon className="h-4 w-4 text-muted-foreground mt-0.5 shrink-0" />
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-base font-bold text-foreground">
|
<h4 className="text-sm font-semibold">{t("systemConfig.pageTitle")}</h4>
|
||||||
{t("systemConfig.pageTitle")}
|
<p className="text-xs text-muted-foreground mt-0.5 leading-relaxed">
|
||||||
</h4>
|
|
||||||
<p className="text-xs text-muted-foreground mt-1 max-w-2xl leading-relaxed">
|
|
||||||
{t("systemConfig.pageDescription")}
|
{t("systemConfig.pageDescription")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className="p-6 grid grid-cols-1 lg:grid-cols-2 gap-6">
|
{isLoading ? (
|
||||||
<SettingsCard
|
<PageSkeleton />
|
||||||
icon={Server}
|
) : (
|
||||||
title={t("systemConfig.sections.network.title")}
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
||||||
description={t("systemConfig.sections.network.desc")}
|
<SettingsCard
|
||||||
>
|
icon={Server}
|
||||||
<SettingRow label="BICHON_BIND_IP" value={data.bichon_bind_ip ?? "0.0.0.0"} />
|
title={t("systemConfig.sections.network.title")}
|
||||||
<SettingRow label="BICHON_HTTP_PORT" value={data.bichon_http_port} />
|
description={t("systemConfig.sections.network.desc")}
|
||||||
<SettingRow label="BICHON_BASE_URL" value={data.bichon_base_url} />
|
>
|
||||||
<SettingRow label="BICHON_PUBLIC_URL" value={data.bichon_public_url} />
|
<SettingRow label="BICHON_BIND_IP" value={data!.bichon_bind_ip ?? "0.0.0.0"} />
|
||||||
<SettingRow
|
<SettingRow label="BICHON_HTTP_PORT" value={data!.bichon_http_port} />
|
||||||
label="BICHON_ENABLE_REST_HTTPS"
|
<SettingRow label="BICHON_BASE_URL" value={data!.bichon_base_url} />
|
||||||
value={<BooleanBadge value={data.bichon_enable_rest_https} />}
|
<SettingRow label="BICHON_PUBLIC_URL" value={data!.bichon_public_url} />
|
||||||
/>
|
<SettingRow
|
||||||
</SettingsCard>
|
label="BICHON_ENABLE_REST_HTTPS"
|
||||||
<SettingsCard
|
value={<BooleanBadge value={data!.bichon_enable_rest_https} />}
|
||||||
icon={Mail}
|
/>
|
||||||
title={t("systemConfig.sections.smtp.title", "SMTP Server")}
|
</SettingsCard>
|
||||||
description={t("systemConfig.sections.smtp.desc", "Incoming mail reception settings")}
|
|
||||||
>
|
<SettingsCard
|
||||||
<SettingRow label="BICHON_ENABLE_SMTP" value={<BooleanBadge value={data.bichon_enable_smtp} />} />
|
icon={Mail}
|
||||||
<SettingRow label="BICHON_SMTP_PORT" value={data.bichon_smtp_port} />
|
title={t("systemConfig.sections.smtp.title")}
|
||||||
<SettingRow label="BICHON_SMTP_ENCRYPTION" value={data.bichon_smtp_encryption} />
|
description={t("systemConfig.sections.smtp.desc")}
|
||||||
<SettingRow label="BICHON_SMTP_AUTH_REQUIRED" value={<BooleanBadge value={data.bichon_smtp_auth_required} />} />
|
>
|
||||||
</SettingsCard>
|
<SettingRow label="BICHON_ENABLE_SMTP" value={<BooleanBadge value={data!.bichon_enable_smtp} />} />
|
||||||
<SettingsCard
|
<SettingRow label="BICHON_SMTP_PORT" value={data!.bichon_smtp_port} />
|
||||||
icon={Zap}
|
<SettingRow label="BICHON_SMTP_ENCRYPTION" value={data!.bichon_smtp_encryption} />
|
||||||
title={t("systemConfig.sections.performance.title")}
|
<SettingRow label="BICHON_SMTP_AUTH_REQUIRED" value={<BooleanBadge value={data!.bichon_smtp_auth_required} />} />
|
||||||
description={t("systemConfig.sections.performance.desc")}
|
</SettingsCard>
|
||||||
>
|
|
||||||
<SettingRow label="BICHON_SYNC_CONCURRENCY" value={data.bichon_sync_concurrency ?? t("systemConfig.status.auto")} />
|
<SettingsCard
|
||||||
<SettingRow
|
icon={Zap}
|
||||||
label="BICHON_HTTP_COMPRESSION_ENABLED"
|
title={t("systemConfig.sections.performance.title")}
|
||||||
value={<BooleanBadge value={data.bichon_http_compression_enabled} />}
|
description={t("systemConfig.sections.performance.desc")}
|
||||||
/>
|
>
|
||||||
</SettingsCard>
|
<SettingRow label="BICHON_SYNC_CONCURRENCY" value={data!.bichon_sync_concurrency ?? t("systemConfig.status.auto")} />
|
||||||
<SettingsCard
|
<SettingRow
|
||||||
icon={Database}
|
label="BICHON_HTTP_COMPRESSION_ENABLED"
|
||||||
title={t("systemConfig.sections.storage.title")}
|
value={<BooleanBadge value={data!.bichon_http_compression_enabled} />}
|
||||||
description={t("systemConfig.sections.storage.desc")}
|
/>
|
||||||
>
|
</SettingsCard>
|
||||||
<SettingRow label="BICHON_ROOT_DIR" value={<span className="font-mono">{data.bichon_root_dir}</span>} />
|
|
||||||
<SettingRow label="BICHON_DATA_DIR" value={data.bichon_data_dir ? <span className="font-mono">{data.bichon_data_dir}</span> : "—"} />
|
<SettingsCard
|
||||||
<SettingRow label="BICHON_INDEX_DIR" value={data.bichon_index_dir ? <span className="font-mono">{data.bichon_index_dir}</span> : "—"} />
|
icon={Database}
|
||||||
<SettingRow label="BICHON_METADATA_CACHE_SIZE" value={formatMB(data.bichon_metadata_cache_size)} />
|
title={t("systemConfig.sections.storage.title")}
|
||||||
<SettingRow label="BICHON_ENVELOPE_CACHE_SIZE" value={formatMB(data.bichon_envelope_cache_size)} />
|
description={t("systemConfig.sections.storage.desc")}
|
||||||
</SettingsCard>
|
>
|
||||||
<SettingsCard
|
<SettingRow label="BICHON_ROOT_DIR" value={<span className="font-mono">{data!.bichon_root_dir}</span>} />
|
||||||
icon={ShieldCheck}
|
<SettingRow label="BICHON_DATA_DIR" value={data!.bichon_data_dir ? <span className="font-mono">{data!.bichon_data_dir}</span> : "—"} />
|
||||||
title={t("systemConfig.sections.security.title")}
|
<SettingRow label="BICHON_INDEX_DIR" value={data!.bichon_index_dir ? <span className="font-mono">{data!.bichon_index_dir}</span> : "—"} />
|
||||||
description={t("systemConfig.sections.security.desc")}
|
<SettingRow label="BICHON_METADATA_CACHE_SIZE" value={formatMB(data!.bichon_metadata_cache_size)} />
|
||||||
>
|
<SettingRow label="BICHON_ENVELOPE_CACHE_SIZE" value={formatMB(data!.bichon_envelope_cache_size)} />
|
||||||
<SettingRow
|
</SettingsCard>
|
||||||
label="BICHON_ENCRYPT_PASSWORD_SET"
|
|
||||||
value={
|
<SettingsCard
|
||||||
data.bichon_encrypt_password_set ? (
|
icon={ShieldCheck}
|
||||||
<Badge variant="secondary" className="bg-emerald-500/10 text-emerald-600 border-emerald-500/20">
|
title={t("systemConfig.sections.security.title")}
|
||||||
{t("systemConfig.status.configured")}
|
description={t("systemConfig.sections.security.desc")}
|
||||||
</Badge>
|
>
|
||||||
) : (
|
<SettingRow
|
||||||
<Badge variant="destructive">{t("systemConfig.status.missing")}</Badge>
|
label="BICHON_ENCRYPT_PASSWORD_SET"
|
||||||
)
|
value={
|
||||||
}
|
data!.bichon_encrypt_password_set ? (
|
||||||
/>
|
<Badge variant="secondary" className="bg-emerald-500/10 text-emerald-600 border-emerald-500/20">
|
||||||
<SettingRow
|
{t("systemConfig.status.configured")}
|
||||||
label="BICHON_WEBUI_TOKEN_EXPIRATION_HOURS"
|
</Badge>
|
||||||
value={`${data.bichon_webui_token_expiration_hours}h`}
|
) : (
|
||||||
/>
|
<Badge variant="destructive">{t("systemConfig.status.missing")}</Badge>
|
||||||
</SettingsCard>
|
)
|
||||||
<SettingsCard
|
}
|
||||||
icon={Activity}
|
/>
|
||||||
title={t("systemConfig.sections.logging.title")}
|
<SettingRow
|
||||||
description={t("systemConfig.sections.logging.desc")}
|
label="BICHON_WEBUI_TOKEN_EXPIRATION_HOURS"
|
||||||
>
|
value={`${data!.bichon_webui_token_expiration_hours}h`}
|
||||||
<SettingRow label="BICHON_LOG_LEVEL" value={<Badge variant="outline" className="uppercase">{data.bichon_log_level}</Badge>} />
|
/>
|
||||||
<SettingRow label="BICHON_ANSI_LOGS" value={<BooleanBadge value={data.bichon_ansi_logs} />} />
|
</SettingsCard>
|
||||||
<SettingRow label="BICHON_JSON_LOGS" value={<BooleanBadge value={data.bichon_json_logs} />} />
|
|
||||||
<SettingRow label="BICHON_LOG_TO_FILE" value={<BooleanBadge value={data.bichon_log_to_file} />} />
|
<SettingsCard
|
||||||
<SettingRow label="BICHON_MAX_SERVER_LOG_FILES" value={data.bichon_max_server_log_files} />
|
icon={Activity}
|
||||||
</SettingsCard>
|
title={t("systemConfig.sections.logging.title")}
|
||||||
|
description={t("systemConfig.sections.logging.desc")}
|
||||||
|
>
|
||||||
|
<SettingRow label="BICHON_LOG_LEVEL" value={<Badge variant="outline" className="uppercase">{data!.bichon_log_level}</Badge>} />
|
||||||
|
<SettingRow label="BICHON_ANSI_LOGS" value={<BooleanBadge value={data!.bichon_ansi_logs} />} />
|
||||||
|
<SettingRow label="BICHON_JSON_LOGS" value={<BooleanBadge value={data!.bichon_json_logs} />} />
|
||||||
|
<SettingRow label="BICHON_LOG_TO_FILE" value={<BooleanBadge value={data!.bichon_log_to_file} />} />
|
||||||
|
<SettingRow label="BICHON_MAX_SERVER_LOG_FILES" value={data!.bichon_max_server_log_files} />
|
||||||
|
</SettingsCard>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { KeyRound, Palette, SettingsIcon, ShieldCheck, UserCog, Waypoints } from
|
|||||||
import { FixedHeader } from '@/components/layout/fixed-header'
|
import { FixedHeader } from '@/components/layout/fixed-header'
|
||||||
import { useCurrentUser } from '@/hooks/use-current-user'
|
import { useCurrentUser } from '@/hooks/use-current-user'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Separator } from '@/components/ui/separator'
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -68,6 +69,15 @@ export default function Settings() {
|
|||||||
<>
|
<>
|
||||||
<FixedHeader />
|
<FixedHeader />
|
||||||
<Main>
|
<Main>
|
||||||
|
<div className='space-y-0.5'>
|
||||||
|
<h1 className='text-2xl font-bold tracking-tight md:text-3xl'>
|
||||||
|
{t('settings.header.title')}
|
||||||
|
</h1>
|
||||||
|
<p className='text-muted-foreground'>
|
||||||
|
{t('settings.header.description')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Separator className='my-4 lg:my-6' />
|
||||||
<div className='flex flex-1 flex-col space-y-2 md:space-y-2 overflow-hidden lg:flex-row lg:space-x-12 lg:space-y-0'>
|
<div className='flex flex-1 flex-col space-y-2 md:space-y-2 overflow-hidden lg:flex-row lg:space-x-12 lg:space-y-0'>
|
||||||
<aside className='top-0 lg:sticky lg:w-1/5'>
|
<aside className='top-0 lg:sticky lg:w-1/5'>
|
||||||
<SidebarNav items={sidebarNavItems} />
|
<SidebarNav items={sidebarNavItems} />
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "تصفية الوكيل...",
|
"filterProxy": "تصفية الوكيل...",
|
||||||
"general": "عام",
|
"general": "عام",
|
||||||
"generateNewToken": "إنشاء رمز مميز جديد، القديم تنتهي صلاحيته على الفور.",
|
"generateNewToken": "إنشاء رمز مميز جديد، القديم تنتهي صلاحيته على الفور.",
|
||||||
|
"header": {
|
||||||
|
"description": "إدارة ملفك الشخصي وعرض تكوينات النظام.",
|
||||||
|
"title": "الإعدادات"
|
||||||
|
},
|
||||||
"host": "المُضيف",
|
"host": "المُضيف",
|
||||||
"id": "المعرّف",
|
"id": "المعرّف",
|
||||||
"invalidPassword": "كلمة مرور غير صالحة",
|
"invalidPassword": "كلمة مرور غير صالحة",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Filtrer Proxy...",
|
"filterProxy": "Filtrer Proxy...",
|
||||||
"general": "Generelt",
|
"general": "Generelt",
|
||||||
"generateNewToken": "Generer nyt token, det gamle udløber øjeblikkeligt.",
|
"generateNewToken": "Generer nyt token, det gamle udløber øjeblikkeligt.",
|
||||||
|
"header": {
|
||||||
|
"description": "Administrer din profil og se systemkonfigurationer.",
|
||||||
|
"title": "Indstillinger"
|
||||||
|
},
|
||||||
"host": "Vært",
|
"host": "Vært",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"invalidPassword": "Ugyldig adgangskode",
|
"invalidPassword": "Ugyldig adgangskode",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Proxys filtern...",
|
"filterProxy": "Proxys filtern...",
|
||||||
"general": "Allgemein",
|
"general": "Allgemein",
|
||||||
"generateNewToken": "Einen neuen Token generieren, der alte läuft sofort ab.",
|
"generateNewToken": "Einen neuen Token generieren, der alte läuft sofort ab.",
|
||||||
|
"header": {
|
||||||
|
"description": "Profil verwalten und Systemkonfigurationen anzeigen.",
|
||||||
|
"title": "Einstellungen"
|
||||||
|
},
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"invalidPassword": "Ungültiges Passwort",
|
"invalidPassword": "Ungültiges Passwort",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Filter Proxy...",
|
"filterProxy": "Filter Proxy...",
|
||||||
"general": "General",
|
"general": "General",
|
||||||
"generateNewToken": "Generate new token, old one expires immediately.",
|
"generateNewToken": "Generate new token, old one expires immediately.",
|
||||||
|
"header": {
|
||||||
|
"description": "Manage your account profile and view system configurations.",
|
||||||
|
"title": "Settings"
|
||||||
|
},
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"invalidPassword": "Invalid password",
|
"invalidPassword": "Invalid password",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Filtrar proxies...",
|
"filterProxy": "Filtrar proxies...",
|
||||||
"general": "General",
|
"general": "General",
|
||||||
"generateNewToken": "Generar un nuevo token, el antiguo caducará inmediatamente.",
|
"generateNewToken": "Generar un nuevo token, el antiguo caducará inmediatamente.",
|
||||||
|
"header": {
|
||||||
|
"description": "Administra tu perfil y consulta las configuraciones del sistema.",
|
||||||
|
"title": "Configuración"
|
||||||
|
},
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"invalidPassword": "Contraseña inválida",
|
"invalidPassword": "Contraseña inválida",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Suodata välityspalvelimet...",
|
"filterProxy": "Suodata välityspalvelimet...",
|
||||||
"general": "Yleinen",
|
"general": "Yleinen",
|
||||||
"generateNewToken": "Luo uusi tunnus, vanha vanhenee välittömästi.",
|
"generateNewToken": "Luo uusi tunnus, vanha vanhenee välittömästi.",
|
||||||
|
"header": {
|
||||||
|
"description": "Hallitse profiiliasi ja tarkastele järjestelmän määrityksiä.",
|
||||||
|
"title": "Asetukset"
|
||||||
|
},
|
||||||
"host": "Isäntä",
|
"host": "Isäntä",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"invalidPassword": "Virheellinen salasana",
|
"invalidPassword": "Virheellinen salasana",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Filtrer les Proxys...",
|
"filterProxy": "Filtrer les Proxys...",
|
||||||
"general": "Général",
|
"general": "Général",
|
||||||
"generateNewToken": "Génère un nouveau jeton, l'ancien expirera immédiatement.",
|
"generateNewToken": "Génère un nouveau jeton, l'ancien expirera immédiatement.",
|
||||||
|
"header": {
|
||||||
|
"description": "Gérez votre profil et consultez les configurations système.",
|
||||||
|
"title": "Paramètres"
|
||||||
|
},
|
||||||
"host": "Hôte",
|
"host": "Hôte",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"invalidPassword": "Mot de passe non valide",
|
"invalidPassword": "Mot de passe non valide",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Filtra Proxy...",
|
"filterProxy": "Filtra Proxy...",
|
||||||
"general": "Generale",
|
"general": "Generale",
|
||||||
"generateNewToken": "Genera un nuovo token, quello vecchio scadrà immediatamente.",
|
"generateNewToken": "Genera un nuovo token, quello vecchio scadrà immediatamente.",
|
||||||
|
"header": {
|
||||||
|
"description": "Gestisci il profilo e visualizza le configurazioni di sistema.",
|
||||||
|
"title": "Impostazioni"
|
||||||
|
},
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"invalidPassword": "Password non valida",
|
"invalidPassword": "Password non valida",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "プロキシをフィルター...",
|
"filterProxy": "プロキシをフィルター...",
|
||||||
"general": "一般",
|
"general": "一般",
|
||||||
"generateNewToken": "新しいトークンを生成します。古いトークンは直ちに失効します。",
|
"generateNewToken": "新しいトークンを生成します。古いトークンは直ちに失効します。",
|
||||||
|
"header": {
|
||||||
|
"description": "プロフィールの管理とシステム設定の確認。",
|
||||||
|
"title": "設定"
|
||||||
|
},
|
||||||
"host": "ホスト",
|
"host": "ホスト",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"invalidPassword": "無効なパスワード",
|
"invalidPassword": "無効なパスワード",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "프록시 필터링...",
|
"filterProxy": "프록시 필터링...",
|
||||||
"general": "일반",
|
"general": "일반",
|
||||||
"generateNewToken": "새 토큰을 생성합니다. 이전 토큰은 즉시 무효화됩니다.",
|
"generateNewToken": "새 토큰을 생성합니다. 이전 토큰은 즉시 무효화됩니다.",
|
||||||
|
"header": {
|
||||||
|
"description": "프로필 관리 및 시스템 설정 확인.",
|
||||||
|
"title": "설정"
|
||||||
|
},
|
||||||
"host": "호스트",
|
"host": "호스트",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"invalidPassword": "유효하지 않은 비밀번호",
|
"invalidPassword": "유효하지 않은 비밀번호",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Filter Proxy...",
|
"filterProxy": "Filter Proxy...",
|
||||||
"general": "Algemeen",
|
"general": "Algemeen",
|
||||||
"generateNewToken": "Genereer nieuw token, het oude verloopt onmiddellijk.",
|
"generateNewToken": "Genereer nieuw token, het oude verloopt onmiddellijk.",
|
||||||
|
"header": {
|
||||||
|
"description": "Beheer uw profiel en bekijk systeemconfiguraties.",
|
||||||
|
"title": "Instellingen"
|
||||||
|
},
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"invalidPassword": "Ongeldig wachtwoord",
|
"invalidPassword": "Ongeldig wachtwoord",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Filtrer Proxy...",
|
"filterProxy": "Filtrer Proxy...",
|
||||||
"general": "Generelt",
|
"general": "Generelt",
|
||||||
"generateNewToken": "Generer nytt token, det gamle utløper umiddelbart.",
|
"generateNewToken": "Generer nytt token, det gamle utløper umiddelbart.",
|
||||||
|
"header": {
|
||||||
|
"description": "Administrer profilen din og se systemkonfigurasjoner.",
|
||||||
|
"title": "Innstillinger"
|
||||||
|
},
|
||||||
"host": "Vert",
|
"host": "Vert",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"invalidPassword": "Ugyldig passord",
|
"invalidPassword": "Ugyldig passord",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Filtr Proxy...",
|
"filterProxy": "Filtr Proxy...",
|
||||||
"general": "Ogólne",
|
"general": "Ogólne",
|
||||||
"generateNewToken": "Wygeneruj nowy token, stary wygaśnie natychmiast.",
|
"generateNewToken": "Wygeneruj nowy token, stary wygaśnie natychmiast.",
|
||||||
|
"header": {
|
||||||
|
"description": "Zarządzaj profilem i przeglądaj konfiguracje systemu.",
|
||||||
|
"title": "Ustawienia"
|
||||||
|
},
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"invalidPassword": "Błędne hasło",
|
"invalidPassword": "Błędne hasło",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Filtrar Proxy...",
|
"filterProxy": "Filtrar Proxy...",
|
||||||
"general": "Geral",
|
"general": "Geral",
|
||||||
"generateNewToken": "Gera um novo token. O token antigo é imediatamente invalidado.",
|
"generateNewToken": "Gera um novo token. O token antigo é imediatamente invalidado.",
|
||||||
|
"header": {
|
||||||
|
"description": "Gerencie seu perfil e visualize as configurações do sistema.",
|
||||||
|
"title": "Configurações"
|
||||||
|
},
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"invalidPassword": "Senha Inválida",
|
"invalidPassword": "Senha Inválida",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Фильтр прокси...",
|
"filterProxy": "Фильтр прокси...",
|
||||||
"general": "Общие",
|
"general": "Общие",
|
||||||
"generateNewToken": "Сгенерировать новый токен, старый немедленно истечет.",
|
"generateNewToken": "Сгенерировать новый токен, старый немедленно истечет.",
|
||||||
|
"header": {
|
||||||
|
"description": "Управление профилем и просмотр конфигураций системы.",
|
||||||
|
"title": "Настройки"
|
||||||
|
},
|
||||||
"host": "Хост",
|
"host": "Хост",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"invalidPassword": "Неверный пароль",
|
"invalidPassword": "Неверный пароль",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "Filtrera Proxy...",
|
"filterProxy": "Filtrera Proxy...",
|
||||||
"general": "Allmänt",
|
"general": "Allmänt",
|
||||||
"generateNewToken": "Generera ny token, den gamla upphör att gälla omedelbart.",
|
"generateNewToken": "Generera ny token, den gamla upphör att gälla omedelbart.",
|
||||||
|
"header": {
|
||||||
|
"description": "Hantera din profil och visa systemkonfigurationer.",
|
||||||
|
"title": "Inställningar"
|
||||||
|
},
|
||||||
"host": "Värd",
|
"host": "Värd",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"invalidPassword": "Ogiltigt lösenord",
|
"invalidPassword": "Ogiltigt lösenord",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "篩選代理...",
|
"filterProxy": "篩選代理...",
|
||||||
"general": "一般",
|
"general": "一般",
|
||||||
"generateNewToken": "產生一個新權杖。舊權杖將立即失效。",
|
"generateNewToken": "產生一個新權杖。舊權杖將立即失效。",
|
||||||
|
"header": {
|
||||||
|
"description": "管理您的個人資料並查看系統配置。",
|
||||||
|
"title": "設置"
|
||||||
|
},
|
||||||
"host": "主機",
|
"host": "主機",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"invalidPassword": "密碼無效",
|
"invalidPassword": "密碼無效",
|
||||||
|
|||||||
@@ -1202,6 +1202,10 @@
|
|||||||
"filterProxy": "筛选代理...",
|
"filterProxy": "筛选代理...",
|
||||||
"general": "常规",
|
"general": "常规",
|
||||||
"generateNewToken": "生成新令牌,旧令牌立即失效。",
|
"generateNewToken": "生成新令牌,旧令牌立即失效。",
|
||||||
|
"header": {
|
||||||
|
"description": "管理您的个人资料并查看系统配置。",
|
||||||
|
"title": "设置"
|
||||||
|
},
|
||||||
"host": "主机",
|
"host": "主机",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"invalidPassword": "密码无效",
|
"invalidPassword": "密码无效",
|
||||||
|
|||||||
Reference in New Issue
Block a user