// src/gcodefiles.js import React, { useEffect, useState, useContext, useCallback } from 'react' import { useNavigate } from 'react-router-dom' import axios from 'axios' import { Table, Button, Flex, Space, Modal, Dropdown, message, Typography, Spin, Checkbox, Popover, Input } from 'antd' import { createStyles } from 'antd-style' import { LoadingOutlined, DownloadOutlined } from '@ant-design/icons' import { AuthContext } from '../../Auth/AuthContext' import IdText from '../common/IdText' import NewProduct from './Products/NewProduct' import PartIcon from '../../Icons/PartIcon' import InfoCircleIcon from '../../Icons/InfoCircleIcon' import PlusIcon from '../../Icons/PlusIcon' import ReloadIcon from '../../Icons/ReloadIcon' import XMarkIcon from '../../Icons/XMarkIcon' import CheckIcon from '../../Icons/CheckIcon' import useColumnVisibility from '../hooks/useColumnVisibility' import TimeDisplay from '../common/TimeDisplay' import config from '../../../config' const { Text } = Typography const useStyle = createStyles(({ css, token }) => { const { antCls } = token return { customTable: css` ${antCls}-table { ${antCls}-table-container { ${antCls}-table-body, ${antCls}-table-content { scrollbar-width: thin; scrollbar-color: #eaeaea transparent; scrollbar-gutter: stable; } } } ` } }) const Parts = () => { const [messageApi, contextHolder] = message.useMessage() const navigate = useNavigate() const { styles } = useStyle() const [partsData, setPartsData] = useState([]) const [page, setPage] = useState(1) const [hasMore, setHasMore] = useState(true) const [loading, setLoading] = useState(true) const [lazyLoading, setLazyLoading] = useState(false) const [newProductOpen, setNewProductOpen] = useState(false) // Column definitions const columns = [ { title: '', dataIndex: '', key: '', width: 40, fixed: 'left', render: () => }, { title: 'Name', dataIndex: 'name', key: 'name', width: 200, fixed: 'left', render: (text) => {text}, filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => getFilterDropdown({ setSelectedKeys, selectedKeys, confirm, clearFilters, propertyName: 'name' }), onFilter: (value, record) => record.name.toLowerCase().includes(value.toLowerCase()) }, { title: 'ID', dataIndex: '_id', key: 'id', width: 165, render: (text) => }, { title: 'Product Name', key: 'productName', width: 200, render: (record) => {record.product.name}, filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => getFilterDropdown({ setSelectedKeys, selectedKeys, confirm, clearFilters, propertyName: 'product name' }), onFilter: (value, record) => record.product.name.toLowerCase().includes(value.toLowerCase()) }, { title: 'Product ID', key: 'productId', width: 165, render: (record) => ( ) }, { title: 'Created At', dataIndex: 'createdAt', key: 'createdAt', width: 180, render: (createdAt) => { if (createdAt) { return } else { return 'n/a' } }, sorter: true, defaultSortOrder: 'descend' }, { title: 'Updated At', dataIndex: 'updatedAt', key: 'updatedAt', width: 180, render: (updatedAt) => { if (updatedAt) { return } else { return 'n/a' } }, sorter: true, defaultSortOrder: 'descend' }, { title: 'Actions', key: 'actions', fixed: 'right', width: 150, render: (text, record) => { return ( ) } } ] const [filters, setFilters] = useState({}) const [sorter, setSorter] = useState({}) const [columnVisibility, updateColumnVisibility] = useColumnVisibility( 'Parts', columns ) const { authenticated } = useContext(AuthContext) const fetchPartsData = useCallback( async (pageNum = 1, append = false) => { try { const response = await axios.get(`${config.backendUrl}/parts`, { params: { page: pageNum, limit: 25, ...filters, sort: sorter.field, order: sorter.order }, headers: { Accept: 'application/json' }, withCredentials: true }) const newData = response.data setHasMore(newData.length === 25) if (append) { setPartsData((prev) => [...prev, ...newData]) } else { setPartsData(newData) } setLoading(false) setLazyLoading(false) } catch (error) { if (error.response) { messageApi.error( 'Error updating printer details:', error.response.status ) } else { messageApi.error( 'An unexpected error occurred. Please try again later.' ) } setLoading(false) setLazyLoading(false) } }, [messageApi, filters, sorter] ) useEffect(() => { if (authenticated) { fetchPartsData() } }, [authenticated, fetchPartsData]) const handleScroll = useCallback( (e) => { const { target } = e const scrollHeight = target.scrollHeight const scrollTop = target.scrollTop const clientHeight = target.clientHeight // If we're near the bottom (within 100px) and not currently loading if ( scrollHeight - scrollTop - clientHeight < 100 && !lazyLoading && hasMore ) { setLazyLoading(true) const nextPage = page + 1 setPage(nextPage) fetchPartsData(nextPage, true) } }, [page, lazyLoading, hasMore, fetchPartsData] ) const getPartActionItems = (id) => { return { items: [ { label: 'Info', key: 'info', icon: }, { label: 'Download', key: 'download', icon: } ], onClick: ({ key }) => { if (key === 'info') { navigate(`/dashboard/management/parts/info?partId=${id}`) } } } } const getFilterDropdown = ({ setSelectedKeys, selectedKeys, confirm, clearFilters, propertyName }) => { return (
setSelectedKeys(e.target.value ? [e.target.value] : []) } onPressEnter={() => confirm()} style={{ width: 200, display: 'block' }} />
) } const actionItems = { items: [ { label: 'New Product', key: 'newProduct', icon: }, { type: 'divider' }, { label: 'Reload List', key: 'reloadList', icon: } ], onClick: ({ key }) => { if (key === 'reloadList') { setPage(1) fetchPartsData(1) } else if (key === 'newProduct') { setNewProductOpen(true) } } } const handleTableChange = (pagination, filters, sorter) => { const newFilters = {} Object.entries(filters).forEach(([key, value]) => { if (value && value.length > 0) { newFilters[key] = value[0] } }) setPage(1) setFilters(newFilters) setSorter({ field: sorter.field, order: sorter.order }) } const getViewDropdownItems = () => { const columnItems = columns .filter((col) => col.key && col.title !== '') .map((col) => ( { updateColumnVisibility(col.key, e.target.checked) }} > {col.title} )) return ( {columnItems} ) } const visibleColumns = columns.filter( (col) => !col.key || columnVisibility[col.key] ) return ( <> {contextHolder} {lazyLoading && } />} }} onScroll={handleScroll} onChange={handleTableChange} showSorterTooltip={false} /> { setNewProductOpen(false) }} destroyOnClose > { setNewProductOpen(false) setPage(1) fetchPartsData(1) }} reset={newProductOpen} /> ) } export default Parts