// src/materials.js import { useEffect, useState, useContext, useCallback } from 'react' import { useNavigate } from 'react-router-dom' import axios from 'axios' import { Table, Button, Flex, Space, Modal, Dropdown, Spin } from 'antd' import { createStyles } from 'antd-style' import { LoadingOutlined } from '@ant-design/icons' import { useMessageContext } from '../context/MessageContext' import { AuthContext } from '../context/AuthContext' import NewMaterial from './Materials/NewMaterial' import IdDisplay from '../common/IdDisplay' import MaterialIcon from '../../Icons/MaterialIcon' import InfoCircleIcon from '../../Icons/InfoCircleIcon' import PlusIcon from '../../Icons/PlusIcon' import ReloadIcon from '../../Icons/ReloadIcon' import TimeDisplay from '../common/TimeDisplay' import config from '../../../config' 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 Materials = () => { const { showError } = useMessageContext() const navigate = useNavigate() const { styles } = useStyle() const [materialsData, setMaterialsData] = useState([]) const [page, setPage] = useState(1) const [hasMore, setHasMore] = useState(true) const [loading, setLoading] = useState(true) const [lazyLoading, setLazyLoading] = useState(false) const [newMaterialOpen, setNewMaterialOpen] = useState(false) const { authenticated } = useContext(AuthContext) const fetchMaterialsData = useCallback( async (pageNum = 1, append = false) => { try { const response = await axios.get(`${config.backendUrl}/materials`, { params: { page: pageNum, limit: 25 }, headers: { Accept: 'application/json' }, withCredentials: true }) const newData = response.data setHasMore(newData.length === 25) // If we get less than 25 items, we've reached the end if (append) { setMaterialsData((prev) => [...prev, ...newData]) } else { setMaterialsData(newData) } setLoading(false) setLazyLoading(false) } catch (error) { if (error.response) { showError( `Error updating material details: ${error.response.status}` ) } else { showError( 'An unexpected error occurred. Please try again later.' ) } setLoading(false) setLazyLoading(false) } }, [showError] ) useEffect(() => { if (authenticated) { fetchMaterialsData() } }, [authenticated, fetchMaterialsData]) 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) fetchMaterialsData(nextPage, true) } }, [page, lazyLoading, hasMore, fetchMaterialsData] ) const getMaterialActionItems = (id) => { return { items: [ { label: 'Info', key: 'info', icon: } ], onClick: ({ key }) => { if (key === 'info') { navigate(`/dashboard/management/materials/info?materialId=${id}`) } } } } const columns = [ { title: '', dataIndex: '', key: 'icon', width: 40, fixed: 'left', render: () => }, { title: 'Name', dataIndex: 'name', key: 'name', width: 200, fixed: 'left' }, { title: 'ID', dataIndex: '_id', key: 'id', width: 180, render: (text) => }, { title: 'Category', dataIndex: 'category', key: 'category', width: 150 }, { title: 'Created At', dataIndex: 'createdAt', key: 'createdAt', width: 180, render: (createdAt) => { if (createdAt) { return } else { return 'n/a' } } }, { title: 'Actions', key: 'actions', fixed: 'right', width: 150, render: (text, record) => { return ( ) } } ] const actionItems = { items: [ { label: 'New Material', key: 'newMaterial', icon: }, { type: 'divider' }, { label: 'Reload List', key: 'reloadList', icon: } ], onClick: ({ key }) => { if (key === 'reloadList') { fetchMaterialsData() } else if (key === 'newMaterial') { setNewMaterialOpen(true) } } } return ( <> }} scroll={{ y: 'calc(100vh - 270px)' }} onScroll={handleScroll} /> {lazyLoading && (
} />
)} { setNewMaterialOpen(false) }} > { setNewMaterialOpen(false) fetchMaterialsData() }} /> ) } export default Materials