Started app update implementation.
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
This commit is contained in:
parent
c581705cdd
commit
afbab60ab9
186
src/components/Dashboard/Management/AppUpdate.jsx
Normal file
186
src/components/Dashboard/Management/AppUpdate.jsx
Normal file
@ -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) => (
|
||||
<Option key={branch} value={branch}>
|
||||
{branch}
|
||||
</Option>
|
||||
)),
|
||||
[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 (
|
||||
<div style={{ height: '100%', minHeight: 0, overflowY: 'auto' }}>
|
||||
<Flex vertical gap='large'>
|
||||
<Collapse
|
||||
ghost
|
||||
expandIconPosition='end'
|
||||
activeKey={collapseState.updater ? ['1'] : []}
|
||||
onChange={(keys) => updateCollapseState('updater', keys.length > 0)}
|
||||
expandIcon={({ isActive }) => (
|
||||
<CaretLeftOutlined
|
||||
rotate={isActive ? 90 : 0}
|
||||
style={{ paddingTop: '9px' }}
|
||||
/>
|
||||
)}
|
||||
className='no-h-padding-collapse'
|
||||
>
|
||||
<Collapse.Panel
|
||||
header={
|
||||
<Flex
|
||||
align='center'
|
||||
justify='space-between'
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Title level={5} style={{ margin: 0 }}>
|
||||
Application Updater
|
||||
</Title>
|
||||
</Flex>
|
||||
}
|
||||
key='1'
|
||||
>
|
||||
<Descriptions bordered column={1}>
|
||||
<Descriptions.Item label='Branch'>
|
||||
<Select
|
||||
value={selectedBranch}
|
||||
onChange={setSelectedBranch}
|
||||
style={{ width: '100%' }}
|
||||
loading={branchLoading}
|
||||
placeholder='Select a branch'
|
||||
>
|
||||
{branchOptions}
|
||||
</Select>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label='Actions'>
|
||||
<Button
|
||||
type='primary'
|
||||
onClick={handleCheckForUpdates}
|
||||
loading={checking}
|
||||
disabled={!selectedBranch}
|
||||
>
|
||||
Check for Updates
|
||||
</Button>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<div style={{ marginTop: 16 }}>
|
||||
{currentUpdate ? (
|
||||
<Descriptions bordered column={1} title='Latest Build'>
|
||||
<Descriptions.Item label='Branch'>
|
||||
{currentUpdate.branch || selectedBranch}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label='Build Number'>
|
||||
{currentUpdate.buildNumber || 'Unknown'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label='Build Source'>
|
||||
{currentUpdate.buildSource || 'Unknown'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label='Build Status'>
|
||||
{currentUpdate.buildResult || 'Unknown'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label='Build Time'>
|
||||
{buildTimestamp}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label='Build URL'>
|
||||
{currentUpdate.buildUrl ? (
|
||||
<Link href={currentUpdate.buildUrl} target='_blank'>
|
||||
Open Jenkins Build
|
||||
</Link>
|
||||
) : (
|
||||
<Text type='secondary'>No build URL available</Text>
|
||||
)}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label='Artifacts'>
|
||||
{Array.isArray(currentUpdate.artifacts) &&
|
||||
currentUpdate.artifacts.length > 0 ? (
|
||||
<Space direction='vertical'>
|
||||
{currentUpdate.artifacts.map((artifact) => (
|
||||
<Link
|
||||
key={artifact.url}
|
||||
href={artifact.url}
|
||||
target='_blank'
|
||||
>
|
||||
{artifact.fileName || artifact.relativePath}
|
||||
</Link>
|
||||
))}
|
||||
</Space>
|
||||
) : (
|
||||
<Text type='secondary'>No artifacts published</Text>
|
||||
)}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
) : (
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description='No update check has been run yet'
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</Flex>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppUpdate
|
||||
@ -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}
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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={<AppPasswordInfo />}
|
||||
/>,
|
||||
<Route key='settings' path='management/settings' element={<Settings />} />,
|
||||
<Route key='appupdate' path='management/appupdate' element={<AppUpdate />} />,
|
||||
<Route key='auditlogs' path='management/auditlogs' element={<AuditLogs />} />,
|
||||
<Route key='taxrates' path='management/taxrates' element={<TaxRates />} />,
|
||||
<Route
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user