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
|
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) => {
|
const flushFile = async (id) => {
|
||||||
logger.debug('Flushing file...')
|
logger.debug('Flushing file...')
|
||||||
try {
|
try {
|
||||||
@ -1774,7 +1814,9 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
getMarketplaceAuthUrl,
|
getMarketplaceAuthUrl,
|
||||||
refreshMarketplaceAuth,
|
refreshMarketplaceAuth,
|
||||||
completeAppLaunchSession,
|
completeAppLaunchSession,
|
||||||
getAppLaunchSession
|
getAppLaunchSession,
|
||||||
|
fetchAppUpdateBranches,
|
||||||
|
fetchAppUpdateCurrent
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{contextHolder}
|
{contextHolder}
|
||||||
|
|||||||
@ -143,6 +143,12 @@ const managementSidebarItems = [
|
|||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
path: '/dashboard/management/settings'
|
path: '/dashboard/management/settings'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'appUpdate',
|
||||||
|
iconKey: 'settings',
|
||||||
|
label: 'App Update',
|
||||||
|
path: '/dashboard/management/appupdate'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'files',
|
key: 'files',
|
||||||
iconKey: 'file',
|
iconKey: 'file',
|
||||||
|
|||||||
@ -24,6 +24,7 @@ const CourierInfo = lazy(() => import('../components/Dashboard/Management/Courie
|
|||||||
const CourierServices = lazy(() => import('../components/Dashboard/Management/CourierServices'))
|
const CourierServices = lazy(() => import('../components/Dashboard/Management/CourierServices'))
|
||||||
const CourierServiceInfo = lazy(() => import('../components/Dashboard/Management/CourierServices/CourierServiceInfo.jsx'))
|
const CourierServiceInfo = lazy(() => import('../components/Dashboard/Management/CourierServices/CourierServiceInfo.jsx'))
|
||||||
const Settings = lazy(() => import('../components/Dashboard/Management/Settings'))
|
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 AuditLogs = lazy(() => import('../components/Dashboard/Management/AuditLogs.jsx'))
|
||||||
const NoteTypes = lazy(() => import('../components/Dashboard/Management/NoteTypes.jsx'))
|
const NoteTypes = lazy(() => import('../components/Dashboard/Management/NoteTypes.jsx'))
|
||||||
const NoteTypeInfo = lazy(() => import('../components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx'))
|
const NoteTypeInfo = lazy(() => import('../components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx'))
|
||||||
@ -206,6 +207,7 @@ const ManagementRoutes = [
|
|||||||
element={<AppPasswordInfo />}
|
element={<AppPasswordInfo />}
|
||||||
/>,
|
/>,
|
||||||
<Route key='settings' path='management/settings' element={<Settings />} />,
|
<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='auditlogs' path='management/auditlogs' element={<AuditLogs />} />,
|
||||||
<Route key='taxrates' path='management/taxrates' element={<TaxRates />} />,
|
<Route key='taxrates' path='management/taxrates' element={<TaxRates />} />,
|
||||||
<Route
|
<Route
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user