From fdc862d16c984807c0588e12efb4735f437bcd37 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Sun, 6 Jul 2025 01:51:14 +0100 Subject: [PATCH] Refactored management components to utilize ObjectTable for consistent data representation and improved functionality. Updated column visibility handling and integrated new model properties for better data management. Removed unused imports and streamlined code for enhanced readability. --- .../Dashboard/Management/Filaments.jsx | 515 ++---------------- .../Dashboard/Management/NoteTypes.jsx | 261 +-------- .../Management/NoteTypes/NoteTypeInfo.jsx | 53 +- src/components/Dashboard/Management/Parts.jsx | 268 +-------- .../Dashboard/Management/Products.jsx | 9 +- src/components/Dashboard/Management/Users.jsx | 339 +----------- .../Dashboard/Management/Users/UserInfo.jsx | 65 +-- .../Dashboard/Management/Vendors.jsx | 319 +---------- .../Dashboard/Production/GCodeFiles.jsx | 334 +----------- .../Production/GCodeFiles/GCodeFileInfo.jsx | 321 ++++++----- src/components/Dashboard/Production/Jobs.jsx | 9 +- .../Dashboard/Production/Jobs/JobInfo.jsx | 275 +++++----- .../Dashboard/Production/Printers.jsx | 259 +-------- .../Dashboard/common/ActionHandler.jsx | 57 ++ .../Dashboard/common/ColumnViewButton.jsx | 50 ++ .../common}/EmailDisplay.jsx | 8 +- src/components/Dashboard/common/IdDisplay.jsx | 142 ++--- .../Dashboard/common/ObjectProperty.jsx | 8 +- .../Dashboard/common/ObjectTable.jsx | 420 +++++++++----- .../common}/UrlDisplay.jsx | 4 +- .../Dashboard/common/ViewButton.jsx | 14 +- .../Dashboard/context/ApiServerContext.js | 99 +++- .../Dashboard/hooks/useColumnVisibility.js | 20 +- src/database/ObjectModels.js | 3 + src/database/models/AuditLog.js | 11 + src/database/models/Filament.js | 43 +- src/database/models/FilamentStock.js | 12 + src/database/models/GCodeFile.js | 44 +- src/database/models/Initial.js | 11 + src/database/models/Job.js | 26 +- src/database/models/Note.js | 11 + src/database/models/NoteType.js | 54 +- src/database/models/Part.js | 64 +++ src/database/models/PartStock.js | 11 + src/database/models/Printer.js | 18 +- src/database/models/Product.js | 40 +- src/database/models/ProductStock.js | 12 + src/database/models/Spool.js | 11 + src/database/models/StockAudit.js | 11 + src/database/models/StockEvent.js | 11 + src/database/models/SubJob.js | 11 + src/database/models/User.js | 68 ++- src/database/models/Vendor.js | 20 +- 43 files changed, 1536 insertions(+), 2805 deletions(-) create mode 100644 src/components/Dashboard/common/ActionHandler.jsx create mode 100644 src/components/Dashboard/common/ColumnViewButton.jsx rename src/components/{Icons => Dashboard/common}/EmailDisplay.jsx (87%) rename src/components/{Icons => Dashboard/common}/UrlDisplay.jsx (94%) create mode 100644 src/database/models/Part.js 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} - + - - - + + + +