import { forwardRef, useImperativeHandle, useRef, useEffect, useLayoutEffect, useState, useCallback, useMemo, createElement } from 'react' import { Table, Skeleton, Row, Col, Flex, Spin, Button, Input, Space, Tooltip, Form, Splitter } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import PropTypes from 'prop-types' import { useMediaQuery } from 'react-responsive' import { useContext } from 'react' import { ApiServerContext } from '../context/ApiServerContext' import config from '../../../config' import loglevel from 'loglevel' import { getModelProperties, getModelByName } from '../../../database/ObjectModels' import ObjectProperty from './ObjectProperty' import ObjectCard from './ObjectCard' import FilterSidebar from './FilterSidebar' import XMarkIcon from '../../Icons/XMarkIcon' import CheckIcon from '../../Icons/CheckIcon' import { useLocation } from 'react-router-dom' import QuestionCircleIcon from '../../Icons/QuestionCircleIcon' import { AuthContext } from '../context/AuthContext' import { ElectronContext } from '../context/ElectronContext' import { useActions } from '../context/ActionsContext' import ActionsIcon from '../../Icons/ActionsIcon' import FilterIcon from '../../Icons/FilterIcon' import ScrollBox from './ScrollBox' import { useTableStatePersistence } from '../context/TableStateContext' const logger = loglevel.getLogger('DasboardTable') logger.setLevel(config.logLevel) const SCROLL_THRESHOLD = 50 const SKELETON_HEIGHT = 49.5 const getCardColSpan = (containerWidth) => { if (containerWidth >= 2980) return 2 if (containerWidth >= 1780) return 4 if (containerWidth >= 1080) return 8 if (containerWidth >= 680) return 12 return 24 } const RowForm = ({ record, isEditing, onRegister, children }) => { const [form] = Form.useForm() useEffect(() => { if (isEditing && record && !record.isSkeleton) { form.setFieldsValue(record) onRegister(record._id, form) } return () => { if (record?._id) onRegister(record._id, null) } }, [isEditing, record, form, onRegister]) return (
{children}
) } RowForm.propTypes = { record: PropTypes.object, isEditing: PropTypes.bool, onRegister: PropTypes.func, children: PropTypes.node } const EditableRow = ({ record, isEditing, onRegister, ...props }) => { return ( ) } EditableRow.propTypes = { record: PropTypes.object, isEditing: PropTypes.bool, onRegister: PropTypes.func } const ObjectTable = forwardRef( ( { type, pageSize = 25, scrollHeight = 'calc(var(--unit-100vh) - 258px)', onDataChange, initialPage = 1, cards = false, visibleColumns = {}, masterFilter = {}, size = 'middle', onStateChange, showFilterSidebar = false, expandHeight = false, showActions = true, saveFilterInSession = false, saveFilterInUrl = false, useFilterInSession = false, useFilterInUrl = false, saveSortInSession = false, saveSortInUrl = false, useSortInSession = false, useSortInUrl = false, onRowAction }, ref ) => { const { token, userProfile } = useContext(AuthContext) const { isElectron } = useContext(ElectronContext) const { callAction } = useActions() const onStateChangeRef = useRef(onStateChange) useEffect(() => { onStateChangeRef.current = onStateChange }, [onStateChange]) const { fetchObjects, connected, subscribeToObjectUpdates, subscribeToObjectTypeUpdates, updateMultipleObjects, setObjectActivity, clearObjectActivity } = useContext(ApiServerContext) const isMobile = useMediaQuery({ maxWidth: 768 }) const location = useLocation() const { getPersistedFilter, getPersistedSorter, persistFilter, persistTableState, registerPageFilter, getActiveFilterValues } = useTableStatePersistence({ scope: type, pagePath: location.pathname, saveFilterInSession, saveFilterInUrl, useFilterInSession, useFilterInUrl, saveSortInSession, saveSortInUrl, useSortInSession, useSortInUrl }) var adjustedScrollHeight = scrollHeight if (isMobile) { adjustedScrollHeight = 'calc(var(--unit-100vh) - 298px)' } if (cards) { adjustedScrollHeight = 'calc(var(--unit-100vh) - 210px)' } if (isElectron) { adjustedScrollHeight = 'calc(var(--unit-100vh) - 238px)' } if (isMobile && isElectron) { adjustedScrollHeight = 'calc(var(--unit-100vh) - 282px)' } if (cards && isElectron) { adjustedScrollHeight = 'calc(var(--unit-100vh) - 258px)' } const tableRef = useRef(null) const model = getModelByName(type) const activeFilterRef = useRef({}) const tableSorterRef = useRef({}) const [sidebarFilter, setSidebarFilter] = useState({}) const [tableSorter, setTableSorter] = useState({}) const [initialized, setInitialized] = useState(false) // Table state const [pages, setPages] = useState([]) const pagesRef = useRef(pages) const tableData = useMemo( () => pages.flatMap((page) => page.items), [pages] ) const [loading, setLoading] = useState(true) const [lazyLoading, setLazyLoading] = useState(false) const [isEditing, setIsEditing] = useState(false) const [editLoading, setEditLoading] = useState(false) const rowFormsRef = useRef({}) const registerForm = useCallback((id, form) => { if (form) { rowFormsRef.current[id] = form } else { delete rowFormsRef.current[id] } }, []) useEffect(() => { onStateChangeRef.current?.({ isEditing, editLoading }) }, [isEditing, editLoading]) const subscribedIdsRef = useRef([]) // const [typeSubscribed, setTypeSubscribed] = useState(false) const unsubscribesRef = useRef([]) const updateEventHandlerRef = useRef() const subscribeToObjectTypeUpdatesRef = useRef(null) const subscribedTypeRef = useRef(null) const newEventHandlerRef = useRef() const subscriptionFilterRef = useRef() const subscribeToObjectTypeUpdatesFnRef = useRef( subscribeToObjectTypeUpdates ) const prevValuesRef = useRef({ type, masterFilter }) const rowActions = model.actions?.filter((action) => action.row == true) || [] const createSkeletonData = useCallback( (pageNum) => { return Array(pageSize) .fill(null) .map((_, index) => ({ _id: `skeleton-${pageNum}-${index}`, isSkeleton: true })) }, [pageSize] ) const createSkeletonPage = useCallback( (pageNum) => ({ pageNum, items: createSkeletonData(pageNum), isSkeletonPage: true }), [createSkeletonData] ) const loadingPagesRef = useRef(new Set()) const pendingScrollAnchorRef = useRef(null) const renderActions = (objectData) => { return ( {rowActions.map((action, index) => { var disabled = false if (action.disabled) { if (typeof action.disabled === 'function') { disabled = action.disabled({ ...objectData, _user: userProfile }) } else { disabled = action.disabled } } return (