mirror of
https://github.com/perfect-panel/ppanel-web.git
synced 2026-02-17 13:51:10 -05:00
🐛 fix: Simplify initialValues assignment and update node submission logic in NodesPage
This commit is contained in:
parent
8700cf6a91
commit
05d6c8947c
@ -74,6 +74,20 @@ export default function NodeForm(props: {
|
|||||||
const Scheme = useMemo(() => buildSchema(t), [t]);
|
const Scheme = useMemo(() => buildSchema(t), [t]);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const [autoFilledFields, setAutoFilledFields] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
|
const addAutoFilledField = (fieldName: string) => {
|
||||||
|
setAutoFilledFields((prev) => new Set(prev).add(fieldName));
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeAutoFilledField = (fieldName: string) => {
|
||||||
|
setAutoFilledFields((prev) => {
|
||||||
|
const newSet = new Set(prev);
|
||||||
|
newSet.delete(fieldName);
|
||||||
|
return newSet;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const form = useForm<NodeFormValues>({
|
const form = useForm<NodeFormValues>({
|
||||||
resolver: zodResolver(Scheme),
|
resolver: zodResolver(Scheme),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@ -112,12 +126,14 @@ export default function NodeForm(props: {
|
|||||||
const currentServer = useMemo(() => servers?.find((s) => s.id === serverId), [servers, serverId]);
|
const currentServer = useMemo(() => servers?.find((s) => s.id === serverId), [servers, serverId]);
|
||||||
|
|
||||||
const availableProtocols = useMemo(() => {
|
const availableProtocols = useMemo(() => {
|
||||||
return (currentServer?.protocols || [])
|
if (!currentServer?.protocols) return [];
|
||||||
|
|
||||||
|
return (currentServer.protocols as Array<{ type: ProtocolName; port?: number }>)
|
||||||
|
.filter((p) => p.type)
|
||||||
.map((p) => ({
|
.map((p) => ({
|
||||||
protocol: (p as any).type as ProtocolName,
|
protocol: p.type,
|
||||||
port: (p as any).port as number | undefined,
|
port: p.port,
|
||||||
}))
|
}));
|
||||||
.filter((p) => !!p.protocol);
|
|
||||||
}, [currentServer]);
|
}, [currentServer]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -139,60 +155,96 @@ export default function NodeForm(props: {
|
|||||||
const id = nextId ?? undefined;
|
const id = nextId ?? undefined;
|
||||||
form.setValue('server_id', id);
|
form.setValue('server_id', id);
|
||||||
|
|
||||||
const sel = servers.find((s) => s.id === id);
|
if (!id) {
|
||||||
const dirty = form.formState.dirtyFields as Record<string, any>;
|
setAutoFilledFields(new Set());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedServer = servers.find((s) => s.id === id);
|
||||||
|
if (!selectedServer) return;
|
||||||
|
|
||||||
const currentValues = form.getValues();
|
const currentValues = form.getValues();
|
||||||
|
const fieldsToFill: string[] = [];
|
||||||
|
|
||||||
if (!dirty.name) {
|
if (!currentValues.name || autoFilledFields.has('name')) {
|
||||||
form.setValue('name', (sel?.name as string) || '', { shouldDirty: false });
|
form.setValue('name', selectedServer.name as string, { shouldDirty: false });
|
||||||
|
fieldsToFill.push('name');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (!currentValues.address || autoFilledFields.has('address')) {
|
||||||
!dirty.address &&
|
form.setValue('address', selectedServer.address as string, { shouldDirty: false });
|
||||||
(!currentValues.address || currentValues.address === (sel?.address as string))
|
fieldsToFill.push('address');
|
||||||
) {
|
|
||||||
form.setValue('address', (sel?.address as string) || '', { shouldDirty: false });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const allowed = (sel?.protocols || [])
|
const protocols =
|
||||||
.map((p) => (p as any).type as ProtocolName)
|
(selectedServer.protocols as Array<{ type: ProtocolName; port?: number }>) || [];
|
||||||
.filter(Boolean);
|
const firstProtocol = protocols[0];
|
||||||
|
|
||||||
const currentProtocol = form.getValues('protocol') as ProtocolName;
|
if (firstProtocol && (!currentValues.protocol || autoFilledFields.has('protocol'))) {
|
||||||
|
form.setValue('protocol', firstProtocol.type, { shouldDirty: false });
|
||||||
|
fieldsToFill.push('protocol');
|
||||||
|
|
||||||
if (!allowed.includes(currentProtocol)) {
|
if (!currentValues.port || currentValues.port === 0 || autoFilledFields.has('port')) {
|
||||||
const firstProtocol = allowed[0] || '';
|
const port = firstProtocol.port || 0;
|
||||||
form.setValue('protocol', firstProtocol as any);
|
form.setValue('port', port, { shouldDirty: false });
|
||||||
|
fieldsToFill.push('port');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (firstProtocol) {
|
setAutoFilledFields(new Set(fieldsToFill));
|
||||||
handleProtocolChange(firstProtocol);
|
}
|
||||||
|
|
||||||
|
const handleManualFieldChange = (fieldName: keyof NodeFormValues, value: any) => {
|
||||||
|
form.setValue(fieldName, value);
|
||||||
|
removeAutoFilledField(fieldName);
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleProtocolChange(nextProto?: ProtocolName | null) {
|
||||||
|
const protocol = (nextProto || '') as ProtocolName | '';
|
||||||
|
form.setValue('protocol', protocol);
|
||||||
|
|
||||||
|
if (!protocol || !currentServer) {
|
||||||
|
removeAutoFilledField('protocol');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentValues = form.getValues();
|
||||||
|
const isPortAutoFilled = autoFilledFields.has('port');
|
||||||
|
|
||||||
|
removeAutoFilledField('protocol');
|
||||||
|
|
||||||
|
if (!currentValues.port || currentValues.port === 0 || isPortAutoFilled) {
|
||||||
|
const protocolData = (
|
||||||
|
currentServer.protocols as Array<{ type: ProtocolName; port?: number }>
|
||||||
|
)?.find((p) => p.type === protocol);
|
||||||
|
|
||||||
|
if (protocolData) {
|
||||||
|
const port = protocolData.port || 0;
|
||||||
|
form.setValue('port', port, { shouldDirty: false });
|
||||||
|
addAutoFilledField('port');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleProtocolChange(nextProto?: ProtocolName | null) {
|
async function handleSubmit(values: NodeFormValues) {
|
||||||
const p = (nextProto || '') as ProtocolName | '';
|
const result = await onSubmit(values);
|
||||||
form.setValue('protocol', p);
|
if (result) {
|
||||||
if (!p || !currentServer) return;
|
setOpen(false);
|
||||||
|
setAutoFilledFields(new Set());
|
||||||
const dirty = form.formState.dirtyFields as Record<string, any>;
|
|
||||||
const currentValues = form.getValues();
|
|
||||||
|
|
||||||
if (!dirty.port) {
|
|
||||||
const hit = (currentServer.protocols as any[]).find((x) => (x as any).type === p);
|
|
||||||
const port = (hit as any)?.port as number | undefined;
|
|
||||||
const newPort = typeof port === 'number' && port > 0 ? port : 0;
|
|
||||||
|
|
||||||
if (!currentValues.port || currentValues.port === 0 || currentValues.port === newPort) {
|
|
||||||
form.setValue('port', newPort, { shouldDirty: false });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sheet open={open} onOpenChange={setOpen}>
|
<Sheet open={open} onOpenChange={setOpen}>
|
||||||
<SheetTrigger asChild>
|
<SheetTrigger asChild>
|
||||||
<Button onClick={() => form.reset()}>{trigger}</Button>
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
form.reset();
|
||||||
|
setAutoFilledFields(new Set());
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{trigger}
|
||||||
|
</Button>
|
||||||
</SheetTrigger>
|
</SheetTrigger>
|
||||||
|
|
||||||
<SheetContent className='w-[560px] max-w-full'>
|
<SheetContent className='w-[560px] max-w-full'>
|
||||||
@ -253,7 +305,7 @@ export default function NodeForm(props: {
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<EnhancedInput
|
<EnhancedInput
|
||||||
{...field}
|
{...field}
|
||||||
onValueChange={(v) => form.setValue(field.name, v as string)}
|
onValueChange={(v) => handleManualFieldChange('name', v as string)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@ -270,7 +322,7 @@ export default function NodeForm(props: {
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<EnhancedInput
|
<EnhancedInput
|
||||||
{...field}
|
{...field}
|
||||||
onValueChange={(v) => form.setValue(field.name, v as string)}
|
onValueChange={(v) => handleManualFieldChange('address', v as string)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@ -291,7 +343,7 @@ export default function NodeForm(props: {
|
|||||||
min={1}
|
min={1}
|
||||||
max={65535}
|
max={65535}
|
||||||
placeholder='1-65535'
|
placeholder='1-65535'
|
||||||
onValueChange={(v) => form.setValue(field.name, Number(v))}
|
onValueChange={(v) => handleManualFieldChange('port', Number(v))}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@ -327,7 +379,7 @@ export default function NodeForm(props: {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
onClick={form.handleSubmit(onSubmit, (errors) => {
|
onClick={form.handleSubmit(handleSubmit, (errors) => {
|
||||||
const key = Object.keys(errors)[0] as keyof typeof errors;
|
const key = Object.keys(errors)[0] as keyof typeof errors;
|
||||||
if (key) toast.error(String(errors[key]?.message));
|
if (key) toast.error(String(errors[key]?.message));
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -152,26 +152,13 @@ export default function NodesPage() {
|
|||||||
trigger={t('edit')}
|
trigger={t('edit')}
|
||||||
title={t('drawerEditTitle')}
|
title={t('drawerEditTitle')}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
initialValues={{
|
initialValues={row}
|
||||||
name: row.name,
|
|
||||||
server_id: row.server_id,
|
|
||||||
protocol: row.protocol as any,
|
|
||||||
address: row.address as any,
|
|
||||||
port: row.port as any,
|
|
||||||
tags: (row.tags as any) || [],
|
|
||||||
}}
|
|
||||||
onSubmit={async (values) => {
|
onSubmit={async (values) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const body: API.UpdateNodeRequest = {
|
const body: API.UpdateNodeRequest = {
|
||||||
id: row.id,
|
...row,
|
||||||
name: values.name,
|
...values,
|
||||||
server_id: Number(values.server_id!),
|
|
||||||
protocol: values.protocol,
|
|
||||||
address: values.address,
|
|
||||||
port: Number(values.port!),
|
|
||||||
tags: values.tags || [],
|
|
||||||
enabled: row.enabled,
|
|
||||||
} as any;
|
} as any;
|
||||||
await updateNode(body);
|
await updateNode(body);
|
||||||
toast.success(t('updated'));
|
toast.success(t('updated'));
|
||||||
@ -201,16 +188,11 @@ export default function NodesPage() {
|
|||||||
key='copy'
|
key='copy'
|
||||||
variant='outline'
|
variant='outline'
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const { id, enabled, created_at, updated_at, ...rest } = row as any;
|
const { id, enabled, created_at, updated_at, sort, ...rest } = row as any;
|
||||||
await createNode({
|
await createNode({
|
||||||
name: rest.name,
|
...rest,
|
||||||
server_id: rest.server_id,
|
|
||||||
protocol: rest.protocol,
|
|
||||||
address: rest.address,
|
|
||||||
port: rest.port,
|
|
||||||
tags: rest.tags || [],
|
|
||||||
enabled: false,
|
enabled: false,
|
||||||
} as any);
|
});
|
||||||
toast.success(t('copied'));
|
toast.success(t('copied'));
|
||||||
ref.current?.refresh();
|
ref.current?.refresh();
|
||||||
}}
|
}}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user