Compare commits
7 Commits
v1.6.0
..
b31c70a5c1
| Author | SHA1 | Date | |
|---|---|---|---|
| b31c70a5c1 | |||
| 75db379624 | |||
| c8401af672 | |||
| ea3964ebe5 | |||
| 5eac6a9f4a | |||
| 2182400adc | |||
| 5318b9cf44 |
+16
-7
@@ -1,17 +1,26 @@
|
|||||||
<a name="readme-top"></a>
|
<a name="readme-top"></a>
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
# [1.6.0](https://github.com/perfect-panel/ppanel-web/compare/v1.5.4...v1.6.0) (2025-10-28)
|
## [1.6.1](https://github.com/perfect-panel/ppanel-web/compare/v1.6.0...v1.6.1) (2025-11-05)
|
||||||
|
|
||||||
|
|
||||||
### ✨ Features
|
|
||||||
|
|
||||||
* Add server installation dialog and commands ([4429c9d](https://github.com/perfect-panel/ppanel-web/commit/4429c9d))
|
|
||||||
|
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
* Add typeRoots configuration to ensure type definitions are resolved correctly ([ad60ea9](https://github.com/perfect-panel/ppanel-web/commit/ad60ea9))
|
* Fixing issues with generating standard and quantum-resistant encryption keys ([5eac6a9](https://github.com/perfect-panel/ppanel-web/commit/5eac6a9))
|
||||||
|
|
||||||
|
<a name="readme-top"></a>
|
||||||
|
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
# [1.6.0](https://github.com/perfect-panel/ppanel-web/compare/v1.5.4...v1.6.0) (2025-10-28)
|
||||||
|
|
||||||
|
### ✨ Features
|
||||||
|
|
||||||
|
- Add server installation dialog and commands ([4429c9d](https://github.com/perfect-panel/ppanel-web/commit/4429c9d))
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
- Add typeRoots configuration to ensure type definitions are resolved correctly ([ad60ea9](https://github.com/perfect-panel/ppanel-web/commit/ad60ea9))
|
||||||
|
|
||||||
<a name="readme-top"></a>
|
<a name="readme-top"></a>
|
||||||
|
|
||||||
|
|||||||
@@ -444,7 +444,16 @@ export const PROTOCOL_FIELDS: Record<string, FieldConfig[]> = {
|
|||||||
placeholder: (t) => t('encryption_private_key_placeholder'),
|
placeholder: (t) => t('encryption_private_key_placeholder'),
|
||||||
group: 'encryption',
|
group: 'encryption',
|
||||||
generate: {
|
generate: {
|
||||||
function: generateMLKEM768KeyPair,
|
functions: [
|
||||||
|
{
|
||||||
|
label: (t) => t('generate_standard_encryption_key'),
|
||||||
|
function: generateRealityKeyPair,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: (t) => t('generate_quantum_resistant_key'),
|
||||||
|
function: generateMLKEM768KeyPair,
|
||||||
|
},
|
||||||
|
],
|
||||||
updateFields: {
|
updateFields: {
|
||||||
encryption_private_key: 'privateKey',
|
encryption_private_key: 'privateKey',
|
||||||
encryption_password: 'publicKey',
|
encryption_password: 'publicKey',
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ export type FieldConfig = {
|
|||||||
step?: number;
|
step?: number;
|
||||||
suffix?: string;
|
suffix?: string;
|
||||||
generate?: {
|
generate?: {
|
||||||
function: () => Promise<string | Record<string, string>> | string | Record<string, string>;
|
function?: () => Promise<string | Record<string, string>> | string | Record<string, string>;
|
||||||
|
functions?: {
|
||||||
|
label: string | ((t: (key: string) => string, protocol: any) => string);
|
||||||
|
function: () => Promise<string | Record<string, string>> | string | Record<string, string>;
|
||||||
|
}[];
|
||||||
updateFields?: Record<string, string>;
|
updateFields?: Record<string, string>;
|
||||||
};
|
};
|
||||||
condition?: (protocol: any, values: any) => boolean;
|
condition?: (protocol: any, values: any) => boolean;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import * as x25519 from '@noble/ed25519';
|
import { x25519 } from '@noble/curves/ed25519.js';
|
||||||
import { toB64Url } from './util';
|
import { toB64Url } from './util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a Reality key pair
|
* Generate a Reality key pair
|
||||||
* @returns An object containing the private and public keys in base64url format
|
* @returns An object containing the private and public keys in base64url format
|
||||||
*/
|
*/
|
||||||
export async function generateRealityKeyPair() {
|
export function generateRealityKeyPair() {
|
||||||
const { secretKey, publicKey } = await x25519.keygenAsync();
|
const { secretKey, publicKey } = x25519.keygen();
|
||||||
return { privateKey: toB64Url(secretKey), publicKey: toB64Url(publicKey) };
|
return { privateKey: toB64Url(secretKey), publicKey: toB64Url(publicKey) };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ export default function ServersPage() {
|
|||||||
const { fetchServers } = useServer();
|
const { fetchServers } = useServer();
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [migrating, setMigrating] = useState(false);
|
|
||||||
const ref = useRef<ProTableActions>(null);
|
const ref = useRef<ProTableActions>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ import {
|
|||||||
} from '@workspace/ui/components/accordion';
|
} from '@workspace/ui/components/accordion';
|
||||||
import { Badge } from '@workspace/ui/components/badge';
|
import { Badge } from '@workspace/ui/components/badge';
|
||||||
import { Button } from '@workspace/ui/components/button';
|
import { Button } from '@workspace/ui/components/button';
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@workspace/ui/components/dropdown-menu';
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -99,29 +105,68 @@ function DynamicField({
|
|||||||
onValueChange={(v) => fieldProps.onChange(v)}
|
onValueChange={(v) => fieldProps.onChange(v)}
|
||||||
suffix={
|
suffix={
|
||||||
field.generate ? (
|
field.generate ? (
|
||||||
<Button
|
field.generate.functions && field.generate.functions.length > 0 ? (
|
||||||
type='button'
|
<DropdownMenu>
|
||||||
variant='ghost'
|
<DropdownMenuTrigger asChild>
|
||||||
onClick={async () => {
|
<Button type='button' variant='ghost' size='sm'>
|
||||||
const result = await field.generate!.function();
|
<Icon icon='mdi:key' className='h-4 w-4' />
|
||||||
if (typeof result === 'string') {
|
</Button>
|
||||||
fieldProps.onChange(result);
|
</DropdownMenuTrigger>
|
||||||
} else if (field.generate!.updateFields) {
|
<DropdownMenuContent align='end'>
|
||||||
Object.entries(field.generate!.updateFields).forEach(
|
{field.generate.functions.map((genFunc, idx) => (
|
||||||
([fieldName, resultKey]) => {
|
<DropdownMenuItem
|
||||||
const fullFieldName = `protocols.${protocolIndex}.${fieldName}`;
|
key={idx}
|
||||||
form.setValue(fullFieldName, (result as any)[resultKey]);
|
onClick={async () => {
|
||||||
},
|
const result = await genFunc.function();
|
||||||
);
|
if (typeof result === 'string') {
|
||||||
} else {
|
fieldProps.onChange(result);
|
||||||
if (result.privateKey) {
|
} else if (field.generate!.updateFields) {
|
||||||
fieldProps.onChange(result.privateKey);
|
Object.entries(field.generate!.updateFields).forEach(
|
||||||
|
([fieldName, resultKey]) => {
|
||||||
|
const fullFieldName = `protocols.${protocolIndex}.${fieldName}`;
|
||||||
|
form.setValue(fullFieldName, (result as any)[resultKey]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (result.privateKey) {
|
||||||
|
fieldProps.onChange(result.privateKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{typeof genFunc.label === 'function'
|
||||||
|
? genFunc.label(t, protocolData)
|
||||||
|
: genFunc.label}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
) : field.generate.function ? (
|
||||||
|
<Button
|
||||||
|
type='button'
|
||||||
|
variant='ghost'
|
||||||
|
size='sm'
|
||||||
|
onClick={async () => {
|
||||||
|
const result = await field.generate!.function!();
|
||||||
|
if (typeof result === 'string') {
|
||||||
|
fieldProps.onChange(result);
|
||||||
|
} else if (field.generate!.updateFields) {
|
||||||
|
Object.entries(field.generate!.updateFields).forEach(
|
||||||
|
([fieldName, resultKey]) => {
|
||||||
|
const fullFieldName = `protocols.${protocolIndex}.${fieldName}`;
|
||||||
|
form.setValue(fullFieldName, (result as any)[resultKey]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (result.privateKey) {
|
||||||
|
fieldProps.onChange(result.privateKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<Icon icon='mdi:key' className='h-4 w-4' />
|
||||||
<Icon icon='mdi:key' className='h-4 w-4' />
|
</Button>
|
||||||
</Button>
|
) : null
|
||||||
) : (
|
) : (
|
||||||
field.suffix
|
field.suffix
|
||||||
)
|
)
|
||||||
@@ -356,7 +401,8 @@ export default function ServerForm(props: {
|
|||||||
...initialValues,
|
...initialValues,
|
||||||
protocols: PROTOCOLS.map((type) => {
|
protocols: PROTOCOLS.map((type) => {
|
||||||
const existingProtocol = initialValues.protocols?.find((p) => p.type === type);
|
const existingProtocol = initialValues.protocols?.find((p) => p.type === type);
|
||||||
return existingProtocol || getProtocolDefaultConfig(type);
|
const defaultConfig = getProtocolDefaultConfig(type);
|
||||||
|
return existingProtocol ? { ...defaultConfig, ...existingProtocol } : defaultConfig;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Display } from '@/components/display';
|
|
||||||
import { ProTable, ProTableActions } from '@/components/pro-table';
|
import { ProTable, ProTableActions } from '@/components/pro-table';
|
||||||
import {
|
import {
|
||||||
createUser,
|
createUser,
|
||||||
@@ -12,7 +11,6 @@ import {
|
|||||||
import { useSubscribe } from '@/store/subscribe';
|
import { useSubscribe } from '@/store/subscribe';
|
||||||
import { formatDate } from '@/utils/common';
|
import { formatDate } from '@/utils/common';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { Badge } from '@workspace/ui/components/badge';
|
|
||||||
import { Button } from '@workspace/ui/components/button';
|
import { Button } from '@workspace/ui/components/button';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -20,6 +18,13 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@workspace/ui/components/dropdown-menu';
|
} from '@workspace/ui/components/dropdown-menu';
|
||||||
|
import { Input } from '@workspace/ui/components/input';
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverClose,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@workspace/ui/components/popover';
|
||||||
import { ScrollArea } from '@workspace/ui/components/scroll-area';
|
import { ScrollArea } from '@workspace/ui/components/scroll-area';
|
||||||
import {
|
import {
|
||||||
Sheet,
|
Sheet,
|
||||||
@@ -31,6 +36,7 @@ import {
|
|||||||
import { Switch } from '@workspace/ui/components/switch';
|
import { Switch } from '@workspace/ui/components/switch';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@workspace/ui/components/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@workspace/ui/components/tabs';
|
||||||
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
||||||
|
import { FilePenLine } from 'lucide-react';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
@@ -38,11 +44,69 @@ import { useRef, useState } from 'react';
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { UserDetail } from './user-detail';
|
import { UserDetail } from './user-detail';
|
||||||
import UserForm from './user-form';
|
import UserForm from './user-form';
|
||||||
import { AuthMethodsForm } from './user-profile/auth-methods-form';
|
|
||||||
import { BasicInfoForm } from './user-profile/basic-info-form';
|
import { BasicInfoForm } from './user-profile/basic-info-form';
|
||||||
import { NotifySettingsForm } from './user-profile/notify-settings-form';
|
import { NotifySettingsForm } from './user-profile/notify-settings-form';
|
||||||
import UserSubscription from './user-subscription';
|
import UserSubscription from './user-subscription';
|
||||||
|
|
||||||
|
function getDeviceTypeInfo(userAgent = '') {
|
||||||
|
let deviceType = 'Unknown';
|
||||||
|
const ua = userAgent.toLowerCase();
|
||||||
|
|
||||||
|
if (ua.includes('android')) {
|
||||||
|
deviceType = 'Android';
|
||||||
|
} else if (ua.includes('iphone') || ua.includes('ios')) {
|
||||||
|
deviceType = 'iPhone';
|
||||||
|
} else if (ua.includes('ipad')) {
|
||||||
|
deviceType = 'iPad';
|
||||||
|
} else if (ua.includes('mac os') || ua.includes('mac')) {
|
||||||
|
deviceType = 'Mac';
|
||||||
|
} else if (ua.includes('windows')) {
|
||||||
|
deviceType = 'Windows';
|
||||||
|
} else if (ua.includes('linux')) {
|
||||||
|
deviceType = 'Linux';
|
||||||
|
}
|
||||||
|
|
||||||
|
return { deviceType };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为 RemarkForm 组件定义 props 类型
|
||||||
|
interface RemarkFormProps {
|
||||||
|
initialRemark?: string | null;
|
||||||
|
onSave: (remark: string) => void;
|
||||||
|
CloseComponent: React.ComponentType<{ asChild?: boolean; children: React.ReactNode }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新的子组件,在管理它自己的备注状态
|
||||||
|
const RemarkForm: React.FC<RemarkFormProps> = ({ onSave, initialRemark, CloseComponent }) => {
|
||||||
|
const [remark, setRemark] = useState<string>(initialRemark ?? '');
|
||||||
|
|
||||||
|
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setRemark(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveClick = () => {
|
||||||
|
onSave(remark);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='mb-2 text-sm font-semibold'>备注</div>
|
||||||
|
<Input
|
||||||
|
type='text'
|
||||||
|
value={remark}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder='在此输入备注...'
|
||||||
|
className='w-full'
|
||||||
|
/>
|
||||||
|
<CloseComponent asChild>
|
||||||
|
<Button onClick={handleSaveClick} variant='default' size={'sm'} className={'mt-2'}>
|
||||||
|
保存
|
||||||
|
</Button>
|
||||||
|
</CloseComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const t = useTranslations('user');
|
const t = useTranslations('user');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -56,6 +120,7 @@ export default function Page() {
|
|||||||
user_id: sp.get('user_id') || undefined,
|
user_id: sp.get('user_id') || undefined,
|
||||||
subscribe_id: sp.get('subscribe_id') || undefined,
|
subscribe_id: sp.get('subscribe_id') || undefined,
|
||||||
user_subscribe_id: sp.get('user_subscribe_id') || undefined,
|
user_subscribe_id: sp.get('user_subscribe_id') || undefined,
|
||||||
|
device_id: sp.get('device_id') || undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -128,20 +193,96 @@ export default function Page() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'auth_methods',
|
accessorKey: 'auth_methods',
|
||||||
header: t('userName'),
|
header: '绑定邮箱',
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const method = row.original.auth_methods?.[0];
|
const method = row.original.auth_methods;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Badge className='mr-1 uppercase' title={method?.verified ? t('verified') : ''}>
|
<Popover>
|
||||||
{method?.auth_type}
|
<PopoverTrigger>
|
||||||
</Badge>
|
<div className={'flex items-center'}>
|
||||||
{method?.auth_identifier}
|
{method?.find((v) => v.auth_type === 'email')?.auth_identifier || '待绑定'}
|
||||||
|
{row.original?.remark ? `(${row.original.remark})` : ''}
|
||||||
|
<FilePenLine size={14} className={'text-primary ml-2'} />
|
||||||
|
</div>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className={'w-64'}>
|
||||||
|
<RemarkForm
|
||||||
|
initialRemark={row.original.remark}
|
||||||
|
CloseComponent={PopoverClose}
|
||||||
|
onSave={async (remark) => {
|
||||||
|
const {
|
||||||
|
auth_methods,
|
||||||
|
user_devices,
|
||||||
|
enable_balance_notify,
|
||||||
|
enable_login_notify,
|
||||||
|
enable_subscribe_notify,
|
||||||
|
enable_trade_notify,
|
||||||
|
updated_at,
|
||||||
|
created_at,
|
||||||
|
id,
|
||||||
|
...rest
|
||||||
|
} = row.original;
|
||||||
|
await updateUserBasicInfo({
|
||||||
|
user_id: id,
|
||||||
|
...rest,
|
||||||
|
remark,
|
||||||
|
} as unknown as API.UpdateUserBasiceInfoRequest);
|
||||||
|
toast.success(t('updateSuccess'));
|
||||||
|
ref.current?.refresh();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
accessorKey: 'user_devices',
|
||||||
|
header: '绑定设备',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const devices = row?.original.user_devices ?? [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
||||||
|
{devices.map((v, index) => {
|
||||||
|
const { deviceType } = getDeviceTypeInfo(v.user_agent);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={v.id + '_wrapper'}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '4px 6px',
|
||||||
|
background: '#f8f8f8',
|
||||||
|
borderRadius: '4px',
|
||||||
|
border: '1px solid #e0e0e0',
|
||||||
|
fontSize: '12px',
|
||||||
|
lineHeight: '16px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ fontWeight: 500 }}>
|
||||||
|
ID:{v.id}({deviceType})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{index !== devices.length - 1 && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '1px',
|
||||||
|
background: '#eee',
|
||||||
|
margin: '4px 0',
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/*{
|
||||||
accessorKey: 'balance',
|
accessorKey: 'balance',
|
||||||
header: t('balance'),
|
header: t('balance'),
|
||||||
cell: ({ row }) => <Display type='currency' value={row.getValue('balance')} />,
|
cell: ({ row }) => <Display type='currency' value={row.getValue('balance')} />,
|
||||||
@@ -155,7 +296,7 @@ export default function Page() {
|
|||||||
accessorKey: 'commission',
|
accessorKey: 'commission',
|
||||||
header: t('commission'),
|
header: t('commission'),
|
||||||
cell: ({ row }) => <Display type='currency' value={row.getValue('commission')} />,
|
cell: ({ row }) => <Display type='currency' value={row.getValue('commission')} />,
|
||||||
},
|
},*/
|
||||||
{
|
{
|
||||||
accessorKey: 'refer_code',
|
accessorKey: 'refer_code',
|
||||||
header: t('inviteCode'),
|
header: t('inviteCode'),
|
||||||
@@ -203,6 +344,10 @@ export default function Page() {
|
|||||||
key: 'user_subscribe_id',
|
key: 'user_subscribe_id',
|
||||||
placeholder: t('subscriptionId'),
|
placeholder: t('subscriptionId'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'device_id',
|
||||||
|
placeholder: '设备id',
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
actions={{
|
actions={{
|
||||||
render: (row) => {
|
render: (row) => {
|
||||||
@@ -281,7 +426,7 @@ function ProfileSheet({ userId }: { userId: number }) {
|
|||||||
<TabsList className='mb-3'>
|
<TabsList className='mb-3'>
|
||||||
<TabsTrigger value='basic'>{t('basicInfoTitle')}</TabsTrigger>
|
<TabsTrigger value='basic'>{t('basicInfoTitle')}</TabsTrigger>
|
||||||
<TabsTrigger value='notify'>{t('notifySettingsTitle')}</TabsTrigger>
|
<TabsTrigger value='notify'>{t('notifySettingsTitle')}</TabsTrigger>
|
||||||
<TabsTrigger value='auth'>{t('authMethodsTitle')}</TabsTrigger>
|
{/*<TabsTrigger value='auth'>{t('authMethodsTitle')}</TabsTrigger>*/}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value='basic' className='mt-0'>
|
<TabsContent value='basic' className='mt-0'>
|
||||||
<BasicInfoForm user={user} refetch={refetch as any} />
|
<BasicInfoForm user={user} refetch={refetch as any} />
|
||||||
@@ -289,9 +434,9 @@ function ProfileSheet({ userId }: { userId: number }) {
|
|||||||
<TabsContent value='notify' className='mt-0'>
|
<TabsContent value='notify' className='mt-0'>
|
||||||
<NotifySettingsForm user={user} refetch={refetch as any} />
|
<NotifySettingsForm user={user} refetch={refetch as any} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value='auth' className='mt-0'>
|
{/*<TabsContent value='auth' className='mt-0'>
|
||||||
<AuthMethodsForm user={user} refetch={refetch as any} />
|
<AuthMethodsForm user={user} refetch={refetch as any} />
|
||||||
</TabsContent>
|
</TabsContent>*/}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -148,7 +148,8 @@ export function UserDetail({ id }: { id: number }) {
|
|||||||
|
|
||||||
const identifier =
|
const identifier =
|
||||||
data?.auth_methods.find((m) => m.auth_type === 'email')?.auth_identifier ||
|
data?.auth_methods.find((m) => m.auth_type === 'email')?.auth_identifier ||
|
||||||
data?.auth_methods[0]?.auth_identifier;
|
`设备Id:${data?.user_devices[0]?.id}` ||
|
||||||
|
'账号不存在';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HoverCard>
|
<HoverCard>
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Vypršelo",
|
"expired": "Vypršelo",
|
||||||
"extra": "Další konfigurace",
|
"extra": "Další konfigurace",
|
||||||
"flow": "Tok",
|
"flow": "Tok",
|
||||||
|
"generate_quantum_resistant_key": "Generovat kvantově odolný klíč",
|
||||||
|
"generate_standard_encryption_key": "Generovat standardní šifrovací klíč",
|
||||||
"hop_interval": "Interval skoku",
|
"hop_interval": "Interval skoku",
|
||||||
"hop_ports": "Porty skoku",
|
"hop_ports": "Porty skoku",
|
||||||
"hop_ports_placeholder": "např. 1-65535",
|
"hop_ports_placeholder": "např. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Abgelaufen",
|
"expired": "Abgelaufen",
|
||||||
"extra": "Zusätzliche Konfiguration",
|
"extra": "Zusätzliche Konfiguration",
|
||||||
"flow": "Fluss",
|
"flow": "Fluss",
|
||||||
|
"generate_quantum_resistant_key": "Quantenresistenten Schlüssel generieren",
|
||||||
|
"generate_standard_encryption_key": "Standard-Verschlüsselungsschlüssel generieren",
|
||||||
"hop_interval": "Hop-Intervall",
|
"hop_interval": "Hop-Intervall",
|
||||||
"hop_ports": "Hop-Ports",
|
"hop_ports": "Hop-Ports",
|
||||||
"hop_ports_placeholder": "z.B. 1-65535",
|
"hop_ports_placeholder": "z.B. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Expired",
|
"expired": "Expired",
|
||||||
"extra": "Extra Configuration",
|
"extra": "Extra Configuration",
|
||||||
"flow": "Flow",
|
"flow": "Flow",
|
||||||
|
"generate_quantum_resistant_key": "Generate Quantum-Resistant Key",
|
||||||
|
"generate_standard_encryption_key": "Generate Standard Encryption Key",
|
||||||
"hop_interval": "Hop interval",
|
"hop_interval": "Hop interval",
|
||||||
"hop_ports": "Hop ports",
|
"hop_ports": "Hop ports",
|
||||||
"hop_ports_placeholder": "e.g. 1-65535",
|
"hop_ports_placeholder": "e.g. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Expirado",
|
"expired": "Expirado",
|
||||||
"extra": "Configuración Extra",
|
"extra": "Configuración Extra",
|
||||||
"flow": "Flujo",
|
"flow": "Flujo",
|
||||||
|
"generate_quantum_resistant_key": "Generar clave resistente a cuánticos",
|
||||||
|
"generate_standard_encryption_key": "Generar clave de cifrado estándar",
|
||||||
"hop_interval": "Intervalo de salto",
|
"hop_interval": "Intervalo de salto",
|
||||||
"hop_ports": "Puertos de salto",
|
"hop_ports": "Puertos de salto",
|
||||||
"hop_ports_placeholder": "p. ej. 1-65535",
|
"hop_ports_placeholder": "p. ej. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Expirado",
|
"expired": "Expirado",
|
||||||
"extra": "Configuración Extra",
|
"extra": "Configuración Extra",
|
||||||
"flow": "Flujo",
|
"flow": "Flujo",
|
||||||
|
"generate_quantum_resistant_key": "Generar clave resistente a cuánticos",
|
||||||
|
"generate_standard_encryption_key": "Generar clave de cifrado estándar",
|
||||||
"hop_interval": "Intervalo de salto",
|
"hop_interval": "Intervalo de salto",
|
||||||
"hop_ports": "Puertos de salto",
|
"hop_ports": "Puertos de salto",
|
||||||
"hop_ports_placeholder": "p. ej. 1-65535",
|
"hop_ports_placeholder": "p. ej. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "منقضی شده",
|
"expired": "منقضی شده",
|
||||||
"extra": "پیکربندی اضافی",
|
"extra": "پیکربندی اضافی",
|
||||||
"flow": "جریان",
|
"flow": "جریان",
|
||||||
|
"generate_quantum_resistant_key": "تولید کلید مقاوم در برابر کوانتوم",
|
||||||
|
"generate_standard_encryption_key": "تولید کلید رمزگذاری استاندارد",
|
||||||
"hop_interval": "فاصله پرش",
|
"hop_interval": "فاصله پرش",
|
||||||
"hop_ports": "پورتهای پرش",
|
"hop_ports": "پورتهای پرش",
|
||||||
"hop_ports_placeholder": "مثلاً 1-65535",
|
"hop_ports_placeholder": "مثلاً 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Vanhentunut",
|
"expired": "Vanhentunut",
|
||||||
"extra": "Lisäasetukset",
|
"extra": "Lisäasetukset",
|
||||||
"flow": "Virta",
|
"flow": "Virta",
|
||||||
|
"generate_quantum_resistant_key": "Luo kvanttikestävä avain",
|
||||||
|
"generate_standard_encryption_key": "Luo standardi salausavain",
|
||||||
"hop_interval": "Hyppyvälit",
|
"hop_interval": "Hyppyvälit",
|
||||||
"hop_ports": "Hyppysatamat",
|
"hop_ports": "Hyppysatamat",
|
||||||
"hop_ports_placeholder": "esim. 1-65535",
|
"hop_ports_placeholder": "esim. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Expiré",
|
"expired": "Expiré",
|
||||||
"extra": "Configuration supplémentaire",
|
"extra": "Configuration supplémentaire",
|
||||||
"flow": "Flux",
|
"flow": "Flux",
|
||||||
|
"generate_quantum_resistant_key": "Générer une clé résistante aux quantiques",
|
||||||
|
"generate_standard_encryption_key": "Générer une clé de chiffrement standard",
|
||||||
"hop_interval": "Intervalle de saut",
|
"hop_interval": "Intervalle de saut",
|
||||||
"hop_ports": "Ports de saut",
|
"hop_ports": "Ports de saut",
|
||||||
"hop_ports_placeholder": "ex. 1-65535",
|
"hop_ports_placeholder": "ex. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "समय समाप्त",
|
"expired": "समय समाप्त",
|
||||||
"extra": "अतिरिक्त कॉन्फ़िगरेशन",
|
"extra": "अतिरिक्त कॉन्फ़िगरेशन",
|
||||||
"flow": "प्रवाह",
|
"flow": "प्रवाह",
|
||||||
|
"generate_quantum_resistant_key": "क्वांटम-प्रतिरोधी कुंजी उत्पन्न करें",
|
||||||
|
"generate_standard_encryption_key": "मानक एन्क्रिप्शन कुंजी उत्पन्न करें",
|
||||||
"hop_interval": "हॉप अंतराल",
|
"hop_interval": "हॉप अंतराल",
|
||||||
"hop_ports": "हॉप पोर्ट",
|
"hop_ports": "हॉप पोर्ट",
|
||||||
"hop_ports_placeholder": "जैसे 1-65535",
|
"hop_ports_placeholder": "जैसे 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Lejárt",
|
"expired": "Lejárt",
|
||||||
"extra": "További konfiguráció",
|
"extra": "További konfiguráció",
|
||||||
"flow": "Forgalom",
|
"flow": "Forgalom",
|
||||||
|
"generate_quantum_resistant_key": "Kvantumálló kulcs generálása",
|
||||||
|
"generate_standard_encryption_key": "Szabványos titkosítási kulcs generálása",
|
||||||
"hop_interval": "Ugrás időköz",
|
"hop_interval": "Ugrás időköz",
|
||||||
"hop_ports": "Ugrás portok",
|
"hop_ports": "Ugrás portok",
|
||||||
"hop_ports_placeholder": "pl. 1-65535",
|
"hop_ports_placeholder": "pl. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "期限切れ",
|
"expired": "期限切れ",
|
||||||
"extra": "追加設定",
|
"extra": "追加設定",
|
||||||
"flow": "フロー",
|
"flow": "フロー",
|
||||||
|
"generate_quantum_resistant_key": "量子耐性キーを生成",
|
||||||
|
"generate_standard_encryption_key": "標準暗号化キーを生成",
|
||||||
"hop_interval": "ホップ間隔",
|
"hop_interval": "ホップ間隔",
|
||||||
"hop_ports": "ホップポート",
|
"hop_ports": "ホップポート",
|
||||||
"hop_ports_placeholder": "例: 1-65535",
|
"hop_ports_placeholder": "例: 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "만료됨",
|
"expired": "만료됨",
|
||||||
"extra": "추가 구성",
|
"extra": "추가 구성",
|
||||||
"flow": "흐름",
|
"flow": "흐름",
|
||||||
|
"generate_quantum_resistant_key": "양자 저항 키 생성",
|
||||||
|
"generate_standard_encryption_key": "표준 암호화 키 생성",
|
||||||
"hop_interval": "홉 간격",
|
"hop_interval": "홉 간격",
|
||||||
"hop_ports": "홉 포트",
|
"hop_ports": "홉 포트",
|
||||||
"hop_ports_placeholder": "예: 1-65535",
|
"hop_ports_placeholder": "예: 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Utløpt",
|
"expired": "Utløpt",
|
||||||
"extra": "Ekstra konfigurasjon",
|
"extra": "Ekstra konfigurasjon",
|
||||||
"flow": "Flyt",
|
"flow": "Flyt",
|
||||||
|
"generate_quantum_resistant_key": "Generer kvantumresistent nøkkel",
|
||||||
|
"generate_standard_encryption_key": "Generer standard krypteringsnøkkel",
|
||||||
"hop_interval": "Hoppintervall",
|
"hop_interval": "Hoppintervall",
|
||||||
"hop_ports": "Hoppporter",
|
"hop_ports": "Hoppporter",
|
||||||
"hop_ports_placeholder": "f.eks. 1-65535",
|
"hop_ports_placeholder": "f.eks. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Wygasł",
|
"expired": "Wygasł",
|
||||||
"extra": "Dodatkowa konfiguracja",
|
"extra": "Dodatkowa konfiguracja",
|
||||||
"flow": "Przepływ",
|
"flow": "Przepływ",
|
||||||
|
"generate_quantum_resistant_key": "Generuj klucz odporny na kwanty",
|
||||||
|
"generate_standard_encryption_key": "Generuj standardowy klucz szyfrowania",
|
||||||
"hop_interval": "Interwał skoku",
|
"hop_interval": "Interwał skoku",
|
||||||
"hop_ports": "Porty skoku",
|
"hop_ports": "Porty skoku",
|
||||||
"hop_ports_placeholder": "np. 1-65535",
|
"hop_ports_placeholder": "np. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Expirado",
|
"expired": "Expirado",
|
||||||
"extra": "Configuração Extra",
|
"extra": "Configuração Extra",
|
||||||
"flow": "Fluxo",
|
"flow": "Fluxo",
|
||||||
|
"generate_quantum_resistant_key": "Gerar chave resistente a quânticos",
|
||||||
|
"generate_standard_encryption_key": "Gerar chave de criptografia padrão",
|
||||||
"hop_interval": "Intervalo de salto",
|
"hop_interval": "Intervalo de salto",
|
||||||
"hop_ports": "Portas de salto",
|
"hop_ports": "Portas de salto",
|
||||||
"hop_ports_placeholder": "ex. 1-65535",
|
"hop_ports_placeholder": "ex. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Expirat",
|
"expired": "Expirat",
|
||||||
"extra": "Configurație suplimentară",
|
"extra": "Configurație suplimentară",
|
||||||
"flow": "Flux",
|
"flow": "Flux",
|
||||||
|
"generate_quantum_resistant_key": "Generează cheie rezistentă la cuantică",
|
||||||
|
"generate_standard_encryption_key": "Generează cheie de criptare standard",
|
||||||
"hop_interval": "Interval de hop",
|
"hop_interval": "Interval de hop",
|
||||||
"hop_ports": "Porturi hop",
|
"hop_ports": "Porturi hop",
|
||||||
"hop_ports_placeholder": "de ex. 1-65535",
|
"hop_ports_placeholder": "de ex. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Истекло",
|
"expired": "Истекло",
|
||||||
"extra": "Дополнительная конфигурация",
|
"extra": "Дополнительная конфигурация",
|
||||||
"flow": "Поток",
|
"flow": "Поток",
|
||||||
|
"generate_quantum_resistant_key": "Генерировать квантово-устойчивый ключ",
|
||||||
|
"generate_standard_encryption_key": "Генерировать стандартный ключ шифрования",
|
||||||
"hop_interval": "Интервал перехода",
|
"hop_interval": "Интервал перехода",
|
||||||
"hop_ports": "Порты перехода",
|
"hop_ports": "Порты перехода",
|
||||||
"hop_ports_placeholder": "например, 1-65535",
|
"hop_ports_placeholder": "например, 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "หมดอายุ",
|
"expired": "หมดอายุ",
|
||||||
"extra": "การกำหนดค่าพิเศษ",
|
"extra": "การกำหนดค่าพิเศษ",
|
||||||
"flow": "การไหล",
|
"flow": "การไหล",
|
||||||
|
"generate_quantum_resistant_key": "สร้างคีย์ต้านทานควอนตัม",
|
||||||
|
"generate_standard_encryption_key": "สร้างคีย์เข้ารหัสมาตรฐาน",
|
||||||
"hop_interval": "ช่วงเวลาการกระโดด",
|
"hop_interval": "ช่วงเวลาการกระโดด",
|
||||||
"hop_ports": "พอร์ตการกระโดด",
|
"hop_ports": "พอร์ตการกระโดด",
|
||||||
"hop_ports_placeholder": "เช่น 1-65535",
|
"hop_ports_placeholder": "เช่น 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Süresi dolmuş",
|
"expired": "Süresi dolmuş",
|
||||||
"extra": "Ek Yapılandırma",
|
"extra": "Ek Yapılandırma",
|
||||||
"flow": "Akış",
|
"flow": "Akış",
|
||||||
|
"generate_quantum_resistant_key": "Kuantuma Dayanıklı Anahtar Oluştur",
|
||||||
|
"generate_standard_encryption_key": "Standart Şifreleme Anahtarı Oluştur",
|
||||||
"hop_interval": "Atlama aralığı",
|
"hop_interval": "Atlama aralığı",
|
||||||
"hop_ports": "Atlama portları",
|
"hop_ports": "Atlama portları",
|
||||||
"hop_ports_placeholder": "örn. 1-65535",
|
"hop_ports_placeholder": "örn. 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Термін дії закінчився",
|
"expired": "Термін дії закінчився",
|
||||||
"extra": "Додаткова конфігурація",
|
"extra": "Додаткова конфігурація",
|
||||||
"flow": "Потік",
|
"flow": "Потік",
|
||||||
|
"generate_quantum_resistant_key": "Згенерувати квантово-стійкий ключ",
|
||||||
|
"generate_standard_encryption_key": "Згенерувати стандартний ключ шифрування",
|
||||||
"hop_interval": "Інтервал стрибка",
|
"hop_interval": "Інтервал стрибка",
|
||||||
"hop_ports": "Порти стрибка",
|
"hop_ports": "Порти стрибка",
|
||||||
"hop_ports_placeholder": "наприклад, 1-65535",
|
"hop_ports_placeholder": "наприклад, 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "Đã hết hạn",
|
"expired": "Đã hết hạn",
|
||||||
"extra": "Cấu hình thêm",
|
"extra": "Cấu hình thêm",
|
||||||
"flow": "Lưu lượng",
|
"flow": "Lưu lượng",
|
||||||
|
"generate_quantum_resistant_key": "Tạo khóa chống lượng tử",
|
||||||
|
"generate_standard_encryption_key": "Tạo khóa mã hóa tiêu chuẩn",
|
||||||
"hop_interval": "Khoảng thời gian nhảy",
|
"hop_interval": "Khoảng thời gian nhảy",
|
||||||
"hop_ports": "Cổng nhảy",
|
"hop_ports": "Cổng nhảy",
|
||||||
"hop_ports_placeholder": "vd. 1-65535",
|
"hop_ports_placeholder": "vd. 1-65535",
|
||||||
|
|||||||
@@ -53,6 +53,8 @@
|
|||||||
"expired": "已过期",
|
"expired": "已过期",
|
||||||
"extra": "额外配置",
|
"extra": "额外配置",
|
||||||
"flow": "流控",
|
"flow": "流控",
|
||||||
|
"generate_quantum_resistant_key": "生成抗量子密钥",
|
||||||
|
"generate_standard_encryption_key": "生成标准加密密钥",
|
||||||
"hop_interval": "跳跃端口间隔",
|
"hop_interval": "跳跃端口间隔",
|
||||||
"hop_ports": "跳跃端口",
|
"hop_ports": "跳跃端口",
|
||||||
"hop_ports_placeholder": "例如 1-65535",
|
"hop_ports_placeholder": "例如 1-65535",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"expired": "已過期",
|
"expired": "已過期",
|
||||||
"extra": "額外配置",
|
"extra": "額外配置",
|
||||||
"flow": "流量",
|
"flow": "流量",
|
||||||
|
"generate_quantum_resistant_key": "生成抗量子密鑰",
|
||||||
|
"generate_standard_encryption_key": "生成標準加密密鑰",
|
||||||
"hop_interval": "跳躍間隔",
|
"hop_interval": "跳躍間隔",
|
||||||
"hop_ports": "跳躍端口",
|
"hop_ports": "跳躍端口",
|
||||||
"hop_ports_placeholder": "例如 1-65535",
|
"hop_ports_placeholder": "例如 1-65535",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lottiefiles/dotlottie-react": "^0.15.1",
|
"@lottiefiles/dotlottie-react": "^0.15.1",
|
||||||
|
"@noble/curves": "^2.0.1",
|
||||||
"@noble/ed25519": "^3.0.0",
|
"@noble/ed25519": "^3.0.0",
|
||||||
"@tanstack/react-query": "^5.85.5",
|
"@tanstack/react-query": "^5.85.5",
|
||||||
"@tanstack/react-query-next-experimental": "^5.85.5",
|
"@tanstack/react-query-next-experimental": "^5.85.5",
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"mlkem-wasm": "^0.0.6",
|
"mlkem-wasm": "^0.0.6",
|
||||||
"nanoid": "^5.1.5",
|
"nanoid": "^5.1.5",
|
||||||
"next": "^15.5.2",
|
"next": "15.5.7",
|
||||||
"next-intl": "^3.26.3",
|
"next-intl": "^3.26.3",
|
||||||
"next-runtime-env": "^3.3.0",
|
"next-runtime-env": "^3.3.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
|
|||||||
+1
@@ -2360,6 +2360,7 @@ declare namespace API {
|
|||||||
id: number;
|
id: number;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
balance: number;
|
balance: number;
|
||||||
|
remark: string;
|
||||||
commission: number;
|
commission: number;
|
||||||
referral_percentage: number;
|
referral_percentage: number;
|
||||||
only_first_purchase: boolean;
|
only_first_purchase: boolean;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
"framer-motion": "^12.23.12",
|
"framer-motion": "^12.23.12",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"lucide-react": "^0.542.0",
|
"lucide-react": "^0.542.0",
|
||||||
"next": "^15.5.2",
|
"next": "15.5.7",
|
||||||
"next-intl": "^3.26.3",
|
"next-intl": "^3.26.3",
|
||||||
"next-runtime-env": "^3.3.0",
|
"next-runtime-env": "^3.3.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"@iconify/react": "^5.2.0",
|
"@iconify/react": "^5.2.0",
|
||||||
"@lottiefiles/dotlottie-react": "^0.15.1",
|
"@lottiefiles/dotlottie-react": "^0.15.1",
|
||||||
"@monaco-editor/react": "^4.7.0",
|
"@monaco-editor/react": "^4.7.0",
|
||||||
|
"@noble/curves": "^2.0.1",
|
||||||
"@radix-ui/react-accordion": "^1.2.12",
|
"@radix-ui/react-accordion": "^1.2.12",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-aspect-ratio": "^1.1.7",
|
"@radix-ui/react-aspect-ratio": "^1.1.7",
|
||||||
@@ -104,6 +105,7 @@
|
|||||||
"name": "ppanel-admin-web",
|
"name": "ppanel-admin-web",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lottiefiles/dotlottie-react": "^0.15.1",
|
"@lottiefiles/dotlottie-react": "^0.15.1",
|
||||||
|
"@noble/curves": "^2.0.1",
|
||||||
"@noble/ed25519": "^3.0.0",
|
"@noble/ed25519": "^3.0.0",
|
||||||
"@tanstack/react-query": "^5.85.5",
|
"@tanstack/react-query": "^5.85.5",
|
||||||
"@tanstack/react-query-next-experimental": "^5.85.5",
|
"@tanstack/react-query-next-experimental": "^5.85.5",
|
||||||
@@ -113,7 +115,7 @@
|
|||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"mlkem-wasm": "^0.0.6",
|
"mlkem-wasm": "^0.0.6",
|
||||||
"nanoid": "^5.1.5",
|
"nanoid": "^5.1.5",
|
||||||
"next": "^15.5.2",
|
"next": "15.5.7",
|
||||||
"next-intl": "^3.26.3",
|
"next-intl": "^3.26.3",
|
||||||
"next-runtime-env": "^3.3.0",
|
"next-runtime-env": "^3.3.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
@@ -149,7 +151,7 @@
|
|||||||
"framer-motion": "^12.23.12",
|
"framer-motion": "^12.23.12",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"lucide-react": "^0.542.0",
|
"lucide-react": "^0.542.0",
|
||||||
"next": "^15.5.2",
|
"next": "15.5.7",
|
||||||
"next-intl": "^3.26.3",
|
"next-intl": "^3.26.3",
|
||||||
"next-runtime-env": "^3.3.0",
|
"next-runtime-env": "^3.3.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
@@ -526,30 +528,34 @@
|
|||||||
|
|
||||||
"@netlify/plugin-nextjs": ["@netlify/plugin-nextjs@5.14.0", "", {}, "sha512-8WEnVm88Ed6v6j/D0iqXDSnAW8Mf9P2CDVgBG3x2+vkiEI48X6Na7KnoQRyi6oulgA6foHIPWqgQIClsPiW20A=="],
|
"@netlify/plugin-nextjs": ["@netlify/plugin-nextjs@5.14.0", "", {}, "sha512-8WEnVm88Ed6v6j/D0iqXDSnAW8Mf9P2CDVgBG3x2+vkiEI48X6Na7KnoQRyi6oulgA6foHIPWqgQIClsPiW20A=="],
|
||||||
|
|
||||||
"@next/env": ["@next/env@15.5.6", "", {}, "sha512-3qBGRW+sCGzgbpc5TS1a0p7eNxnOarGVQhZxfvTdnV0gFI61lX7QNtQ4V1TSREctXzYn5NetbUsLvyqwLFJM6Q=="],
|
"@next/env": ["@next/env@15.5.7", "", {}, "sha512-4h6Y2NyEkIEN7Z8YxkA27pq6zTkS09bUSYC0xjd0NpwFxjnIKeZEeH591o5WECSmjpUhLn3H2QLJcDye3Uzcvg=="],
|
||||||
|
|
||||||
"@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.5.6", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-YxDvsT2fwy1j5gMqk3ppXlsgDopHnkM4BoxSVASbvvgh5zgsK8lvWerDzPip8k3WVzsTZ1O7A7si1KNfN4OZfQ=="],
|
"@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.5.6", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-YxDvsT2fwy1j5gMqk3ppXlsgDopHnkM4BoxSVASbvvgh5zgsK8lvWerDzPip8k3WVzsTZ1O7A7si1KNfN4OZfQ=="],
|
||||||
|
|
||||||
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ES3nRz7N+L5Umz4KoGfZ4XX6gwHplwPhioVRc25+QNsDa7RtUF/z8wJcbuQ2Tffm5RZwuN2A063eapoJ1u4nPg=="],
|
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw=="],
|
||||||
|
|
||||||
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-JIGcytAyk9LQp2/nuVZPAtj8uaJ/zZhsKOASTjxDug0SPU9LAM3wy6nPU735M1OqacR4U20LHVF5v5Wnl9ptTA=="],
|
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg=="],
|
||||||
|
|
||||||
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qvz4SVKQ0P3/Im9zcS2RmfFL/UCQnsJKJwQSkissbngnB/12c6bZTCB0gHTexz1s6d/mD0+egPKXAIRFVS7hQg=="],
|
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA=="],
|
||||||
|
|
||||||
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-FsbGVw3SJz1hZlvnWD+T6GFgV9/NYDeLTNQB2MXoPN5u9VA9OEDy6fJEfePfsUKAhJufFbZLgp0cPxMuV6SV0w=="],
|
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw=="],
|
||||||
|
|
||||||
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-3QnHGFWlnvAgyxFxt2Ny8PTpXtQD7kVEeaFat5oPAHHI192WKYB+VIKZijtHLGdBBvc16tiAkPTDmQNOQ0dyrA=="],
|
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw=="],
|
||||||
|
|
||||||
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-OsGX148sL+TqMK9YFaPFPoIaJKbFJJxFzkXZljIgA9hjMjdruKht6xDCEv1HLtlLNfkx3c5w2GLKhj7veBQizQ=="],
|
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.7", "", { "os": "linux", "cpu": "x64" }, "sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA=="],
|
||||||
|
|
||||||
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-ONOMrqWxdzXDJNh2n60H6gGyKed42Ieu6UTVPZteXpuKbLZTH4G4eBMsr5qWgOBA+s7F+uB4OJbZnrkEDnZ5Fg=="],
|
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ=="],
|
||||||
|
|
||||||
"@next/swc-win32-ia32-msvc": ["@next/swc-win32-ia32-msvc@14.2.33", "", { "os": "win32", "cpu": "ia32" }, "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q=="],
|
"@next/swc-win32-ia32-msvc": ["@next/swc-win32-ia32-msvc@14.2.33", "", { "os": "win32", "cpu": "ia32" }, "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q=="],
|
||||||
|
|
||||||
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-pxK4VIjFRx1MY92UycLOOw7dTdvccWsNETQ0kDHkBlcFH1GrTLUjSiHU1ohrznnux6TqRHgv5oflhfIWZwVROQ=="],
|
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.7", "", { "os": "win32", "cpu": "x64" }, "sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw=="],
|
||||||
|
|
||||||
|
"@noble/curves": ["@noble/curves@2.0.1", "", { "dependencies": { "@noble/hashes": "2.0.1" } }, "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw=="],
|
||||||
|
|
||||||
"@noble/ed25519": ["@noble/ed25519@3.0.0", "", {}, "sha512-QyteqMNm0GLqfa5SoYbSC3+Pvykwpn95Zgth4MFVSMKBB75ELl9tX1LAVsN4c3HXOrakHsF2gL4zWDAYCcsnzg=="],
|
"@noble/ed25519": ["@noble/ed25519@3.0.0", "", {}, "sha512-QyteqMNm0GLqfa5SoYbSC3+Pvykwpn95Zgth4MFVSMKBB75ELl9tX1LAVsN4c3HXOrakHsF2gL4zWDAYCcsnzg=="],
|
||||||
|
|
||||||
|
"@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="],
|
||||||
|
|
||||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||||
|
|
||||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||||
@@ -1912,7 +1918,7 @@
|
|||||||
|
|
||||||
"netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="],
|
"netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="],
|
||||||
|
|
||||||
"next": ["next@15.5.6", "", { "dependencies": { "@next/env": "15.5.6", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.6", "@next/swc-darwin-x64": "15.5.6", "@next/swc-linux-arm64-gnu": "15.5.6", "@next/swc-linux-arm64-musl": "15.5.6", "@next/swc-linux-x64-gnu": "15.5.6", "@next/swc-linux-x64-musl": "15.5.6", "@next/swc-win32-arm64-msvc": "15.5.6", "@next/swc-win32-x64-msvc": "15.5.6", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ=="],
|
"next": ["next@15.5.7", "", { "dependencies": { "@next/env": "15.5.7", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.7", "@next/swc-darwin-x64": "15.5.7", "@next/swc-linux-arm64-gnu": "15.5.7", "@next/swc-linux-arm64-musl": "15.5.7", "@next/swc-linux-x64-gnu": "15.5.7", "@next/swc-linux-x64-musl": "15.5.7", "@next/swc-win32-arm64-msvc": "15.5.7", "@next/swc-win32-x64-msvc": "15.5.7", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-+t2/0jIJ48kUpGKkdlhgkv+zPTEOoXyr60qXe68eB/pl3CMJaLeIGjzp5D6Oqt25hCBiBTt8wEeeAzfJvUKnPQ=="],
|
||||||
|
|
||||||
"next-intl": ["next-intl@3.26.5", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", "negotiator": "^1.0.0", "use-intl": "^3.26.5" }, "peerDependencies": { "next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, "sha512-EQlCIfY0jOhRldiFxwSXG+ImwkQtDEfQeSOEQp6ieAGSLWGlgjdb/Ck/O7wMfC430ZHGeUKVKax8KGusTPKCgg=="],
|
"next-intl": ["next-intl@3.26.5", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", "negotiator": "^1.0.0", "use-intl": "^3.26.5" }, "peerDependencies": { "next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, "sha512-EQlCIfY0jOhRldiFxwSXG+ImwkQtDEfQeSOEQp6ieAGSLWGlgjdb/Ck/O7wMfC430ZHGeUKVKax8KGusTPKCgg=="],
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ppanel-web",
|
"name": "ppanel-web",
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/perfect-panel/ppanel-web",
|
"homepage": "https://github.com/perfect-panel/ppanel-web",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
@@ -60,6 +60,7 @@
|
|||||||
"@iconify/react": "^5.2.0",
|
"@iconify/react": "^5.2.0",
|
||||||
"@lottiefiles/dotlottie-react": "^0.15.1",
|
"@lottiefiles/dotlottie-react": "^0.15.1",
|
||||||
"@monaco-editor/react": "^4.7.0",
|
"@monaco-editor/react": "^4.7.0",
|
||||||
|
"@noble/curves": "^2.0.1",
|
||||||
"@radix-ui/react-accordion": "^1.2.12",
|
"@radix-ui/react-accordion": "^1.2.12",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-aspect-ratio": "^1.1.7",
|
"@radix-ui/react-aspect-ratio": "^1.1.7",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const Popover = PopoverPrimitive.Root;
|
|||||||
const PopoverTrigger = PopoverPrimitive.Trigger;
|
const PopoverTrigger = PopoverPrimitive.Trigger;
|
||||||
|
|
||||||
const PopoverAnchor = PopoverPrimitive.Anchor;
|
const PopoverAnchor = PopoverPrimitive.Anchor;
|
||||||
|
const PopoverClose = PopoverPrimitive.Close;
|
||||||
|
|
||||||
const PopoverContent = React.forwardRef<
|
const PopoverContent = React.forwardRef<
|
||||||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||||
@@ -30,4 +31,4 @@ const PopoverContent = React.forwardRef<
|
|||||||
));
|
));
|
||||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };
|
export { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger };
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export function ProTable<
|
|||||||
const [rowCount, setRowCount] = useState<number>(0);
|
const [rowCount, setRowCount] = useState<number>(0);
|
||||||
const [pagination, setPagination] = useState({
|
const [pagination, setPagination] = useState({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: 200,
|
||||||
});
|
});
|
||||||
const loading = useRef(false);
|
const loading = useRef(false);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user