feat(nodes): add Dev channel option to node panel updates

The node update confirm dialog now offers a 'Dev channel (latest commit)' choice. The dev flag threads master -> nodes/updatePanel -> UpdatePanels -> remote.UpdatePanel -> the node's updatePanel endpoint, which calls StartUpdateChannel(dev) to install the rolling dev-latest build. With no dev flag the node keeps following its own channel setting.
This commit is contained in:
MHSanaei
2026-06-25 00:29:03 +02:00
parent 11c5b53fac
commit e8878b71a4
22 changed files with 100 additions and 25 deletions
+39 -8
View File
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query';
import { Button, Card, Col, ConfigProvider, Input, Layout, Modal, Result, Row, Spin, Statistic, Typography, message } from 'antd';
import { Alert, Button, Card, Checkbox, Col, ConfigProvider, Input, Layout, Modal, Result, Row, Spin, Statistic, Typography, message } from 'antd';
import {
CheckCircleOutlined,
CloseCircleOutlined,
@@ -21,6 +21,33 @@ import { setMessageInstance } from '@/utils/messageBus';
import { HttpUtil } from '@/utils';
import type { PanelUpdateInfo } from '../index/PanelUpdateModal';
// Confirm-dialog body that lets the operator pick the stable or dev channel for
// a node panel update. Reports changes via onChange so the imperative
// modal.confirm onOk can read the latest choice through a ref.
function UpdateChannelChoice({ onChange }: { onChange: (dev: boolean) => void }) {
const { t } = useTranslation();
const [dev, setDev] = useState(false);
return (
<div>
<p>{t('pages.nodes.updateConfirmContent')}</p>
<Checkbox
checked={dev}
onChange={(e) => { setDev(e.target.checked); onChange(e.target.checked); }}
>
{t('pages.nodes.updateDevChannel')}
</Checkbox>
{dev && (
<Alert
type="info"
showIcon
style={{ marginTop: 8 }}
message={t('pages.index.devChannelWarning')}
/>
)}
</div>
);
}
export default function NodesPage() {
const { t } = useTranslation();
const { isDark, isUltra, antdThemeConfig } = useTheme();
@@ -136,8 +163,10 @@ export default function NodesPage() {
await setEnable(node.id, next);
}, [setEnable]);
const runUpdate = useCallback(async (ids: number[]) => {
const msg = await updatePanels(ids);
const devRef = useRef(false);
const runUpdate = useCallback(async (ids: number[], dev: boolean) => {
const msg = await updatePanels(ids, dev);
if (!msg?.success) {
messageApi.error(msg?.msg || t('somethingWentWrong'));
return;
@@ -156,12 +185,13 @@ export default function NodesPage() {
}, [updatePanels, messageApi, t]);
const onUpdateNode = useCallback((node: NodeRecord) => {
devRef.current = false;
modal.confirm({
title: t('pages.nodes.updateConfirmTitle', { count: 1 }),
content: t('pages.nodes.updateConfirmContent'),
content: <UpdateChannelChoice onChange={(v) => { devRef.current = v; }} />,
okText: t('update'),
cancelText: t('cancel'),
onOk: () => runUpdate([node.id]),
onOk: () => runUpdate([node.id], devRef.current),
});
}, [modal, t, runUpdate]);
@@ -173,12 +203,13 @@ export default function NodesPage() {
messageApi.warning(t('pages.nodes.toasts.updateNoneEligible'));
return;
}
devRef.current = false;
modal.confirm({
title: t('pages.nodes.updateConfirmTitle', { count: eligible.length }),
content: t('pages.nodes.updateConfirmContent'),
content: <UpdateChannelChoice onChange={(v) => { devRef.current = v; }} />,
okText: t('update'),
cancelText: t('cancel'),
onOk: () => runUpdate(eligible),
onOk: () => runUpdate(eligible, devRef.current),
});
}, [modal, t, nodes, selectedIds, runUpdate, messageApi]);