From ce3b3c900a97f15e4dab2d9fe4e1650448f24a7d Mon Sep 17 00:00:00 2001 From: DungTT Date: Wed, 3 Jun 2026 15:29:56 +0700 Subject: [PATCH] =?UTF-8?q?UI=20c=E1=BA=A3nh=20b=C3=A1o=20cho=20m=C3=A1y?= =?UTF-8?q?=20kh=C3=B4ng=20ph=E1=BA=A3i=20linux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web-client/src/main.jsx | 123 ++++++++++++++++++++++++++++++-------- web-client/src/styles.css | 13 ++++ 2 files changed, 111 insertions(+), 25 deletions(-) diff --git a/web-client/src/main.jsx b/web-client/src/main.jsx index fa20e0a..f91466f 100644 --- a/web-client/src/main.jsx +++ b/web-client/src/main.jsx @@ -141,6 +141,34 @@ function compareAgentVersions(currentVersion, latestVersion) { return AGENT_VERSION_COLLATOR.compare(current, latest); } +const CLIENT_OS_LABELS = { + android: 'Android', + linux: 'Linux', + macos: 'macOS', + unknown: 'unknown OS', + windows: 'Windows' +}; + +function detectClientOs() { + if (typeof navigator === 'undefined') return 'unknown'; + + const ua = String(navigator.userAgent || '').toLowerCase(); + const platform = [ + navigator.userAgentData?.platform, + navigator.platform + ].filter(Boolean).join(' ').toLowerCase(); + + if (ua.includes('android') || platform.includes('android')) return 'android'; + if (platform.includes('linux') || ua.includes('linux')) return 'linux'; + if (platform.includes('win') || ua.includes('windows')) return 'windows'; + if (platform.includes('mac') || ua.includes('mac os')) return 'macos'; + return 'unknown'; +} + +function getClientOsLabel(os) { + return CLIENT_OS_LABELS[os] || CLIENT_OS_LABELS.unknown; +} + function App() { const [settings, setSettings] = useState(readSettings); const [draftSettings, setDraftSettings] = useState(settings); @@ -166,6 +194,9 @@ function App() { const agentBaseUrl = settings.agentBaseUrl; const installCommand = `curl -fsSL ${joinUrl(packageBaseUrl, '/install-agent.sh')} | sudo bash`; const agentCommand = latestAgentPackage?.installCommand || installCommand; + const clientOs = useMemo(() => detectClientOs(), []); + const isClientLinux = clientOs === 'linux'; + const clientOsLabel = getClientOsLabel(clientOs); const installedByAppId = useMemo(() => { return new Map(installedApps.map((app) => [app.appId, app])); @@ -251,6 +282,17 @@ function App() { }, [packageBaseUrl]); const refreshAgent = useCallback(async () => { + if (!isClientLinux) { + setAgentHealth(null); + setSystemInfo(null); + setInstalledApps([]); + setAgentStatus({ + state: 'warning', + message: `Current client is ${clientOsLabel}. Web Client only supports Linux.` + }); + return false; + } + setAgentStatus({ state: 'loading', message: 'Đang kiểm tra Agent local' }); try { const health = await fetchAgentHealth(agentBaseUrl); @@ -271,7 +313,7 @@ function App() { setAgentStatus({ state: 'danger', message: getErrorMessage(error) }); return false; } - }, [agentBaseUrl]); + }, [agentBaseUrl, clientOsLabel, isClientLinux]); const refreshAll = useCallback(async () => { await Promise.all([refreshPackage(), refreshAgent()]); @@ -385,6 +427,11 @@ function App() { }, []); const runAppAction = useCallback(async (action, app) => { + if (!isClientLinux) { + notify('warning', 'Web Client chỉ hỗ trợ cài đặt trên máy Linux.'); + return; + } + if (!agentHealth) { notify('warning', 'Agent local đang offline. Cài Agent rồi bấm Retry.'); return; @@ -425,7 +472,7 @@ function App() { } finally { setBusyAction(''); } - }, [agentBaseUrl, agentHealth, notify, packageBaseUrl, startPreflightFailedTask, startTask]); + }, [agentBaseUrl, agentHealth, isClientLinux, notify, packageBaseUrl, startPreflightFailedTask, startTask]); const openEndpointDialog = useCallback(() => { setDraftSettings(settings); @@ -450,6 +497,11 @@ function App() { }, [draftSettings, notify]); const copyInstallCommand = useCallback(async () => { + if (!isClientLinux) { + notify('warning', 'Lệnh cài Agent chỉ hiển thị trên máy Linux.'); + return; + } + if (!navigator.clipboard?.writeText) { if (copyTextFallback(agentCommand)) { notify('success', agentNeedsUpdate ? 'Da copy lenh update Agent' : 'Da copy lenh cai Agent'); @@ -467,7 +519,7 @@ function App() { } catch { notify('warning', 'Không thể copy tự động trong browser này'); } - }, [agentCommand, agentNeedsUpdate, notify]); + }, [agentCommand, agentNeedsUpdate, isClientLinux, notify]); useEffect(() => { refreshAll(); @@ -551,9 +603,9 @@ function App() {
Runtime
${latestAgentPackage.version}` : ''}` : '127.0.0.1:5010'} - tone={agentNeedsUpdate ? 'warning' : (agentHealth ? 'success' : 'danger')} + title={!isClientLinux ? 'Linux client required' : (agentNeedsUpdate ? 'Agent update available' : (agentHealth ? 'Agent online' : 'Agent offline'))} + detail={!isClientLinux ? `${clientOsLabel} detected` : (agentHealth ? `${agentHealth.hostname || 'localhost'} · ${agentHealth.agentVersion || '-'}${agentNeedsUpdate ? ` -> ${latestAgentPackage.version}` : ''}` : '127.0.0.1:5010')} + tone={!isClientLinux ? 'warning' : (agentNeedsUpdate ? 'warning' : (agentHealth ? 'success' : 'danger'))} />
robot.installer - {agentHealth ? 'Ready for install' : 'Waiting for Agent'} + {!isClientLinux ? 'Linux client required' : (agentHealth ? 'Ready for install' : 'Waiting for Agent')}
@@ -603,16 +655,18 @@ function App() {

Application catalog

Released apps từ package server và trạng thái cài đặt trên máy local.

-
- - - -
+ {isClientLinux && ( +
+ + + +
+ )}
@@ -622,7 +676,11 @@ function App() {
- {!agentHealth && ( + {!isClientLinux && ( + + )} + + {isClientLinux && !agentHealth && (