Added excel support.
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good

This commit is contained in:
Tom Butcher 2026-03-03 01:01:31 +00:00
parent 1e2adb2b28
commit 4a03b7bfd4
2 changed files with 89 additions and 9 deletions

View File

@ -1,11 +1,12 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { useState } from 'react' import { useState, useContext } from 'react'
import { Button, Dropdown, Modal } from 'antd' import { Button, Dropdown, Modal } from 'antd'
import ExcelIcon from '../../Icons/ExcelIcon' import ExcelIcon from '../../Icons/ExcelIcon'
import ODataIcon from '../../Icons/ODataIcon' import ODataIcon from '../../Icons/ODataIcon'
import CsvIcon from '../../Icons/CsvIcon' import CsvIcon from '../../Icons/CsvIcon'
import ExportIcon from '../../Icons/ExportIcon' import ExportIcon from '../../Icons/ExportIcon'
import ODataURL from './ODataURL' import ODataURL from './ODataURL'
import { ApiServerContext } from '../context/ApiServerContext'
const ExportListButton = ({ const ExportListButton = ({
objectType, objectType,
@ -14,16 +15,38 @@ const ExportListButton = ({
...buttonProps ...buttonProps
}) => { }) => {
const [odataModalOpen, setOdataModalOpen] = useState(false) const [odataModalOpen, setOdataModalOpen] = useState(false)
const [excelLoading, setExcelLoading] = useState(false)
const { exportToExcel } = useContext(ApiServerContext)
const handleExcelExport = async (mode) => {
setExcelLoading(true)
try {
await exportToExcel(objectType, mode)
} finally {
setExcelLoading(false)
}
}
const menuItems = [ const menuItems = [
{ {
key: 'excel', key: 'excel',
label: 'Excel', label: excelLoading ? 'Exporting...' : 'Excel',
icon: <ExcelIcon />, icon: <ExcelIcon />,
disabled: true, disabled: excelLoading,
onClick: () => { children: [
// TODO: implement Excel export {
key: 'excel-download',
label: 'Download',
disabled: excelLoading,
onClick: () => handleExcelExport('download')
},
{
key: 'excel-open',
label: 'Open in Excel',
disabled: excelLoading,
onClick: () => handleExcelExport('open')
} }
]
}, },
{ {
key: 'odata', key: 'odata',

View File

@ -120,8 +120,7 @@ const ApiServerProvider = ({ children }) => {
newSocket.on('notification', (data) => { newSocket.on('notification', (data) => {
let notification let notification
try { try {
notification = notification = typeof data === 'string' ? JSON.parse(data) : data
typeof data === 'string' ? JSON.parse(data) : data
} catch (err) { } catch (err) {
logger.error('Failed to parse notification:', err) logger.error('Failed to parse notification:', err)
return return
@ -916,6 +915,60 @@ const ApiServerProvider = ({ children }) => {
} }
} }
// Export list data to Excel and download or open in Excel
const exportToExcel = async (objectType, mode = 'download') => {
try {
if (mode === 'open') {
// Open in Excel: get a temporary https URL (Excel fetches it when opening)
const response = await axios.post(
`${config.backendUrl}/excel/${objectType}/open`,
{},
{
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
}
)
const url = response.data?.url
if (!url || typeof url !== 'string') {
throw new Error('No URL received from server')
}
const excelUri = `ms-excel:ofe|u|${url}`
window.location.href = excelUri
message.success('Opening in Excel')
} else if (mode === 'download') {
const response = await axios.get(
`${config.backendUrl}/excel/${objectType}`,
{
responseType: 'blob',
headers: {
Accept:
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
Authorization: `Bearer ${token}`
}
}
)
const blob = new Blob([response.data], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `${objectType}-export-${new Date().toISOString().slice(0, 10)}.xlsx`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
message.success('Excel file downloaded')
} else {
throw new Error('Invalid mode')
}
} catch (err) {
console.error(err)
showError(err, () => exportToExcel(objectType))
}
}
// Download GCode file content // Download GCode file content
const fetchFileContent = async (file, download = false) => { const fetchFileContent = async (file, download = false) => {
try { try {
@ -1002,7 +1055,10 @@ const ApiServerProvider = ({ children }) => {
} }
} }
) )
spotlightCache.set(query, { data: response.data, timestamp: Date.now() }) spotlightCache.set(query, {
data: response.data,
timestamp: Date.now()
})
return response.data return response.data
} catch (err) { } catch (err) {
console.error(err) console.error(err)
@ -1458,6 +1514,7 @@ const ApiServerProvider = ({ children }) => {
fetchLoading, fetchLoading,
showError, showError,
fetchFileContent, fetchFileContent,
exportToExcel,
fetchTemplatePreview, fetchTemplatePreview,
fetchTemplatePDF, fetchTemplatePDF,
fetchNotes, fetchNotes,