mirror of
https://github.com/perfect-panel/ppanel-web.git
synced 2026-02-06 03:30:25 -05:00
🐛 fix: Add localization updates and new utility functions
This commit is contained in:
parent
e4fbd5c754
commit
4da59609b4
@ -2,11 +2,11 @@
|
||||
|
||||
import { ProTable, ProTableActions } from '@/components/pro-table';
|
||||
import { createAds, deleteAds, getAdsList, updateAds } from '@/services/admin/ads';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import { Switch } from '@workspace/ui/components/switch';
|
||||
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useRef, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
@ -10,12 +10,12 @@ import {
|
||||
updateCoupon,
|
||||
} from '@/services/admin/coupon';
|
||||
import { getSubscribeList } from '@/services/admin/subscribe';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import { Switch } from '@workspace/ui/components/switch';
|
||||
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useRef, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
@ -8,10 +8,10 @@ import {
|
||||
getDocumentList,
|
||||
updateDocument,
|
||||
} from '@/services/admin/document';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import { Switch } from '@workspace/ui/components/switch';
|
||||
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useRef, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import { UserDetail } from '@/app/dashboard/user/user-detail';
|
||||
import { ProTable } from '@/components/pro-table';
|
||||
import { filterBalanceLog } from '@/services/admin/log';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import { UserDetail } from '@/app/dashboard/user/user-detail';
|
||||
import { ProTable } from '@/components/pro-table';
|
||||
import { filterCommissionLog } from '@/services/admin/log';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
import { ProTable } from '@/components/pro-table';
|
||||
import { filterEmailLog } from '@/services/admin/log';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import { UserDetail } from '@/app/dashboard/user/user-detail';
|
||||
import { ProTable } from '@/components/pro-table';
|
||||
import { filterGiftLog } from '@/services/admin/log';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
|
||||
@ -4,8 +4,8 @@ import { UserDetail } from '@/app/dashboard/user/user-detail';
|
||||
import { IpLink } from '@/components/ip-link';
|
||||
import { ProTable } from '@/components/pro-table';
|
||||
import { filterLoginLog } from '@/services/admin/log';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
import { ProTable } from '@/components/pro-table';
|
||||
import { filterMobileLog } from '@/services/admin/log';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
export default function MobileLogPage() {
|
||||
|
||||
@ -4,7 +4,7 @@ import { UserDetail } from '@/app/dashboard/user/user-detail';
|
||||
import { IpLink } from '@/components/ip-link';
|
||||
import { ProTable } from '@/components/pro-table';
|
||||
import { filterRegisterLog } from '@/services/admin/log';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import { UserDetail } from '@/app/dashboard/user/user-detail';
|
||||
import { ProTable } from '@/components/pro-table';
|
||||
import { filterResetSubscribeLog } from '@/services/admin/log';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import { UserDetail } from '@/app/dashboard/user/user-detail';
|
||||
import { IpLink } from '@/components/ip-link';
|
||||
import { ProTable } from '@/components/pro-table';
|
||||
import { filterSubscribeLog } from '@/services/admin/log';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
|
||||
@ -3,7 +3,8 @@
|
||||
import { UserDetail } from '@/app/dashboard/user/user-detail';
|
||||
import { ProTable } from '@/components/pro-table';
|
||||
import { filterTrafficLogDetails } from '@/services/admin/log';
|
||||
import { formatBytes, formatDate } from '@workspace/ui/utils';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { formatBytes } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
getBatchSendEmailTaskStatus,
|
||||
stopBatchSendEmailTask,
|
||||
} from '@/services/admin/marketing';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import {
|
||||
@ -24,7 +25,6 @@ import {
|
||||
SheetTrigger,
|
||||
} from '@workspace/ui/components/sheet';
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
@ -9,13 +9,13 @@ import { Display } from '@/components/display';
|
||||
import { ProTable, ProTableActions } from '@/components/pro-table';
|
||||
import { getOrderList, updateOrderStatus } from '@/services/admin/order';
|
||||
import { getSubscribeList } from '@/services/admin/subscribe';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@workspace/ui/components/hover-card';
|
||||
import { Separator } from '@workspace/ui/components/separator';
|
||||
import { Combobox } from '@workspace/ui/custom-components/combobox';
|
||||
import { cn } from '@workspace/ui/lib/utils';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { UserDetail } from '../user/user-detail';
|
||||
|
||||
export default function Page() {
|
||||
|
||||
@ -4,6 +4,7 @@ import { UserDetail } from '@/app/dashboard/user/user-detail';
|
||||
import { IpLink } from '@/components/ip-link';
|
||||
import { ProTable } from '@/components/pro-table';
|
||||
import { getUserSubscribeById } from '@/services/admin/user';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import {
|
||||
@ -13,7 +14,7 @@ import {
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from '@workspace/ui/components/sheet';
|
||||
import { formatBytes, formatDate } from '@workspace/ui/utils';
|
||||
import { formatBytes } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useState } from 'react';
|
||||
|
||||
@ -113,7 +114,7 @@ export default function OnlineUsersCell({ status }: { status?: API.ServerStatus
|
||||
{ips.map((item, i) => (
|
||||
<div className='whitespace-nowrap text-sm' key={i}>
|
||||
<Badge>{item.protocol}</Badge>
|
||||
<IpLink ip={item.ip} className='font-medium' />
|
||||
<IpLink ip={item.ip} className='ml-1 font-medium' />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
getTicketList,
|
||||
updateTicketStatus,
|
||||
} from '@/services/admin/ticket';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import {
|
||||
@ -23,7 +24,6 @@ import { ScrollArea } from '@workspace/ui/components/scroll-area';
|
||||
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { cn } from '@workspace/ui/lib/utils';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { getSystemLog, getVersion, restartSystem } from '@/services/admin/tool';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import {
|
||||
Accordion,
|
||||
@ -29,7 +30,6 @@ import {
|
||||
} from '@workspace/ui/components/card';
|
||||
import { ScrollArea } from '@workspace/ui/components/scroll-area';
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useState } from 'react';
|
||||
import packageJson from '../../../../../package.json';
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
getUserList,
|
||||
updateUserBasicInfo,
|
||||
} from '@/services/admin/user';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
@ -30,7 +31,6 @@ import {
|
||||
import { Switch } from '@workspace/ui/components/switch';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@workspace/ui/components/tabs';
|
||||
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import Link from 'next/link';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
@ -2,10 +2,11 @@
|
||||
|
||||
import { Display } from '@/components/display';
|
||||
import { getUserDetail, getUserSubscribeById } from '@/services/admin/user';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@workspace/ui/components/hover-card';
|
||||
import { formatBytes, formatDate } from '@workspace/ui/utils';
|
||||
import { formatBytes } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import Link from 'next/link';
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
getUserSubscribe,
|
||||
updateUserSubscribe,
|
||||
} from '@/services/admin/user';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
@ -16,7 +17,6 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from '@workspace/ui/components/dropdown-menu';
|
||||
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import Link from 'next/link';
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
import { IpLink } from '@/components/ip-link';
|
||||
import { ProTable } from '@/components/pro-table';
|
||||
import { getUserSubscribeDevices, kickOfflineByUserDevice } from '@/services/admin/user';
|
||||
import { formatDate } from '@/utils/common';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import {
|
||||
@ -14,7 +15,6 @@ import {
|
||||
} from '@workspace/ui/components/sheet';
|
||||
import { Switch } from '@workspace/ui/components/switch';
|
||||
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { ReactNode, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
@ -27,14 +27,14 @@ import { UserStatisticsCard } from './user-statistics-card';
|
||||
export default function Statistics() {
|
||||
const t = useTranslations('index');
|
||||
|
||||
const { data: TicketTotal } = useQuery({
|
||||
const { data: TicketTotal, isLoading: ticketLoading } = useQuery({
|
||||
queryKey: ['queryTicketWaitReply'],
|
||||
queryFn: async () => {
|
||||
const { data } = await queryTicketWaitReply();
|
||||
return data.data?.count;
|
||||
},
|
||||
});
|
||||
const { data: ServerTotal } = useQuery({
|
||||
const { data: ServerTotal, isLoading: serverLoading } = useQuery({
|
||||
queryKey: ['queryServerTotalData'],
|
||||
queryFn: async () => {
|
||||
const { data } = await queryServerTotalData();
|
||||
@ -42,6 +42,8 @@ export default function Statistics() {
|
||||
},
|
||||
});
|
||||
|
||||
const isLoading = ticketLoading || serverLoading;
|
||||
|
||||
const [dataType, setDataType] = useState<string | 'nodes' | 'users'>('nodes');
|
||||
const [timeFrame, setTimeFrame] = useState<string | 'today' | 'yesterday'>('today');
|
||||
|
||||
@ -76,61 +78,75 @@ export default function Statistics() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='grid grid-cols-2 gap-2 md:grid-cols-4'>
|
||||
<div className='grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-5'>
|
||||
{[
|
||||
{
|
||||
title: t('onlineUsersCount'),
|
||||
value: ServerTotal?.online_users || 0,
|
||||
subtitle: t('currentlyOnline'),
|
||||
icon: 'uil:users-alt',
|
||||
href: '/dashboard/servers',
|
||||
color: 'text-blue-600 dark:text-blue-400',
|
||||
iconBg: 'bg-blue-100 dark:bg-blue-900/30',
|
||||
},
|
||||
{
|
||||
title: t('onlineNodeCount'),
|
||||
value: ServerTotal?.online_servers || 0,
|
||||
title: t('totalServers'),
|
||||
value: (ServerTotal?.online_servers || 0) + (ServerTotal?.offline_servers || 0),
|
||||
subtitle: `${t('online')} ${ServerTotal?.online_servers || 0} ${t('offline')} ${ServerTotal?.offline_servers || 0}`,
|
||||
icon: 'uil:server-network',
|
||||
href: '/dashboard/servers',
|
||||
color: 'text-green-600 dark:text-green-400',
|
||||
iconBg: 'bg-green-100 dark:bg-green-900/30',
|
||||
},
|
||||
{
|
||||
title: t('offlineNodeCount'),
|
||||
value: ServerTotal?.offline_servers || 0,
|
||||
icon: 'uil:server-network-alt',
|
||||
href: '/dashboard/servers',
|
||||
title: t('todayTraffic'),
|
||||
value: formatBytes(
|
||||
(ServerTotal?.today_upload || 0) + (ServerTotal?.today_download || 0),
|
||||
),
|
||||
subtitle: `↑${formatBytes(ServerTotal?.today_upload || 0)} ↓${formatBytes(ServerTotal?.today_download || 0)}`,
|
||||
icon: 'uil:exchange-alt',
|
||||
color: 'text-purple-600 dark:text-purple-400',
|
||||
iconBg: 'bg-purple-100 dark:bg-purple-900/30',
|
||||
},
|
||||
{
|
||||
title: t('monthTraffic'),
|
||||
value: formatBytes(
|
||||
(ServerTotal?.monthly_upload || 0) + (ServerTotal?.monthly_download || 0),
|
||||
),
|
||||
subtitle: `↑${formatBytes(ServerTotal?.monthly_upload || 0)} ↓${formatBytes(ServerTotal?.monthly_download || 0)}`,
|
||||
icon: 'uil:cloud-data-connection',
|
||||
color: 'text-orange-600 dark:text-orange-400',
|
||||
iconBg: 'bg-orange-100 dark:bg-orange-900/30',
|
||||
},
|
||||
{
|
||||
title: t('pendingTickets'),
|
||||
value: TicketTotal || 0,
|
||||
subtitle: t('pending'),
|
||||
icon: 'uil:clipboard-notes',
|
||||
href: '/dashboard/ticket',
|
||||
},
|
||||
{
|
||||
title: t('todayUploadTraffic'),
|
||||
value: formatBytes(ServerTotal?.today_upload || 0),
|
||||
icon: 'uil:arrow-up',
|
||||
},
|
||||
{
|
||||
title: t('todayDownloadTraffic'),
|
||||
value: formatBytes(ServerTotal?.today_download || 0),
|
||||
icon: 'uil:arrow-down',
|
||||
},
|
||||
{
|
||||
title: t('monthUploadTraffic'),
|
||||
value: formatBytes(ServerTotal?.monthly_upload || 0),
|
||||
icon: 'uil:cloud-upload',
|
||||
},
|
||||
{
|
||||
title: t('monthDownloadTraffic'),
|
||||
value: formatBytes(ServerTotal?.monthly_download || 0),
|
||||
icon: 'uil:cloud-download',
|
||||
color: 'text-red-600 dark:text-red-400',
|
||||
iconBg: 'bg-red-100 dark:bg-red-900/30',
|
||||
},
|
||||
].map((item, index) => (
|
||||
<Link href={item.href || '#'} key={index}>
|
||||
<Card className='cursor-pointer'>
|
||||
<CardHeader className='p-4'>
|
||||
<CardTitle>{item.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className='flex justify-between p-4 text-xl'>
|
||||
<Icon icon={item.icon} className='text-muted-foreground' />
|
||||
<div className='text-xl font-bold tabular-nums leading-none'>{item.value}</div>
|
||||
<Link
|
||||
href={item.href || '#'}
|
||||
key={index}
|
||||
className={!item.href ? 'pointer-events-none' : ''}
|
||||
>
|
||||
<Card className={`group ${item.href ? 'cursor-pointer' : ''}`}>
|
||||
<CardContent className='p-6'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='flex-1'>
|
||||
<p className='text-muted-foreground mb-2 text-sm font-medium'>{item.title}</p>
|
||||
<div className={`text-2xl font-bold ${item.color} mb-1`}>{item.value}</div>
|
||||
<div className={`text-muted-foreground h-4 text-xs`}>{item.subtitle}</div>
|
||||
</div>
|
||||
<div
|
||||
className={`rounded-full p-3 ${item.iconBg} transition-transform duration-300 group-hover:scale-110`}
|
||||
>
|
||||
<Icon icon={item.icon} className={`h-6 w-6 ${item.color}`} />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
|
||||
@ -192,8 +192,8 @@ export function UserStatisticsCard() {
|
||||
tickMargin={10}
|
||||
axisLine={false}
|
||||
tickFormatter={(value) => {
|
||||
// value format: "YYYY-MM-DD"
|
||||
return new Date(value).toLocaleDateString(locale, {
|
||||
const [year, month, day] = value.split('-');
|
||||
return new Date(year, month - 1, day).toLocaleDateString(locale, {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
});
|
||||
|
||||
@ -15,6 +15,7 @@ import { usePathname } from 'next/navigation';
|
||||
import { Fragment, useMemo } from 'react';
|
||||
import LanguageSwitch from './language-switch';
|
||||
import ThemeSwitch from './theme-switch';
|
||||
import TimezoneSwitch from './timezone-switch';
|
||||
import { UserNav } from './user-nav';
|
||||
|
||||
export function Header() {
|
||||
@ -48,6 +49,7 @@ export function Header() {
|
||||
</div>
|
||||
<div className='flex items-center gap-2 px-3'>
|
||||
<LanguageSwitch />
|
||||
<TimezoneSwitch />
|
||||
<ThemeSwitch />
|
||||
<UserNav />
|
||||
</div>
|
||||
|
||||
129
apps/admin/components/timezone-switch.tsx
Normal file
129
apps/admin/components/timezone-switch.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
'use client';
|
||||
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import { Command, CommandInput, CommandItem, CommandList } from '@workspace/ui/components/command';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@workspace/ui/components/popover';
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { cn } from '@workspace/ui/lib/utils';
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
interface TimezoneOption {
|
||||
value: string;
|
||||
label: string;
|
||||
offset: string;
|
||||
}
|
||||
|
||||
function getAllTimezones(): TimezoneOption[] {
|
||||
try {
|
||||
const timeZones = Intl.supportedValuesOf('timeZone');
|
||||
|
||||
return [
|
||||
{
|
||||
value: 'UTC',
|
||||
label: 'UTC',
|
||||
offset: '+00:00',
|
||||
},
|
||||
].concat(
|
||||
timeZones
|
||||
.map((tz) => {
|
||||
const parts = tz.split('/');
|
||||
let label = tz;
|
||||
|
||||
if (parts.length >= 2) {
|
||||
const region = parts[0];
|
||||
const city = parts[1]?.replace(/_/g, ' ') || '';
|
||||
label = `${city} (${region})`;
|
||||
}
|
||||
|
||||
return {
|
||||
value: tz,
|
||||
label: label,
|
||||
offset: getTimezoneOffset(tz),
|
||||
};
|
||||
})
|
||||
.sort((a, b) => a.label.localeCompare(b.label)),
|
||||
);
|
||||
} catch {
|
||||
return [
|
||||
{
|
||||
value: 'UTC',
|
||||
label: 'UTC',
|
||||
offset: '+00:00',
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function getTimezoneOffset(timezone: string): string {
|
||||
try {
|
||||
const now = new Date();
|
||||
const utc = new Date(now.getTime() + now.getTimezoneOffset() * 60000);
|
||||
const targetTime = new Date(utc.toLocaleString('en-US', { timeZone: timezone }));
|
||||
const offset = (targetTime.getTime() - utc.getTime()) / (1000 * 60 * 60);
|
||||
const sign = offset >= 0 ? '+' : '-';
|
||||
const hours = Math.floor(Math.abs(offset));
|
||||
const minutes = Math.floor((Math.abs(offset) - hours) * 60);
|
||||
return `${sign}${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
|
||||
} catch {
|
||||
return '+00:00';
|
||||
}
|
||||
}
|
||||
|
||||
export default function TimezoneSwitch() {
|
||||
const [timezone, setTimezone] = useState<string>('UTC');
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const timezoneOptions = useMemo(() => getAllTimezones(), []);
|
||||
|
||||
const handleTimezoneChange = (newTimezone: string) => {
|
||||
setTimezone(newTimezone);
|
||||
localStorage.setItem('timezone', newTimezone);
|
||||
setOpen(false);
|
||||
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('timezoneChanged', {
|
||||
detail: { timezone: newTimezone },
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant='ghost' size='icon' className='p-0'>
|
||||
<Icon icon='flat-color-icons:overtime' className='!size-6' />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className='w-80 p-0' align='end'>
|
||||
<Command>
|
||||
<CommandInput placeholder='Search...' />
|
||||
<CommandList>
|
||||
{timezoneOptions.map((option) => (
|
||||
<CommandItem
|
||||
key={option.value}
|
||||
value={`${option.label} ${option.value}`}
|
||||
onSelect={() => handleTimezoneChange(option.value)}
|
||||
>
|
||||
<div className='flex w-full items-center gap-3'>
|
||||
<div className='flex flex-1 flex-col'>
|
||||
<span className='font-medium'>{option.label}</span>
|
||||
<span className='text-muted-foreground text-xs'>
|
||||
{option.value} • {option.offset}
|
||||
</span>
|
||||
</div>
|
||||
<Icon
|
||||
icon='uil:check'
|
||||
className={cn(
|
||||
'h-4 w-4',
|
||||
timezone === option.value ? 'opacity-100' : 'opacity-0',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "Aktuálně online",
|
||||
"email": "e-mail",
|
||||
"month": "Tento měsíc",
|
||||
"monthDownloadTraffic": "Měsíční objem stažených dat",
|
||||
"monthTraffic": "Trafik tohoto měsíce",
|
||||
"monthUploadTraffic": "Tento měsíc nahraný provoz",
|
||||
"newPurchase": "Nový nákup",
|
||||
"nodeTraffic": "Provoz uzlu",
|
||||
"nodes": "uzly",
|
||||
"offlineNodeCount": "Počet offline uzlů",
|
||||
"onlineNodeCount": "Počet online uzlů",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"onlineServers": "Online servery",
|
||||
"onlineUsersCount": "Uživatelé online",
|
||||
"pending": "Čekající",
|
||||
"pendingTickets": "Čekající lístky",
|
||||
"register": "Registrovat se",
|
||||
"repurchase": "opětovný nákup",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Vyberte typ",
|
||||
"today": "Dnes",
|
||||
"todayDownloadTraffic": "Dnešní stahovací provoz",
|
||||
"todayTraffic": "Dnešní trafik",
|
||||
"todayUploadTraffic": "Dnešní nahraný provoz",
|
||||
"total": "Celkem",
|
||||
"totalIncome": "Celkový příjem",
|
||||
"totalServers": "Celkový počet serverů",
|
||||
"traffic": "provoz",
|
||||
"trafficRank": "Pořadí návštěvnosti",
|
||||
"type": "Typ",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "Derzeit Online",
|
||||
"email": "E-Mail",
|
||||
"month": "Diesen Monat",
|
||||
"monthDownloadTraffic": "Monatlicher Download-Traffic",
|
||||
"monthTraffic": "Traffic in diesem Monat",
|
||||
"monthUploadTraffic": "Diesen Monat hochgeladener Datenverkehr",
|
||||
"newPurchase": "Neukauf",
|
||||
"nodeTraffic": "Knotenverkehr",
|
||||
"nodes": "Knoten",
|
||||
"offlineNodeCount": "Anzahl der Offline-Knoten",
|
||||
"onlineNodeCount": "Anzahl der Online-Knoten",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"onlineServers": "Online-Server",
|
||||
"onlineUsersCount": "Online-Benutzer",
|
||||
"pending": "Ausstehend",
|
||||
"pendingTickets": "Ausstehende Tickets",
|
||||
"register": "Registrieren",
|
||||
"repurchase": "Wiederkauf",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Typ auswählen",
|
||||
"today": "Heute",
|
||||
"todayDownloadTraffic": "Heutiger Download-Traffic",
|
||||
"todayTraffic": "Traffic heute",
|
||||
"todayUploadTraffic": "Heutiger Upload-Verkehr",
|
||||
"total": "Gesamt",
|
||||
"totalIncome": "Gesamteinkommen",
|
||||
"totalServers": "Gesamtanzahl der Server",
|
||||
"traffic": "Verkehr",
|
||||
"trafficRank": "Verkehrsrangliste",
|
||||
"type": "Typ",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "Currently Online",
|
||||
"email": "Email",
|
||||
"month": "Month",
|
||||
"monthDownloadTraffic": "This Month's Download Traffic",
|
||||
"monthTraffic": "This Month's Traffic",
|
||||
"monthUploadTraffic": "This Month's Upload Traffic",
|
||||
"newPurchase": "New Purchase",
|
||||
"nodeTraffic": "Node Traffic",
|
||||
"nodes": "Nodes",
|
||||
"offlineNodeCount": "Offline Nodes",
|
||||
"onlineNodeCount": "Online Nodes",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"onlineServers": "Online Servers",
|
||||
"onlineUsersCount": "Online Users",
|
||||
"pending": "Pending",
|
||||
"pendingTickets": "Pending Tickets",
|
||||
"register": "Register",
|
||||
"repurchase": "Repurchase",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Select Type",
|
||||
"today": "Today",
|
||||
"todayDownloadTraffic": "Today's Download Traffic",
|
||||
"todayTraffic": "Today's Traffic",
|
||||
"todayUploadTraffic": "Today's Upload Traffic",
|
||||
"total": "Total",
|
||||
"totalIncome": "Total Income",
|
||||
"totalServers": "Total Servers",
|
||||
"traffic": "Traffic",
|
||||
"trafficRank": "Traffic Ranking",
|
||||
"type": "Type",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "Actualmente en línea",
|
||||
"email": "correo electrónico",
|
||||
"month": "Este mes",
|
||||
"monthDownloadTraffic": "Tráfico de descarga del mes",
|
||||
"monthTraffic": "Tráfico de este mes",
|
||||
"monthUploadTraffic": "Tráfico de subida del mes",
|
||||
"newPurchase": "Nueva compra",
|
||||
"nodeTraffic": "Tráfico de nodo",
|
||||
"nodes": "nodos",
|
||||
"offlineNodeCount": "Número de nodos fuera de línea",
|
||||
"onlineNodeCount": "Número de nodos en línea",
|
||||
"offline": "Desconectado",
|
||||
"online": "En línea",
|
||||
"onlineServers": "Servidores en línea",
|
||||
"onlineUsersCount": "Usuarios en línea",
|
||||
"pending": "Pendiente",
|
||||
"pendingTickets": "Tickets pendientes",
|
||||
"register": "registrar",
|
||||
"repurchase": "recompra",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Seleccionar tipo",
|
||||
"today": "hoy",
|
||||
"todayDownloadTraffic": "Tráfico de descarga de hoy",
|
||||
"todayTraffic": "Tráfico de hoy",
|
||||
"todayUploadTraffic": "Tráfico de subida de hoy",
|
||||
"total": "Total",
|
||||
"totalIncome": "Ingresos totales",
|
||||
"totalServers": "Total de servidores",
|
||||
"traffic": "tráfico",
|
||||
"trafficRank": "Clasificación de tráfico",
|
||||
"type": "tipo",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "Actualmente en línea",
|
||||
"email": "correo electrónico",
|
||||
"month": "Este mes",
|
||||
"monthDownloadTraffic": "Tráfico de descarga del mes",
|
||||
"monthTraffic": "Tráfico de este mes",
|
||||
"monthUploadTraffic": "Tráfico de carga de este mes",
|
||||
"newPurchase": "Nueva compra",
|
||||
"nodeTraffic": "Tráfico de nodo",
|
||||
"nodes": "nodos",
|
||||
"offlineNodeCount": "Número de nodos fuera de línea",
|
||||
"onlineNodeCount": "Número de nodos en línea",
|
||||
"offline": "Desconectado",
|
||||
"online": "En línea",
|
||||
"onlineServers": "Servidores en línea",
|
||||
"onlineUsersCount": "Usuarios en línea",
|
||||
"pending": "Pendiente",
|
||||
"pendingTickets": "Tickets pendientes",
|
||||
"register": "Registrarse",
|
||||
"repurchase": "recompra",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Seleccionar tipo",
|
||||
"today": "hoy",
|
||||
"todayDownloadTraffic": "Tráfico de descarga de hoy",
|
||||
"todayTraffic": "Tráfico de hoy",
|
||||
"todayUploadTraffic": "Tráfico de carga de hoy",
|
||||
"total": "Total",
|
||||
"totalIncome": "Ingreso total",
|
||||
"totalServers": "Total de servidores",
|
||||
"traffic": "tráfico",
|
||||
"trafficRank": "Clasificación de tráfico",
|
||||
"type": "tipo",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "در حال حاضر آنلاین",
|
||||
"email": "ایمیل",
|
||||
"month": "ماه",
|
||||
"monthDownloadTraffic": "ترافیک دانلود این ماه",
|
||||
"monthTraffic": "ترافیک این ماه",
|
||||
"monthUploadTraffic": "ترافیک آپلود این ماه",
|
||||
"newPurchase": "خرید جدید",
|
||||
"nodeTraffic": "ترافیک نود",
|
||||
"nodes": "گرهها",
|
||||
"offlineNodeCount": "گرههای آفلاین",
|
||||
"onlineNodeCount": "گرههای آنلاین",
|
||||
"offline": "آفلاین",
|
||||
"online": "آنلاین",
|
||||
"onlineServers": "سرورهای آنلاین",
|
||||
"onlineUsersCount": "کاربران آنلاین",
|
||||
"pending": "در حال انتظار",
|
||||
"pendingTickets": "بلیطهای در انتظار",
|
||||
"register": "ثبت نام",
|
||||
"repurchase": "بازخرید",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "نوع را انتخاب کنید",
|
||||
"today": "امروز",
|
||||
"todayDownloadTraffic": "ترافیک دانلود امروز",
|
||||
"todayTraffic": "ترافیک امروز",
|
||||
"todayUploadTraffic": "ترافیک بارگذاری امروز",
|
||||
"total": "جمع کل",
|
||||
"totalIncome": "کل درآمد",
|
||||
"totalServers": "کل سرورها",
|
||||
"traffic": "ترافیک",
|
||||
"trafficRank": "رتبه ترافیک",
|
||||
"type": "نوع",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "Tällä hetkellä verkossa",
|
||||
"email": "sähköposti",
|
||||
"month": "Tämä kuukausi",
|
||||
"monthDownloadTraffic": "Tämän kuukauden latausliikenne",
|
||||
"monthTraffic": "Tämän kuun liikenne",
|
||||
"monthUploadTraffic": "Tämän kuukauden lähetysliikenne",
|
||||
"newPurchase": "Uusi osto",
|
||||
"nodeTraffic": "Solmun liikenne",
|
||||
"nodes": "solmut",
|
||||
"offlineNodeCount": "Offline-solmujen määrä",
|
||||
"onlineNodeCount": "Verkossa olevien solmujen määrä",
|
||||
"offline": "Poissa käytöstä",
|
||||
"online": "Verkossa",
|
||||
"onlineServers": "Verkossa olevat palvelimet",
|
||||
"onlineUsersCount": "Verkkokäyttäjät",
|
||||
"pending": "Odottaa",
|
||||
"pendingTickets": "Odottavat liput",
|
||||
"register": "Rekisteröidy",
|
||||
"repurchase": "uudelleenosto",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Valitse tyyppi",
|
||||
"today": "tänään",
|
||||
"todayDownloadTraffic": "Tämän päivän latausliikenne",
|
||||
"todayTraffic": "Tänään liikenne",
|
||||
"todayUploadTraffic": "Tämän päivän lähetysliikenne",
|
||||
"total": "Yhteensä",
|
||||
"totalIncome": "Kokonaistulot",
|
||||
"totalServers": "Yhteensä palvelimia",
|
||||
"traffic": "liikenne",
|
||||
"trafficRank": "Liikenteen sijoitus",
|
||||
"type": "tyyppi",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "Actuellement en ligne",
|
||||
"email": "e-mail",
|
||||
"month": "Ce mois-ci",
|
||||
"monthDownloadTraffic": "Trafic de téléchargement ce mois-ci",
|
||||
"monthTraffic": "Trafic de ce mois",
|
||||
"monthUploadTraffic": "Trafic de téléchargement ce mois-ci",
|
||||
"newPurchase": "Nouvel achat",
|
||||
"nodeTraffic": "Trafic du nœud",
|
||||
"nodes": "nœuds",
|
||||
"offlineNodeCount": "Nombre de nœuds hors ligne",
|
||||
"onlineNodeCount": "Nombre de nœuds en ligne",
|
||||
"offline": "Hors ligne",
|
||||
"online": "En ligne",
|
||||
"onlineServers": "Serveurs en ligne",
|
||||
"onlineUsersCount": "Utilisateurs en ligne",
|
||||
"pending": "En attente",
|
||||
"pendingTickets": "Tickets en attente",
|
||||
"register": "S'inscrire",
|
||||
"repurchase": "Rachat",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Sélectionner le type",
|
||||
"today": "aujourd'hui",
|
||||
"todayDownloadTraffic": "Trafic de téléchargement d'aujourd'hui",
|
||||
"todayTraffic": "Trafic d'aujourd'hui",
|
||||
"todayUploadTraffic": "Trafic de téléchargement d'aujourd'hui",
|
||||
"total": "Total",
|
||||
"totalIncome": "Revenu total",
|
||||
"totalServers": "Total des serveurs",
|
||||
"traffic": "trafic",
|
||||
"trafficRank": "Classement du trafic",
|
||||
"type": "Type",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "वर्तमान में ऑनलाइन",
|
||||
"email": "ईमेल",
|
||||
"month": "इस माह",
|
||||
"monthDownloadTraffic": "इस महीने की डाउनलोड ट्रैफिक",
|
||||
"monthTraffic": "इस महीने का ट्रैफ़िक",
|
||||
"monthUploadTraffic": "इस महीने का अपलोड ट्रैफिक",
|
||||
"newPurchase": "नई खरीद",
|
||||
"nodeTraffic": "नोड ट्रैफिक",
|
||||
"nodes": "नोड्स",
|
||||
"offlineNodeCount": "ऑफ़लाइन नोड की संख्या",
|
||||
"onlineNodeCount": "ऑनलाइन नोड की संख्या",
|
||||
"offline": "ऑफ़लाइन",
|
||||
"online": "ऑनलाइन",
|
||||
"onlineServers": "ऑनलाइन सर्वर",
|
||||
"onlineUsersCount": "ऑनलाइन उपयोगकर्ता",
|
||||
"pending": "लंबित",
|
||||
"pendingTickets": "लंबित टिकट",
|
||||
"register": "पंजीकरण",
|
||||
"repurchase": "पुनः खरीद",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "प्रकार चुनें",
|
||||
"today": "आज",
|
||||
"todayDownloadTraffic": "आज का डाउनलोड ट्रैफिक",
|
||||
"todayTraffic": "आज का ट्रैफ़िक",
|
||||
"todayUploadTraffic": "आज का अपलोड ट्रैफिक",
|
||||
"total": "कुल",
|
||||
"totalIncome": "कुल आय",
|
||||
"totalServers": "कुल सर्वर",
|
||||
"traffic": "ट्रैफिक",
|
||||
"trafficRank": "ट्रैफिक रैंक",
|
||||
"type": "प्रकार",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "Jelenleg Online",
|
||||
"email": "e-mail",
|
||||
"month": "Ez a hónap",
|
||||
"monthDownloadTraffic": "Havi letöltési forgalom",
|
||||
"monthTraffic": "A hónap forgalma",
|
||||
"monthUploadTraffic": "Havi feltöltési forgalom",
|
||||
"newPurchase": "Új vásárlás",
|
||||
"nodeTraffic": "Csomópont forgalom",
|
||||
"nodes": "csomópontok",
|
||||
"offlineNodeCount": "Offline csomópontok száma",
|
||||
"onlineNodeCount": "Online csomópontok száma",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"onlineServers": "Online szerverek",
|
||||
"onlineUsersCount": "Online Felhasználók",
|
||||
"pending": "Függőben",
|
||||
"pendingTickets": "Függőben lévő jegyek",
|
||||
"register": "Regisztráció",
|
||||
"repurchase": "újravásárlás",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Válasszon típust",
|
||||
"today": "ma",
|
||||
"todayDownloadTraffic": "Mai letöltési forgalom",
|
||||
"todayTraffic": "A mai forgalom",
|
||||
"todayUploadTraffic": "Mai feltöltési forgalom",
|
||||
"total": "Összesen",
|
||||
"totalIncome": "Összes bevétel",
|
||||
"totalServers": "Összes szerver",
|
||||
"traffic": "forgalom",
|
||||
"trafficRank": "Forgalmi rangsor",
|
||||
"type": "típus",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "現在オンライン",
|
||||
"email": "メールアドレス",
|
||||
"month": "今月",
|
||||
"monthDownloadTraffic": "今月のダウンロードトラフィック",
|
||||
"monthTraffic": "今月のトラフィック",
|
||||
"monthUploadTraffic": "今月のアップロードトラフィック",
|
||||
"newPurchase": "新規購入",
|
||||
"nodeTraffic": "ノードトラフィック",
|
||||
"nodes": "ノード",
|
||||
"offlineNodeCount": "オフラインノード数",
|
||||
"onlineNodeCount": "オンラインノード数",
|
||||
"offline": "オフライン",
|
||||
"online": "オンライン",
|
||||
"onlineServers": "オンラインサーバー",
|
||||
"onlineUsersCount": "オンラインユーザー",
|
||||
"pending": "保留中",
|
||||
"pendingTickets": "保留中のチケット",
|
||||
"register": "登録",
|
||||
"repurchase": "再購入",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "タイプを選択",
|
||||
"today": "今日",
|
||||
"todayDownloadTraffic": "本日のダウンロードトラフィック",
|
||||
"todayTraffic": "今日のトラフィック",
|
||||
"todayUploadTraffic": "本日のアップロードトラフィック",
|
||||
"total": "合計",
|
||||
"totalIncome": "総収入",
|
||||
"totalServers": "総サーバー数",
|
||||
"traffic": "トラフィック",
|
||||
"trafficRank": "トラフィックランキング",
|
||||
"type": "タイプ",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "현재 온라인",
|
||||
"email": "이메일",
|
||||
"month": "이번 달",
|
||||
"monthDownloadTraffic": "이번 달 다운로드 트래픽",
|
||||
"monthTraffic": "이번 달 트래픽",
|
||||
"monthUploadTraffic": "이번 달 업로드 트래픽",
|
||||
"newPurchase": "신규 구매",
|
||||
"nodeTraffic": "노드 트래픽",
|
||||
"nodes": "노드",
|
||||
"offlineNodeCount": "오프라인 노드 수",
|
||||
"onlineNodeCount": "온라인 노드 수",
|
||||
"offline": "오프라인",
|
||||
"online": "온라인",
|
||||
"onlineServers": "온라인 서버",
|
||||
"onlineUsersCount": "온라인 사용자",
|
||||
"pending": "대기 중",
|
||||
"pendingTickets": "처리 대기 중인 티켓",
|
||||
"register": "등록",
|
||||
"repurchase": "재구매",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "유형 선택",
|
||||
"today": "오늘",
|
||||
"todayDownloadTraffic": "오늘 다운로드 트래픽",
|
||||
"todayTraffic": "오늘의 트래픽",
|
||||
"todayUploadTraffic": "오늘 업로드 트래픽",
|
||||
"total": "총계",
|
||||
"totalIncome": "총수입",
|
||||
"totalServers": "총 서버",
|
||||
"traffic": "트래픽",
|
||||
"trafficRank": "트래픽 순위",
|
||||
"type": "유형",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "For øyeblikket på nett",
|
||||
"email": "e-post",
|
||||
"month": "Denne måneden",
|
||||
"monthDownloadTraffic": "Nedlastingstrafikk denne måneden",
|
||||
"monthTraffic": "Månedens trafikk",
|
||||
"monthUploadTraffic": "Månedens opplastningstrafikk",
|
||||
"newPurchase": "Nytt kjøp",
|
||||
"nodeTraffic": "Nodetrafikk",
|
||||
"nodes": "noder",
|
||||
"offlineNodeCount": "Antall frakoblede noder",
|
||||
"onlineNodeCount": "Antall noder på nett",
|
||||
"offline": "Av",
|
||||
"online": "På nett",
|
||||
"onlineServers": "Nettservere",
|
||||
"onlineUsersCount": "Nettbrukere",
|
||||
"pending": "Venter",
|
||||
"pendingTickets": "Ventende billetter",
|
||||
"register": "Registrer",
|
||||
"repurchase": "gjenkjøp",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Velg type",
|
||||
"today": "i dag",
|
||||
"todayDownloadTraffic": "Dagens nedlastningstrafikk",
|
||||
"todayTraffic": "Dagens trafikk",
|
||||
"todayUploadTraffic": "Dagens opplastningstrafikk",
|
||||
"total": "Totalt",
|
||||
"totalIncome": "Totalinntekt",
|
||||
"totalServers": "Totale servere",
|
||||
"traffic": "trafikk",
|
||||
"trafficRank": "Trafikkrangering",
|
||||
"type": "Type",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "Obecnie online",
|
||||
"email": "e-mail",
|
||||
"month": "Ten miesiąc",
|
||||
"monthDownloadTraffic": "Miesięczny ruch pobierania",
|
||||
"monthTraffic": "Ruch w tym miesiącu",
|
||||
"monthUploadTraffic": "Miesięczny przesył danych",
|
||||
"newPurchase": "Nowy zakup",
|
||||
"nodeTraffic": "Ruch węzła",
|
||||
"nodes": "węzły",
|
||||
"offlineNodeCount": "Liczba węzłów offline",
|
||||
"onlineNodeCount": "Liczba węzłów online",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"onlineServers": "Serwery online",
|
||||
"onlineUsersCount": "Użytkownicy online",
|
||||
"pending": "Oczekujące",
|
||||
"pendingTickets": "Oczekujące zgłoszenia",
|
||||
"register": "Rejestracja",
|
||||
"repurchase": "ponowny zakup",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Wybierz typ",
|
||||
"today": "Dziś",
|
||||
"todayDownloadTraffic": "Dzisiejszy ruch pobierania",
|
||||
"todayTraffic": "Ruch dzisiaj",
|
||||
"todayUploadTraffic": "Dzisiejszy przesył danych",
|
||||
"total": "Razem",
|
||||
"totalIncome": "Całkowity dochód",
|
||||
"totalServers": "Łączna liczba serwerów",
|
||||
"traffic": "ruch",
|
||||
"trafficRank": "Ranking ruchu",
|
||||
"type": "typ",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "Atualmente Online",
|
||||
"email": "e-mail",
|
||||
"month": "Este mês",
|
||||
"monthDownloadTraffic": "Tráfego de download do mês",
|
||||
"monthTraffic": "Tráfego deste Mês",
|
||||
"monthUploadTraffic": "Tráfego de upload deste mês",
|
||||
"newPurchase": "Nova Compra",
|
||||
"nodeTraffic": "Tráfego do Nó",
|
||||
"nodes": "nós",
|
||||
"offlineNodeCount": "Contagem de nós offline",
|
||||
"onlineNodeCount": "Contagem de nós online",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"onlineServers": "Servidores Online",
|
||||
"onlineUsersCount": "Usuários Online",
|
||||
"pending": "Pendente",
|
||||
"pendingTickets": "Tickets pendentes",
|
||||
"register": "Registrar",
|
||||
"repurchase": "recompra",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Selecione o tipo",
|
||||
"today": "hoje",
|
||||
"todayDownloadTraffic": "Tráfego de download de hoje",
|
||||
"todayTraffic": "Tráfego de Hoje",
|
||||
"todayUploadTraffic": "Tráfego de upload de hoje",
|
||||
"total": "Total",
|
||||
"totalIncome": "Renda Total",
|
||||
"totalServers": "Total de Servidores",
|
||||
"traffic": "tráfego",
|
||||
"trafficRank": "Classificação de Tráfego",
|
||||
"type": "tipo",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "În prezent online",
|
||||
"email": "e-mail",
|
||||
"month": "Luna aceasta",
|
||||
"monthDownloadTraffic": "Traficul de descărcare din această lună",
|
||||
"monthTraffic": "Traficul din această lună",
|
||||
"monthUploadTraffic": "Traficul de încărcare din această lună",
|
||||
"newPurchase": "Achiziție nouă",
|
||||
"nodeTraffic": "Traficul nodului",
|
||||
"nodes": "noduri",
|
||||
"offlineNodeCount": "Număr de noduri offline",
|
||||
"onlineNodeCount": "Număr de noduri online",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"onlineServers": "Servere online",
|
||||
"onlineUsersCount": "Utilizatori online",
|
||||
"pending": "În așteptare",
|
||||
"pendingTickets": "Tichete în așteptare",
|
||||
"register": "Înregistrare",
|
||||
"repurchase": "recomandare",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Selectați tipul",
|
||||
"today": "astăzi",
|
||||
"todayDownloadTraffic": "Traficul de descărcare de astăzi",
|
||||
"todayTraffic": "Traficul de astăzi",
|
||||
"todayUploadTraffic": "Traficul de încărcare de astăzi",
|
||||
"total": "Total",
|
||||
"totalIncome": "Venit total",
|
||||
"totalServers": "Total servere",
|
||||
"traffic": "trafic",
|
||||
"trafficRank": "Clasament trafic",
|
||||
"type": "tip",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "В данный момент онлайн",
|
||||
"email": "Электронная почта",
|
||||
"month": "Этот месяц",
|
||||
"monthDownloadTraffic": "Трафик загрузки за месяц",
|
||||
"monthTraffic": "Трафик за этот месяц",
|
||||
"monthUploadTraffic": "Трафик загрузки за этот месяц",
|
||||
"newPurchase": "Новая покупка",
|
||||
"nodeTraffic": "Трафик узла",
|
||||
"nodes": "узлы",
|
||||
"offlineNodeCount": "Количество офлайн-узлов",
|
||||
"onlineNodeCount": "Количество онлайн-узлов",
|
||||
"offline": "Офлайн",
|
||||
"online": "Онлайн",
|
||||
"onlineServers": "Онлайн-серверы",
|
||||
"onlineUsersCount": "Пользователи онлайн",
|
||||
"pending": "В ожидании",
|
||||
"pendingTickets": "Ожидающие заявки",
|
||||
"register": "Регистрация",
|
||||
"repurchase": "повторная покупка",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Выберите тип",
|
||||
"today": "Сегодня",
|
||||
"todayDownloadTraffic": "Сегодняшний трафик загрузки",
|
||||
"todayTraffic": "Трафик сегодня",
|
||||
"todayUploadTraffic": "Сегодняшний объем загруженного трафика",
|
||||
"total": "Итого",
|
||||
"totalIncome": "Общий доход",
|
||||
"totalServers": "Всего серверов",
|
||||
"traffic": "трафик",
|
||||
"trafficRank": "Рейтинг трафика",
|
||||
"type": "Тип",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "ออนไลน์อยู่ในขณะนี้",
|
||||
"email": "อีเมล",
|
||||
"month": "เดือนนี้",
|
||||
"monthDownloadTraffic": "ปริมาณการดาวน์โหลดในเดือนนี้",
|
||||
"monthTraffic": "การเข้าชมในเดือนนี้",
|
||||
"monthUploadTraffic": "ปริมาณการอัปโหลดในเดือนนี้",
|
||||
"newPurchase": "ซื้อใหม่",
|
||||
"nodeTraffic": "ปริมาณการใช้งานของโหนด",
|
||||
"nodes": "โหนด",
|
||||
"offlineNodeCount": "จำนวนโหนดออฟไลน์",
|
||||
"onlineNodeCount": "จำนวนโหนดออนไลน์",
|
||||
"offline": "ออฟไลน์",
|
||||
"online": "ออนไลน์",
|
||||
"onlineServers": "เซิร์ฟเวอร์ออนไลน์",
|
||||
"onlineUsersCount": "ผู้ใช้งานออนไลน์",
|
||||
"pending": "รอดำเนินการ",
|
||||
"pendingTickets": "บัตรงานที่รอดำเนินการ",
|
||||
"register": "ลงทะเบียน",
|
||||
"repurchase": "ซื้อซ้ำ",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "เลือกประเภท",
|
||||
"today": "วันนี้",
|
||||
"todayDownloadTraffic": "ปริมาณการดาวน์โหลดวันนี้",
|
||||
"todayTraffic": "การเข้าชมวันนี้",
|
||||
"todayUploadTraffic": "ปริมาณการอัปโหลดวันนี้",
|
||||
"total": "รวมทั้งหมด",
|
||||
"totalIncome": "รายได้รวม",
|
||||
"totalServers": "จำนวนเซิร์ฟเวอร์ทั้งหมด",
|
||||
"traffic": "การจราจร",
|
||||
"trafficRank": "อันดับการเข้าชม",
|
||||
"type": "ประเภท",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "Şu Anda Çevrimiçi",
|
||||
"email": "e-posta",
|
||||
"month": "Bu Ay",
|
||||
"monthDownloadTraffic": "Bu ayki indirme trafiği",
|
||||
"monthTraffic": "Bu Ayki Trafik",
|
||||
"monthUploadTraffic": "Bu ayki yükleme trafiği",
|
||||
"newPurchase": "Yeni Satın Alma",
|
||||
"nodeTraffic": "Düğüm Trafiği",
|
||||
"nodes": "düğümler",
|
||||
"offlineNodeCount": "Çevrimdışı Düğüm Sayısı",
|
||||
"onlineNodeCount": "Çevrimiçi Düğüm Sayısı",
|
||||
"offline": "Çevrimdışı",
|
||||
"online": "Çevrimiçi",
|
||||
"onlineServers": "Çevrimiçi Sunucular",
|
||||
"onlineUsersCount": "Çevrimiçi Kullanıcılar",
|
||||
"pending": "Beklemede",
|
||||
"pendingTickets": "Bekleyen Biletler",
|
||||
"register": "Kayıt Ol",
|
||||
"repurchase": "yeniden satın alma",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Tür seçin",
|
||||
"today": "bugün",
|
||||
"todayDownloadTraffic": "Bugünkü indirme trafiği",
|
||||
"todayTraffic": "Bugünkü Trafik",
|
||||
"todayUploadTraffic": "Bugün Yükleme Trafiği",
|
||||
"total": "Toplam",
|
||||
"totalIncome": "Toplam Gelir",
|
||||
"totalServers": "Toplam Sunucular",
|
||||
"traffic": "trafik",
|
||||
"trafficRank": "Trafik Sıralaması",
|
||||
"type": "Tür",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "В даний час в мережі",
|
||||
"email": "електронна пошта",
|
||||
"month": "Цього місяця",
|
||||
"monthDownloadTraffic": "Трафік завантажень за цей місяць",
|
||||
"monthTraffic": "Трафік цього місяця",
|
||||
"monthUploadTraffic": "Цього місяця завантажено трафіку",
|
||||
"newPurchase": "Нова покупка",
|
||||
"nodeTraffic": "Трафік вузла",
|
||||
"nodes": "вузли",
|
||||
"offlineNodeCount": "Кількість офлайн-вузлів",
|
||||
"onlineNodeCount": "Кількість онлайн-вузлів",
|
||||
"offline": "Вимкнено",
|
||||
"online": "В мережі",
|
||||
"onlineServers": "Сервери в мережі",
|
||||
"onlineUsersCount": "Користувачі онлайн",
|
||||
"pending": "В очікуванні",
|
||||
"pendingTickets": "Невирішені заявки",
|
||||
"register": "Реєстрація",
|
||||
"repurchase": "повторна покупка",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Виберіть тип",
|
||||
"today": "сьогодні",
|
||||
"todayDownloadTraffic": "Сьогоднішній трафік завантажень",
|
||||
"todayTraffic": "Трафік сьогодні",
|
||||
"todayUploadTraffic": "Сьогоднішній обсяг завантаження",
|
||||
"total": "Загалом",
|
||||
"totalIncome": "Загальний дохід",
|
||||
"totalServers": "Всього серверів",
|
||||
"traffic": "трафік",
|
||||
"trafficRank": "Рейтинг трафіку",
|
||||
"type": "Тип",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "Đang Trực Tuyến",
|
||||
"email": "Email",
|
||||
"month": "Tháng này",
|
||||
"monthDownloadTraffic": "Lưu lượng tải xuống tháng này",
|
||||
"monthTraffic": "Lưu Lượng Tháng Này",
|
||||
"monthUploadTraffic": "Lưu lượng tải lên trong tháng này",
|
||||
"newPurchase": "Mua mới",
|
||||
"nodeTraffic": "Lưu lượng nút",
|
||||
"nodes": "nút",
|
||||
"offlineNodeCount": "Số lượng nút ngoại tuyến",
|
||||
"onlineNodeCount": "Số lượng nút trực tuyến",
|
||||
"offline": "Ngoại Tuyến",
|
||||
"online": "Trực Tuyến",
|
||||
"onlineServers": "Máy Chủ Đang Trực Tuyến",
|
||||
"onlineUsersCount": "Người dùng trực tuyến",
|
||||
"pending": "Đang Chờ",
|
||||
"pendingTickets": "Phiếu hỗ trợ đang chờ xử lý",
|
||||
"register": "Đăng ký",
|
||||
"repurchase": "mua lại",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "Chọn loại",
|
||||
"today": "Hôm nay",
|
||||
"todayDownloadTraffic": "Lưu lượng tải xuống hôm nay",
|
||||
"todayTraffic": "Lưu Lượng Hôm Nay",
|
||||
"todayUploadTraffic": "Lưu lượng tải lên hôm nay",
|
||||
"total": "Tổng cộng",
|
||||
"totalIncome": "Tổng thu nhập",
|
||||
"totalServers": "Tổng Số Máy Chủ",
|
||||
"traffic": "lưu lượng",
|
||||
"trafficRank": "Xếp hạng lưu lượng",
|
||||
"type": "Loại",
|
||||
|
||||
@ -1,14 +1,23 @@
|
||||
{
|
||||
"currentlyOnline": "当前在线",
|
||||
"currentlyOnline": "当前在线",
|
||||
"download": "↓",
|
||||
"email": "邮箱",
|
||||
"month": "本月",
|
||||
"monthDownloadTraffic": "本月下载流量",
|
||||
"monthTraffic": "本月流量",
|
||||
"monthUploadTraffic": "本月上传流量",
|
||||
"newPurchase": "新购",
|
||||
"nodeTraffic": "节点流量",
|
||||
"nodes": "节点",
|
||||
"offlineNodeCount": "离线节点数",
|
||||
"onlineNodeCount": "在线节点数",
|
||||
"offline": "离线",
|
||||
"online": "在线",
|
||||
"onlineServers": "在线服务器",
|
||||
"onlineTotal": "在线/总数",
|
||||
"onlineTotal": "在线/总数",
|
||||
"onlineUsersCount": "在线用户数",
|
||||
"pending": "待处理",
|
||||
"pending": "待处理",
|
||||
"pendingTickets": "待处理工单",
|
||||
"register": "注册",
|
||||
"repurchase": "复购",
|
||||
@ -16,12 +25,15 @@
|
||||
"selectTypePlaceholder": "选择类型",
|
||||
"today": "今日",
|
||||
"todayDownloadTraffic": "今日下载流量",
|
||||
"todayTraffic": "今日流量",
|
||||
"todayUploadTraffic": "今日上传流量",
|
||||
"total": "总计",
|
||||
"totalIncome": "总收入",
|
||||
"totalServers": "总服务器",
|
||||
"traffic": "流量",
|
||||
"trafficRank": "流量排行",
|
||||
"type": "类型",
|
||||
"upload": "↑",
|
||||
"userTitle": "用户统计",
|
||||
"userTraffic": "用户流量",
|
||||
"users": "用户",
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
{
|
||||
"currentlyOnline": "目前在線",
|
||||
"email": "電子郵件",
|
||||
"month": "本月",
|
||||
"monthDownloadTraffic": "本月下載流量",
|
||||
"monthTraffic": "本月流量",
|
||||
"monthUploadTraffic": "本月上傳流量",
|
||||
"newPurchase": "新購",
|
||||
"nodeTraffic": "節點流量",
|
||||
"nodes": "節點",
|
||||
"offlineNodeCount": "離線節點數",
|
||||
"onlineNodeCount": "線上節點數",
|
||||
"offline": "離線",
|
||||
"online": "在線",
|
||||
"onlineServers": "在線伺服器",
|
||||
"onlineUsersCount": "線上用戶數",
|
||||
"pending": "待處理",
|
||||
"pendingTickets": "待處理工單",
|
||||
"register": "註冊",
|
||||
"repurchase": "回購",
|
||||
@ -16,9 +20,11 @@
|
||||
"selectTypePlaceholder": "選擇類型",
|
||||
"today": "今天",
|
||||
"todayDownloadTraffic": "今日下載流量",
|
||||
"todayTraffic": "今日流量",
|
||||
"todayUploadTraffic": "今日上傳流量",
|
||||
"total": "總計",
|
||||
"totalIncome": "總收入",
|
||||
"totalServers": "伺服器總數",
|
||||
"traffic": "流量",
|
||||
"trafficRank": "流量排行",
|
||||
"type": "類型",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// @ts-ignore
|
||||
|
||||
|
||||
// API 更新时间:
|
||||
// API 唯一标识:
|
||||
import * as ads from './ads';
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// @ts-ignore
|
||||
|
||||
|
||||
// API 更新时间:
|
||||
// API 唯一标识:
|
||||
import * as auth from './auth';
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { locales, NEXT_PUBLIC_DEFAULT_LANGUAGE } from '@/config/constants';
|
||||
import { isBrowser } from '@workspace/ui/utils';
|
||||
import { intlFormat } from 'date-fns';
|
||||
import Cookies from 'universal-cookie';
|
||||
|
||||
const cookies = new Cookies(null, {
|
||||
@ -49,3 +50,20 @@ export function Logout() {
|
||||
location.href = `/`;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatDate(date?: Date | number, showTime: boolean = true) {
|
||||
if (!date) return;
|
||||
const timeZone = localStorage.getItem('timezone') || 'UTC';
|
||||
return intlFormat(date, {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
...(showTime && {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
}),
|
||||
hour12: false,
|
||||
timeZone,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// @ts-ignore
|
||||
|
||||
|
||||
// API 更新时间:
|
||||
// API 唯一标识:
|
||||
import * as auth from './auth';
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// @ts-ignore
|
||||
|
||||
|
||||
// API 更新时间:
|
||||
// API 唯一标识:
|
||||
import * as announcement from './announcement';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user