Added excel support.
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
1e2adb2b28
commit
4a03b7bfd4
@ -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',
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user