From afbab60ab9ea4aad5f64526dce438980ca8b9ab3 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Sun, 21 Jun 2026 01:59:03 +0100 Subject: [PATCH] Started app update implementation. --- .../Dashboard/Management/AppUpdate.jsx | 186 ++++++++++++++++++ .../Dashboard/context/ApiServerContext.jsx | 44 ++++- src/database/sidebars/management.js | 6 + src/routes/ManagementRoutes.jsx | 2 + 4 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 src/components/Dashboard/Management/AppUpdate.jsx diff --git a/src/components/Dashboard/Management/AppUpdate.jsx b/src/components/Dashboard/Management/AppUpdate.jsx new file mode 100644 index 0000000..b9fb4ca --- /dev/null +++ b/src/components/Dashboard/Management/AppUpdate.jsx @@ -0,0 +1,186 @@ +import { useContext, useEffect, useMemo, useState } from 'react' +import { + Button, + Collapse, + Descriptions, + Empty, + Flex, + Select, + Space, + Typography +} from 'antd' +import { CaretLeftOutlined } from '@ant-design/icons' +import { ApiServerContext } from '../context/ApiServerContext' +import useCollapseState from '../hooks/useCollapseState' + +const { Title, Text, Link } = Typography +const { Option } = Select + +const AppUpdate = () => { + const { fetchAppUpdateBranches, fetchAppUpdateCurrent } = + useContext(ApiServerContext) + const [collapseState, updateCollapseState] = useCollapseState('AppUpdate', { + updater: true + }) + const [branches, setBranches] = useState([]) + const [selectedBranch, setSelectedBranch] = useState(undefined) + const [branchLoading, setBranchLoading] = useState(false) + const [checking, setChecking] = useState(false) + const [currentUpdate, setCurrentUpdate] = useState(null) + + useEffect(() => { + const loadBranches = async () => { + setBranchLoading(true) + const availableBranches = await fetchAppUpdateBranches() + setBranches(availableBranches) + + if (availableBranches.length > 0) { + setSelectedBranch((previous) => + previous && availableBranches.includes(previous) + ? previous + : availableBranches[0] + ) + } + setBranchLoading(false) + } + + loadBranches() + }, [fetchAppUpdateBranches]) + + const branchOptions = useMemo( + () => + branches.map((branch) => ( + + )), + [branches] + ) + + const handleCheckForUpdates = async () => { + if (!selectedBranch) return + setChecking(true) + const updateData = await fetchAppUpdateCurrent(selectedBranch) + setCurrentUpdate(updateData) + setChecking(false) + } + + const buildTimestamp = currentUpdate?.buildTimestamp + ? new Date(currentUpdate.buildTimestamp).toLocaleString() + : 'Unknown' + + return ( +
+ + updateCollapseState('updater', keys.length > 0)} + expandIcon={({ isActive }) => ( + + )} + className='no-h-padding-collapse' + > + + + Application Updater + + + } + key='1' + > + + + + + + + + + +
+ {currentUpdate ? ( + + + {currentUpdate.branch || selectedBranch} + + + {currentUpdate.buildNumber || 'Unknown'} + + + {currentUpdate.buildSource || 'Unknown'} + + + {currentUpdate.buildResult || 'Unknown'} + + + {buildTimestamp} + + + {currentUpdate.buildUrl ? ( + + Open Jenkins Build + + ) : ( + No build URL available + )} + + + {Array.isArray(currentUpdate.artifacts) && + currentUpdate.artifacts.length > 0 ? ( + + {currentUpdate.artifacts.map((artifact) => ( + + {artifact.fileName || artifact.relativePath} + + ))} + + ) : ( + No artifacts published + )} + + + ) : ( + + )} +
+ + + +
+ ) +} + +export default AppUpdate diff --git a/src/components/Dashboard/context/ApiServerContext.jsx b/src/components/Dashboard/context/ApiServerContext.jsx index f4d5ab9..8132d9f 100644 --- a/src/components/Dashboard/context/ApiServerContext.jsx +++ b/src/components/Dashboard/context/ApiServerContext.jsx @@ -1678,6 +1678,46 @@ const ApiServerProvider = ({ children }) => { return response.data }, []) + const fetchAppUpdateBranches = useCallback(async () => { + try { + const response = await axios.get(`${config.backendUrl}/appupdate/branches`, { + headers: { + Accept: 'application/json', + Authorization: `Bearer ${token}` + } + }) + return Array.isArray(response.data?.branches) ? response.data.branches : [] + } catch (err) { + console.error(err) + showError(err, () => { + fetchAppUpdateBranches() + }) + return [] + } + }, [token]) + + const fetchAppUpdateCurrent = useCallback( + async (branch) => { + try { + const response = await axios.get(`${config.backendUrl}/appupdate/current`, { + params: { branch }, + headers: { + Accept: 'application/json', + Authorization: `Bearer ${token}` + } + }) + return response.data + } catch (err) { + console.error(err) + showError(err, () => { + fetchAppUpdateCurrent(branch) + }) + return null + } + }, + [token] + ) + const flushFile = async (id) => { logger.debug('Flushing file...') try { @@ -1774,7 +1814,9 @@ const ApiServerProvider = ({ children }) => { getMarketplaceAuthUrl, refreshMarketplaceAuth, completeAppLaunchSession, - getAppLaunchSession + getAppLaunchSession, + fetchAppUpdateBranches, + fetchAppUpdateCurrent }} > {contextHolder} diff --git a/src/database/sidebars/management.js b/src/database/sidebars/management.js index c2c0b77..d0d2e5d 100644 --- a/src/database/sidebars/management.js +++ b/src/database/sidebars/management.js @@ -143,6 +143,12 @@ const managementSidebarItems = [ label: 'Settings', path: '/dashboard/management/settings' }, + { + key: 'appUpdate', + iconKey: 'settings', + label: 'App Update', + path: '/dashboard/management/appupdate' + }, { key: 'files', iconKey: 'file', diff --git a/src/routes/ManagementRoutes.jsx b/src/routes/ManagementRoutes.jsx index 5836544..dcb7bab 100644 --- a/src/routes/ManagementRoutes.jsx +++ b/src/routes/ManagementRoutes.jsx @@ -24,6 +24,7 @@ const CourierInfo = lazy(() => import('../components/Dashboard/Management/Courie const CourierServices = lazy(() => import('../components/Dashboard/Management/CourierServices')) const CourierServiceInfo = lazy(() => import('../components/Dashboard/Management/CourierServices/CourierServiceInfo.jsx')) const Settings = lazy(() => import('../components/Dashboard/Management/Settings')) +const AppUpdate = lazy(() => import('../components/Dashboard/Management/AppUpdate')) const AuditLogs = lazy(() => import('../components/Dashboard/Management/AuditLogs.jsx')) const NoteTypes = lazy(() => import('../components/Dashboard/Management/NoteTypes.jsx')) const NoteTypeInfo = lazy(() => import('../components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx')) @@ -206,6 +207,7 @@ const ManagementRoutes = [ element={} />, } />, + } />, } />, } />,