diff --git a/src/components/Dashboard/Management/Filaments.jsx b/src/components/Dashboard/Management/Filaments.jsx index d2e23fd..f9d3419 100644 --- a/src/components/Dashboard/Management/Filaments.jsx +++ b/src/components/Dashboard/Management/Filaments.jsx @@ -1,210 +1,33 @@ // src/filaments.js -import React, { useState, useContext, useCallback, useEffect } from 'react' -import { useNavigate } from 'react-router-dom' -import axios from 'axios' -import { - Table, - Badge, - Button, - Flex, - Space, - Modal, - message, - Dropdown, - Typography, - Checkbox, - Popover, - Input, - Spin -} from 'antd' -import { createStyles } from 'antd-style' -import { LoadingOutlined } from '@ant-design/icons' +import React, { useContext, useRef, useState } from 'react' +import { Button, Flex, Space, Modal, message, Dropdown } from 'antd' import { AuthContext } from '../context/AuthContext' import NewFilament from './Filaments/NewFilament' -import IdDisplay from '../common/IdDisplay' -import FilamentIcon from '../../Icons/FilamentIcon' -import InfoCircleIcon from '../../Icons/InfoCircleIcon' + +import useColumnVisibility from '../hooks/useColumnVisibility' +import ColumnViewButton from '../common/ColumnViewButton' +import ObjectTable from '../common/ObjectTable' 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; - } - } - } - ` - } -}) +import ListIcon from '../../Icons/ListIcon' +import GridIcon from '../../Icons/GridIcon' +import useViewMode from '../hooks/useViewMode' const Filaments = () => { const [messageApi, contextHolder] = message.useMessage() - const navigate = useNavigate() - const { styles } = useStyle() - - const [filamentsData, setFilamentsData] = useState([]) const [newFilamentOpen, setNewFilamentOpen] = useState(false) - const [loading, setLoading] = useState(true) + const tableRef = useRef() + + // View mode state (cards/list), persisted in sessionStorage via custom hook + const [viewMode, setViewMode] = useViewMode('filament') + + const [columnVisibility, setColumnVisibility] = + useColumnVisibility('filament') const { authenticated } = useContext(AuthContext) - const [page, setPage] = useState(1) - const [hasMore, setHasMore] = useState(true) - const [lazyLoading, setLazyLoading] = useState(false) - const [filters, setFilters] = useState({}) - const [sorter, setSorter] = useState({}) - - const fetchFilamentsData = useCallback( - async (pageNum = 1, append = false) => { - try { - const response = await axios.get(`${config.backendUrl}/filaments`, { - 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) { - setFilamentsData((prev) => [...prev, ...newData]) - } else { - setFilamentsData(newData) - } - - setLoading(false) - setLazyLoading(false) - } catch (err) { - messageApi.info(err) - setLoading(false) - setLazyLoading(false) - } - }, - [messageApi, filters, sorter] - ) - - const handleScroll = useCallback( - (e) => { - const { target } = e - const scrollHeight = target.scrollHeight - const scrollTop = target.scrollTop - const clientHeight = target.clientHeight - - if ( - scrollHeight - scrollTop - clientHeight < 100 && - !lazyLoading && - hasMore - ) { - setLazyLoading(true) - const nextPage = page + 1 - setPage(nextPage) - fetchFilamentsData(nextPage, true) - } - }, - [page, lazyLoading, hasMore, fetchFilamentsData] - ) - - const getFilterDropdown = ({ - setSelectedKeys, - selectedKeys, - confirm, - clearFilters, - propertyName - }) => { - return ( -
- - - setSelectedKeys(e.target.value ? [e.target.value] : []) - } - onPressEnter={() => confirm()} - style={{ width: 200, display: 'block' }} - /> -
- ) - } - - const getViewDropdownItems = () => { - const columnItems = columns - .filter((col) => col.key && col.title !== '') - .map((col) => ( - { - updateColumnVisibility(col.key, e.target.checked) - }} - > - {col.title} - - )) - - return ( - - - {columnItems} - - - ) - } - - 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 actionItems = { items: [ { @@ -221,291 +44,65 @@ const Filaments = () => { ], onClick: ({ key }) => { if (key === 'reloadList') { - fetchFilamentsData() + tableRef.current?.reload() } else if (key === 'newFilament') { setNewFilamentOpen(true) } } } - const getFilamentActionItems = (id) => { - return { - items: [ - { - label: 'Info', - key: 'info', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'info') { - navigate(`/dashboard/management/filaments/info?filamentId=${id}`) - } - } - } - } - - // Column definitions - const columns = [ - { - title: '', - dataIndex: '', - key: 'icon', - width: 40, - fixed: 'left', - render: () => - }, - { - title: 'Name', - dataIndex: 'name', - key: 'name', - width: 200, - fixed: 'left', - filterDropdown: ({ - setSelectedKeys, - selectedKeys, - confirm, - clearFilters - }) => - getFilterDropdown({ - setSelectedKeys, - selectedKeys, - confirm, - clearFilters, - propertyName: 'name' - }), - onFilter: (value, record) => - record.name.toLowerCase().includes(value.toLowerCase()), - sorter: true - }, - { - title: 'ID', - dataIndex: '_id', - key: 'id', - width: 180, - render: (text) => ( - - ), - filterDropdown: ({ - setSelectedKeys, - selectedKeys, - confirm, - clearFilters - }) => - getFilterDropdown({ - setSelectedKeys, - selectedKeys, - confirm, - clearFilters, - propertyName: 'ID' - }), - onFilter: (value, record) => - record._id.toLowerCase().includes(value.toLowerCase()), - sorter: true - }, - { - title: 'Vendor', - dataIndex: 'vendor', - key: 'vendor', - width: 200, - render: (vendor) => { - return vendor.name - }, - filterDropdown: ({ - setSelectedKeys, - selectedKeys, - confirm, - clearFilters - }) => - getFilterDropdown({ - setSelectedKeys, - selectedKeys, - confirm, - clearFilters, - propertyName: 'vendor' - }), - onFilter: (value, record) => - record.vendor.name.toLowerCase().includes(value.toLowerCase()), - sorter: true - }, - { - title: 'Material', - dataIndex: 'type', - width: 150, - key: 'material', - filterDropdown: ({ - setSelectedKeys, - selectedKeys, - confirm, - clearFilters - }) => - getFilterDropdown({ - setSelectedKeys, - selectedKeys, - confirm, - clearFilters, - propertyName: 'material' - }), - onFilter: (value, record) => - record.type.toLowerCase().includes(value.toLowerCase()), - sorter: true - }, - { - title: 'Cost', - dataIndex: 'cost', - width: 120, - key: 'cost', - render: (cost) => { - return {'£' + cost + ' per kg'} - }, - sorter: true - }, - { - title: 'Colour', - dataIndex: 'color', - key: 'color', - width: 120, - render: (color) => { - return - }, - filterDropdown: ({ - setSelectedKeys, - selectedKeys, - confirm, - clearFilters - }) => - getFilterDropdown({ - setSelectedKeys, - selectedKeys, - confirm, - clearFilters, - propertyName: 'color' - }), - onFilter: (value, record) => - record.color.toLowerCase().includes(value.toLowerCase()), - sorter: true - }, - { - 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 [columnVisibility, updateColumnVisibility] = useColumnVisibility( - 'Filaments', - columns - ) - - const visibleColumns = columns.filter( - (col) => !col.key || columnVisibility[col.key] - ) - - useEffect(() => { - if (authenticated) { - fetchFilamentsData() - } - }, [authenticated, fetchFilamentsData]) - return ( <> {contextHolder} - + - - - + + + +