Table fix

This commit is contained in:
bskjon 2025-04-01 19:49:57 +02:00
parent 48b7a0d2ad
commit 8bd9c9e121
12 changed files with 212 additions and 344 deletions

View File

@ -20,7 +20,6 @@ import FolderIcon from '@mui/icons-material/Folder';
import QueueIcon from '@mui/icons-material/Queue'; import QueueIcon from '@mui/icons-material/Queue';
import AppsIcon from '@mui/icons-material/Apps'; import AppsIcon from '@mui/icons-material/Apps';
import ConstructionIcon from '@mui/icons-material/Construction'; import ConstructionIcon from '@mui/icons-material/Construction';
import ProcesserTasksPage from './app/page/ProcesserTasksPage';
import DashboardIcon from '@mui/icons-material/Dashboard'; import DashboardIcon from '@mui/icons-material/Dashboard';
import GraphicEqIcon from '@mui/icons-material/GraphicEq'; import GraphicEqIcon from '@mui/icons-material/GraphicEq';
import HomeRepairServiceIcon from '@mui/icons-material/HomeRepairService'; import HomeRepairServiceIcon from '@mui/icons-material/HomeRepairService';
@ -110,7 +109,6 @@ function App() {
}}> }}>
<BrowserRouter> <BrowserRouter>
<Routes> <Routes>
<Route path='/tasks' element={<ProcesserTasksPage />} />
<Route path='/processer' element={<EventsPage />} /> <Route path='/processer' element={<EventsPage />} />
<Route path='/unprocessed' element={<UnprocessedFilesPage />} /> <Route path='/unprocessed' element={<UnprocessedFilesPage />} />
<Route path='/files' element={<ExplorePage />} /> <Route path='/files' element={<ExplorePage />} />

View File

@ -1,37 +1,28 @@
import { Box, TableContainer, Table, TableHead, TableRow, TableCell, Typography, TableBody, useTheme } from "@mui/material"; import { Box, TableContainer, Table, TableHead, TableRow, TableCell, Typography, TableBody, useTheme } from "@mui/material";
import { useState, useEffect, useMemo } from "react"; import { useState, useEffect, useMemo } from "react";
import { TablePropetyConfig, TableCellCustomizer, TableRowActionEvents } from "./table"; import { TablePropetyConfig, TableCellCustomizer, TableRowActionEvents, SortByAccessor, TableRowItem, DefaultTableContainer, ITableRow, SortBy, TableSorter } from "./table";
import IconArrowUp from '@mui/icons-material/ArrowUpward'; import IconArrowUp from '@mui/icons-material/ArrowUpward';
import IconArrowDown from '@mui/icons-material/ArrowDownward'; import IconArrowDown from '@mui/icons-material/ArrowDownward';
export interface ExpandableItem<T> {
tag: string;
expandElement: JSX.Element | null;
}
export type ExpandableRender<T> = (item: T) => ExpandableItem<T> | null; export type ExpandableRender<T> = (item: T) => JSX.Element | null;
export interface ExpandableTableItem {
rowId: string
}
export interface SortByAccessor {
accessor: string
order: 'asc' | 'desc'
}
export default function ExpandableTable<T extends ExpandableTableItem>({ items, columns, cellCustomizer: customizer, expandableRender, onRowClickedEvent, defaultSort}: { items: Array<T>, columns: Array<TablePropetyConfig>, cellCustomizer?: TableCellCustomizer<T>, expandableRender: ExpandableRender<T>, onRowClickedEvent?: TableRowActionEvents<T>, defaultSort?: SortByAccessor }) { export default function ExpandableTable<R extends ITableRow<U>, U>({ items, columns, cellCustomizer: customizer, expandableRender, onRowClickedEvent, defaultSort, sorter }: { items: Array<R>, columns: Array<TablePropetyConfig>, cellCustomizer?: TableCellCustomizer<R>, expandableRender: ExpandableRender<R>, onRowClickedEvent?: TableRowActionEvents<R>, defaultSort?: SortByAccessor, sorter?: TableSorter<R> }) {
const muiTheme = useTheme(); const muiTheme = useTheme();
const [order, setOrder] = useState<'asc' | 'desc'>('asc'); const [order, setOrder] = useState<'asc' | 'desc'>(defaultSort?.order ?? 'asc');
const [orderBy, setOrderBy] = useState<string>(''); const [orderBy, setOrderBy] = useState<string>(defaultSort?.accessor ?? columns[0].accessor ?? '');
const [expandedRowIds, setExpandedRowIds] = useState<Set<string>>(new Set()); const [expandedRowIds, setExpandedRowIds] = useState<Set<string>>(new Set());
const [selectedRow, setSelectedRow] = useState<T | null>(null); const [selectedRow, setSelectedRow] = useState<R | null>(null);
const [selectedRowId, setSelectedRowId] = useState<string | null>(null); const [selectedRowId, setSelectedRowId] = useState<string | null>(null);
const tableRowSingleClicked = (row: T | null) => { const tableRowSingleClicked = (row: R | null) => {
console.log("tableRowSingleClicked", row)
if (row != null && 'rowId' in row) { if (row != null && 'rowId' in row) {
setExpandedRowIds(prev => { setExpandedRowIds(prev => {
const newExpandedRows = new Set(prev); const newExpandedRows = new Set(prev);
console.log("newExpandedRows", newExpandedRows)
if (newExpandedRows.has(row.rowId)) { if (newExpandedRows.has(row.rowId)) {
newExpandedRows.delete(row.rowId); newExpandedRows.delete(row.rowId);
} else { } else {
@ -53,27 +44,23 @@ export default function ExpandableTable<T extends ExpandableTableItem>({ items,
} }
} }
const tableRowDoubleClicked = (row: T | null) => { const tableRowDoubleClicked = (row: R | null) => {
setSelectedRow(row); setSelectedRow(row);
if (row && onRowClickedEvent) { if (row && onRowClickedEvent) {
onRowClickedEvent.doubleClick(row); onRowClickedEvent.doubleClick(row);
} }
} }
const tableRowContextMenu = (e: React.MouseEvent<HTMLTableRowElement, MouseEvent> , row: T | null) => { const tableRowContextMenu = (e: React.MouseEvent<HTMLTableRowElement, MouseEvent> , row: R | null) => {
if (row && onRowClickedEvent && onRowClickedEvent.contextMenu) { if (row && onRowClickedEvent && onRowClickedEvent.contextMenu) {
e.preventDefault() e.preventDefault()
onRowClickedEvent.contextMenu(row, e.pageX, e.pageY) onRowClickedEvent.contextMenu(row, e.pageX, e.pageY)
} }
} }
const handleSort = (property: string) => {
const isAsc = orderBy === property && order === 'asc';
setOrder(isAsc ? 'desc' : 'asc');
setOrderBy(property);
};
const compareValues = (a: any, b: any, orderBy: string) => { const compareValues = (a: any, b: any, orderBy: string) => {
console.log("compareValues", a, b, orderBy)
if (typeof a[orderBy] === 'string') { if (typeof a[orderBy] === 'string') {
return a[orderBy].localeCompare(b[orderBy]); return a[orderBy].localeCompare(b[orderBy]);
} else if (typeof a[orderBy] === 'number') { } else if (typeof a[orderBy] === 'number') {
@ -84,22 +71,19 @@ export default function ExpandableTable<T extends ExpandableTableItem>({ items,
const sortedData = useMemo(() => { const sortedData = useMemo(() => {
return items.slice().sort((a, b) => { return [...items].sort((a, b) => {
if (order === 'asc') { if (sorter) {
return compareValues(a, b, orderBy); return sorter(a, b, order, orderBy);
} else { } else {
return compareValues(b, a, orderBy); if (order === 'asc') {
return compareValues(a, b, orderBy);
} else {
return compareValues(b, a, orderBy);
}
} }
});
}, [items, order, orderBy]);
useEffect(() => {
handleSort(columns[0].accessor)
if (defaultSort) {
setOrder(defaultSort.order);
setOrderBy(defaultSort.accessor);
} }
}, [defaultSort]) );
}, [items, order, orderBy]);
useEffect(() => { useEffect(() => {
if (selectedRowId) { if (selectedRowId) {
@ -113,20 +97,17 @@ export default function ExpandableTable<T extends ExpandableTableItem>({ items,
} }
}, [items, selectedRowId]); }, [items, selectedRowId]);
const handleSort = (property: string) => {
const isAsc = orderBy === property && order === 'asc';
setOrder(isAsc ? 'desc' : 'asc');
setOrderBy(property);
console.log("handleSort", property, isAsc ? 'desc' : 'asc')
};
return ( return (
<Box sx={{ <DefaultTableContainer>
display: "flex", <Table>
flexDirection: "column", // Bruk column-fleksretning
height: "100%",
overflow: "hidden"
}}>
<TableContainer sx={{
flex: 1,
overflowY: "auto",
position: "relative", // Legg til denne linjen for å justere layout
maxHeight: "100%" // Legg til denne linjen for å begrense høyden
}}>
<Table>
<TableHead sx={{ <TableHead sx={{
position: "sticky", position: "sticky",
top: 0, top: 0,
@ -150,7 +131,7 @@ export default function ExpandableTable<T extends ExpandableTableItem>({ items,
<TableBody sx={{ <TableBody sx={{
overflowY: "scroll" overflowY: "scroll"
}}> }}>
{sortedData?.map((row: T, rowIndex: number) => [ {sortedData?.map((row: R, rowIndex: number) => [
<TableRow key={row.rowId} <TableRow key={row.rowId}
onClick={() => tableRowSingleClicked(row)} onClick={() => tableRowSingleClicked(row)}
onDoubleClick={() => tableRowDoubleClicked(row)} onDoubleClick={() => tableRowDoubleClicked(row)}
@ -172,7 +153,7 @@ export default function ExpandableTable<T extends ExpandableTableItem>({ items,
(<TableRow key={row.rowId + "-expanded"}> (<TableRow key={row.rowId + "-expanded"}>
<TableCell colSpan={columns.length}> <TableCell colSpan={columns.length}>
{ {
expandableRender(row)?.expandElement expandableRender(row)
} }
</TableCell> </TableCell>
</TableRow>): null </TableRow>): null
@ -180,7 +161,6 @@ export default function ExpandableTable<T extends ExpandableTableItem>({ items,
])} ])}
</TableBody> </TableBody>
</Table> </Table>
</TableContainer> </DefaultTableContainer>
</Box>
) )
} }

View File

@ -4,18 +4,14 @@ import { RootState } from "../../store";
import IconArrowUp from '@mui/icons-material/ArrowUpward'; import IconArrowUp from '@mui/icons-material/ArrowUpward';
import IconArrowDown from '@mui/icons-material/ArrowDownward'; import IconArrowDown from '@mui/icons-material/ArrowDownward';
import { Table, TableHead, TableRow, TableCell, TableBody, Typography, Box, useTheme, TableContainer, IconButton } from "@mui/material"; import { Table, TableHead, TableRow, TableCell, TableBody, Typography, Box, useTheme, TableContainer, IconButton } from "@mui/material";
import { TablePropetyConfig, TableCellCustomizer, TableRowActionEvents } from "./table"; import { TablePropetyConfig, TableCellCustomizer, TableRowActionEvents, TableRowGroupedItem, DefaultTableContainer } from "./table";
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import ExpandLessIcon from '@mui/icons-material/ExpandLess';
export interface TableItemGroup<T> {
title: string
items: Array<T>
}
export default function SortableGroupedTable<T>({ items, columns, customizer, onRowClickedEvent }: { items: Array<TableItemGroup<T>>, columns: Array<TablePropetyConfig>, customizer?: TableCellCustomizer<T>, onRowClickedEvent?: TableRowActionEvents<T> }) { export default function SortableGroupedTable<T extends TableRowGroupedItem<T>>({ items, columns, customizer, onRowClickedEvent }: { items: Array<T>, columns: Array<TablePropetyConfig>, customizer?: TableCellCustomizer<T>, onRowClickedEvent?: TableRowActionEvents<T> }) {
const muiTheme = useTheme(); const muiTheme = useTheme();
const [order, setOrder] = useState<'asc' | 'desc'>('asc'); const [order, setOrder] = useState<'asc' | 'desc'>('asc');
const [orderBy, setOrderBy] = useState<string>(''); const [orderBy, setOrderBy] = useState<string>('');
const [selectedRow, setSelectedRow] = useState<T | null>(null); const [selectedRow, setSelectedRow] = useState<T | null>(null);
@ -45,7 +41,7 @@ export default function SortableGroupedTable<T>({ items, columns, customizer, on
} }
} }
const tableRowContextMenu = (e: React.MouseEvent<HTMLTableRowElement, MouseEvent> , row: T | null) => { const tableRowContextMenu = (e: React.MouseEvent<HTMLTableRowElement, MouseEvent>, row: T | null) => {
if (row && onRowClickedEvent && onRowClickedEvent.contextMenu) { if (row && onRowClickedEvent && onRowClickedEvent.contextMenu) {
e.preventDefault() e.preventDefault()
onRowClickedEvent.contextMenu(row, e.pageX, e.pageY) onRowClickedEvent.contextMenu(row, e.pageX, e.pageY)
@ -67,7 +63,11 @@ export default function SortableGroupedTable<T>({ items, columns, customizer, on
return 0; return 0;
}; };
const sortedData: Array<TableItemGroup<T>> = items.map((item: TableItemGroup<T>) => { /*const sortedData: Array<RowTableItem<T>> = items.map((item: RowTableItem<T>) => {
if (items.length === 0 || !item.items) {
console.log(item);
return item; // Return the item as is if there are no items to sort
}
return { return {
title: item.title, title: item.title,
items: item.items.slice().sort((a, b) => { items: item.items.slice().sort((a, b) => {
@ -78,8 +78,20 @@ export default function SortableGroupedTable<T>({ items, columns, customizer, on
} }
}) })
} }
}); });*/
const sortedData: Array<TableRowGroupedItem<T>> = items.map((item: TableRowGroupedItem<T>) => {
return {
...item,
items: item.items?.slice().sort((a, b) => {
if (order === 'asc') {
return compareValues(a, b, orderBy);
} else {
return compareValues(b, a, orderBy);
}
})
}
});
useEffect(() => { useEffect(() => {
@ -88,86 +100,74 @@ export default function SortableGroupedTable<T>({ items, columns, customizer, on
return ( return (
<Box sx={{ <DefaultTableContainer>
display: "flex", <Table>
flexDirection: "column", // Bruk column-fleksretning <TableHead sx={{
height: "100%", position: "sticky",
overflow: "hidden" top: 0,
}}> backgroundColor: muiTheme.palette.background.paper,
<TableContainer sx={{ }}>
flex: 1, <TableRow>
overflowY: "auto", {columns.map((column) => (
position: "relative", // Legg til denne linjen for å justere layout <TableCell key={column.accessor} onClick={() => handleSort(column.accessor)} sx={{ cursor: "pointer" }}>
maxHeight: "100%" // Legg til denne linjen for å begrense høyden <Box display="flex">
}}> {orderBy === column.accessor ?
<Table> (order === "asc" ? (<IconArrowDown />) : (<IconArrowUp />)) : (
<TableHead sx={{ <IconArrowDown sx={{ color: "transparent" }} />
position: "sticky", )
top: 0, }
backgroundColor: muiTheme.palette.background.paper, <Typography>{column.label}</Typography>
}}> </Box>
<TableRow> </TableCell>
{columns.map((column) => ( ))}
<TableCell key={column.accessor} onClick={() => handleSort(column.accessor)} sx={{ cursor: "pointer" }}> </TableRow>
<Box display="flex"> </TableHead>
{orderBy === column.accessor ?
(order === "asc" ? (<IconArrowDown />) : (<IconArrowUp />)) : ( {sortedData.map((item, index) => (
<IconArrowDown sx={{ color: "transparent" }} /> <>
) <TableHead
}
<Typography>{column.label}</Typography>
</Box>
</TableCell>
))}
</TableRow>
</TableHead>
{sortedData.map((item, index) => (
<>
<TableHead
sx={{ sx={{
backgroundColor: muiTheme.palette.primary.dark, backgroundColor: muiTheme.palette.primary.dark,
}}> }}>
<TableRow> <TableRow>
<TableCell colSpan={columns.length}> <TableCell colSpan={columns.length}>
<Box onClick={() => toggleExpand(index)} sx={{ display: "flex", justifyContent: "space-between" }}> <Box onClick={() => toggleExpand(index)} sx={{ display: "flex", justifyContent: "space-between" }}>
<Typography variant='h6'>{item.title}</Typography> <Typography variant='h6'>{item.title}</Typography>
<IconButton onClick={() => toggleExpand(index)}> <IconButton onClick={() => toggleExpand(index)}>
{expandedRows.has(index) ? <ExpandLessIcon /> : <ExpandMoreIcon />} {expandedRows.has(index) ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</IconButton> </IconButton>
</Box> </Box>
</TableCell> </TableCell>
</TableRow>
</TableHead>
<TableBody sx={{
display: expandedRows.has(index) ? 'table-row-group' : 'none',
overflowY: "scroll"
}}>
{item.items?.map((row: T, rowIndex: number) => (
<TableRow key={rowIndex}
onClick={() => tableRowSingleClicked(row)}
onDoubleClick={() => tableRowDoubleClicked(row)}
onContextMenu={(e) => {
tableRowContextMenu(e, row);
tableRowSingleClicked(row);
}}
style={{ cursor: "pointer", backgroundColor: selectedRow === row ? muiTheme.palette.action.selected : '' }}
>
{columns.map((column) => (
<TableCell key={column.accessor}>
{customizer && customizer(column.accessor, row) !== null
? customizer(column.accessor, row)
: <Typography variant='body1'>{(row as any)[column.accessor]}</Typography>}
</TableCell>
))}
</TableRow> </TableRow>
</TableHead> ))}
<TableBody sx={{ </TableBody>
display: expandedRows.has(index) ? 'table-row-group' : 'none', </>
overflowY: "scroll" ))}
}}>
{item.items?.map((row: T, rowIndex: number) => (
<TableRow key={rowIndex}
onClick={() => tableRowSingleClicked(row)}
onDoubleClick={() => tableRowDoubleClicked(row)}
onContextMenu={(e) => {
tableRowContextMenu(e, row);
tableRowSingleClicked(row);
}}
style={{ cursor: "pointer", backgroundColor: selectedRow === row ? muiTheme.palette.action.selected : '' }}
>
{columns.map((column) => (
<TableCell key={column.accessor}>
{customizer && customizer(column.accessor, row) !== null
? customizer(column.accessor, row)
: <Typography variant='body1'>{(row as any)[column.accessor]}</Typography>}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</>
))}
</Table> </Table>
</TableContainer> </DefaultTableContainer>
</Box>
) )
} }

View File

@ -1,17 +0,0 @@
export interface TablePropetyConfig {
label: string
accessor: string
}
export interface TableCellCustomizer<T> {
(accessor: string, data: T): JSX.Element | null
}
type NullableTableRowActionEvents<T> = TableRowActionEvents<T> | null;
export interface TableRowActionEvents<T> {
click: (row: T) => void;
doubleClick: (row: T) => void;
contextMenu?: (row: T, x: number, y: number) => void;
}

View File

@ -3,4 +3,4 @@ export interface CoordinatorOperationRequest {
file: string; file: string;
source: string; source: string;
mode: "FLOW" | "MANUAL"; mode: "FLOW" | "MANUAL";
} }

View File

@ -9,10 +9,12 @@ import { Box, Button, Typography, useTheme } from "@mui/material";
import { Tree } from "react-d3-tree"; import { Tree } from "react-d3-tree";
import { EventChain, EventGroup, EventGroups, set } from "../store/chained-events-slice"; import { EventChain, EventGroup, EventGroups, set } from "../store/chained-events-slice";
import { toUnixTimestamp, UnixTimestamp } from "../features/UxTc"; import { toUnixTimestamp, UnixTimestamp } from "../features/UxTc";
import { TableCellCustomizer, TablePropetyConfig } from "../features/table/table"; import { TableCellCustomizer, TablePropetyConfig, TableRowGroupedItem } from "../features/table/table";
import ExpandableTable, { ExpandableItem } from "../features/table/expandableTable"; import ExpandableTable from "../features/table/expandableTable";
import { CustomNodeElementProps } from 'react-d3-tree'; import { CustomNodeElementProps } from 'react-d3-tree';
export type ExpandableItemRow = TableRowGroupedItem<EventChain> & EventGroup
interface RawNodeDatum { interface RawNodeDatum {
name: string; name: string;
@ -37,12 +39,8 @@ const transformEventChain = (eventChain: EventChain): RawNodeDatum => ({
}); });
// Transformasjonsfunksjon for EventGroups // Transformasjonsfunksjon for EventGroups
const transformEventGroups = (group: EventGroup): RawNodeDatum[] => { const transformEventGroups = (group: ExpandableItemRow): RawNodeDatum[] => {
return group.events.map(transformEventChain); return group.items.map(transformEventChain);
}
interface EventGroupToTreeView extends EventGroup {
underView: JSX.Element
} }
export default function EventsChainPage() { export default function EventsChainPage() {
@ -51,12 +49,25 @@ export default function EventsChainPage() {
const client = useStompClient(); const client = useStompClient();
const cursor = useSelector((state: RootState) => state.chained) const cursor = useSelector((state: RootState) => state.chained)
const [useReferenceId, setUseReferenceId] = useState<string | null>(null); const [useReferenceId, setUseReferenceId] = useState<string | null>(null);
const [treeData, setTreeData] = useState<RawNodeDatum[] | null>(null); const [tableItems, setTableItems] = useState<Array<ExpandableItemRow>>([]);
useWsSubscription<Array<EventGroup>>("/topic/chained/all", (response) => { useWsSubscription<Array<EventGroup>>("/topic/chained/all", (response) => {
dispatch(set(response)) dispatch(set(response))
console.log(response)
}); });
useEffect(() => {
const items = cursor.groups.map((group: EventGroup) => {
return {
rowId: group.referenceId,
title: group.fileName ?? group.referenceId,
items: group.events,
referenceId: group.referenceId,
} as ExpandableItemRow
});
setTableItems(items);
console.log("tableItems", items)
}, [cursor]);
useEffect(() => { useEffect(() => {
client?.publish({ client?.publish({
@ -67,23 +78,10 @@ export default function EventsChainPage() {
const onRefresh = () => { const onRefresh = () => {
client?.publish({ client?.publish({
"destination": "/app/chained/all", "destination": "/app/chained/all",
"body": "Potato"
}) })
} }
useEffect(() => {
if (useReferenceId) {
const eventGroup = cursor.groups.find((group) => group.referenceId === useReferenceId);
if (eventGroup) {
const data = transformEventGroups(eventGroup);
console.log({
"info": "Tree data",
"data": data
})
setTreeData(data);
}
}
}, [useReferenceId, cursor])
const createTableCell: TableCellCustomizer<EventGroup> = (accessor, data) => { const createTableCell: TableCellCustomizer<EventGroup> = (accessor, data) => {
switch (accessor) { switch (accessor) {
@ -101,7 +99,7 @@ export default function EventsChainPage() {
const columns: Array<TablePropetyConfig> = [ const columns: Array<TablePropetyConfig> = [
{ label: "ReferenceId", accessor: "referenceId" }, { label: "ReferenceId", accessor: "referenceId" },
{ label: "File", accessor: "fileName" }, { label: "File", accessor: "title" },
{ label: "Created", accessor: "created" }, { label: "Created", accessor: "created" },
]; ];
@ -144,27 +142,23 @@ export default function EventsChainPage() {
</>); </>);
} }
function renderExpandableItem(item: EventGroup): ExpandableItem<EventGroup> | null { function renderExpandableItem(item: ExpandableItemRow): JSX.Element | null {
return { console.log(item);
tag: item.referenceId, const data = transformEventGroups(item);
expandElement: (() => { return (
const data = transformEventGroups(item); <>
return ( {(data) ? (
<> <div id="treeWrapper" style={{ width: '100%', height: '60vh', }}>
{(data) ? ( <Tree data={data} orientation="vertical" separation={{
<div id="treeWrapper" style={{ width: '100%', height: '60vh', }}> nonSiblings: 3,
<Tree data={data} orientation="vertical" separation={{ siblings: 2
nonSiblings: 3, }}
siblings: 2 renderCustomNodeElement={renderCustomNodeElement}
}} />
renderCustomNodeElement={renderCustomNodeElement} </div>
/> ) : <Typography>Tree data not available</Typography>}
</div> </>
) : <Typography>Tree data not available</Typography>} );
</>
);
})()
};
} }
@ -185,7 +179,7 @@ export default function EventsChainPage() {
position: "absolute", position: "absolute",
width: "100%" width: "100%"
}}> }}>
<ExpandableTable items={cursor?.groups ?? []} columns={columns} cellCustomizer={createTableCell} expandableRender={renderExpandableItem} /> <ExpandableTable items={tableItems ?? []} columns={columns} cellCustomizer={createTableCell} expandableRender={renderExpandableItem} />
</Box> </Box>
</Box> </Box>
</> </>

View File

@ -17,10 +17,10 @@ import AutoAwesomeMotionIcon from '@mui/icons-material/AutoAwesomeMotion';
import { client } from "stompjs"; import { client } from "stompjs";
import { useStompClient } from "react-stomp-hooks"; import { useStompClient } from "react-stomp-hooks";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { ContentEventState, ProcesserEventInfo, Status, update, updateEncodeProgress, WorkStatus } from "../store/work-slice"; import { ContentEventState, ContentEventStateItems, ProcesserEventInfo, Status, update, updateEncodeProgress, WorkStatus } from "../store/work-slice";
import ExpandableTable, { ExpandableItem } from "../features/table/expandableTable"; import ExpandableTable from "../features/table/expandableTable";
import { RootState } from "../store"; import { RootState } from "../store";
import { TableCellCustomizer, TablePropetyConfig } from "../features/table/table"; import { KeybasedComparator, SortBy, TableCellCustomizer, TablePropetyConfig, TableRowItem } from "../features/table/table";
import SimpleTable from "../features/table/sortableTable"; import SimpleTable from "../features/table/sortableTable";
import { UnixTimestamp } from "../features/UxTc"; import { UnixTimestamp } from "../features/UxTc";
import ProgressbarWithLabel from "../features/components/ProgressbarWithLabel"; import ProgressbarWithLabel from "../features/components/ProgressbarWithLabel";
@ -169,12 +169,24 @@ const transformToSteps = (state: ContentEventState): RawNodeDatum => ({
] ]
}); });
export type ExpandableItemRow = TableRowItem<ContentEventState>
export default function EventsPage() { export default function EventsPage() {
const client = useStompClient(); const client = useStompClient();
const dispatch = useDispatch(); const dispatch = useDispatch();
const events = useSelector((state: RootState) => state.work); const events: ContentEventStateItems = useSelector((state: RootState) => state.work);
const [tableItems, setTableItems] = useState<Array<ExpandableItemRow>>([]);
useEffect(() => {
const items = events.items.map((event: ContentEventState) => {
return {
rowId: event.referenceId,
title: event.referenceId,
item: event
} as ExpandableItemRow
});
setTableItems(items);
}, [events]);
useWsSubscription<Array<ContentEventState>>("/topic/tasks/all", (response) => { useWsSubscription<Array<ContentEventState>>("/topic/tasks/all", (response) => {
console.log(response) console.log(response)
@ -192,7 +204,7 @@ export default function EventsPage() {
}) })
}, [client]); }, [client]);
const createCellTable: TableCellCustomizer<ContentEventState> = (accessor, data) => { const createCellTable: TableCellCustomizer<ExpandableItemRow> = (accessor, data) => {
switch (accessor) { switch (accessor) {
case "runners": { case "runners": {
return (<> return (<>
@ -206,7 +218,7 @@ export default function EventsPage() {
x: 24, x: 24,
y: 24 y: 24
}} }}
data={transformToSteps(data)} data={transformToSteps(data.item)}
orientation="horizontal" orientation="horizontal"
separation={{ separation={{
nonSiblings: 1, nonSiblings: 1,
@ -226,8 +238,8 @@ export default function EventsPage() {
</>) </>)
}; };
case "created": { case "created": {
if (typeof data[accessor] === "number") { if (typeof data.item[accessor] === "number") {
return UnixTimestamp({ timestamp: data[accessor] }); return UnixTimestamp({ timestamp: data.item[accessor] });
} }
return null; return null;
} }
@ -235,33 +247,36 @@ export default function EventsPage() {
} }
}; };
function renderExpandableItem(item: ContentEventState): ExpandableItem<ContentEventState> | null { const sorter = (a: ExpandableItemRow, b: ExpandableItemRow, orderBy: SortBy, accessor: string) => {
const progress = item.encodeWork?.progress?.progress ?? undefined; if (orderBy === "asc") {
const showProgressbar = [WorkStatus.Pending, WorkStatus.Started, WorkStatus.Working, WorkStatus.Completed].includes(item?.encodeWork?.status) return KeybasedComparator(a.item, b.item, "created")
} else {
return KeybasedComparator(b.item, a.item, "created")
}
}
const processer = item.encodeWork // events.encodeWork[item.referenceId]; function renderExpandableItem(item: ExpandableItemRow): JSX.Element | null {
const data = item.item;
const progress = data.encodeWork?.progress?.progress ?? undefined;
const showProgressbar = [WorkStatus.Pending, WorkStatus.Started, WorkStatus.Working, WorkStatus.Completed].includes(data?.encodeWork?.status)
const processer = data.encodeWork // events.encodeWork[item.referenceId];
const showIndeterminate = processer?.status in [WorkStatus.Pending, WorkStatus.Started] || processer?.progress?.progress <= 0 const showIndeterminate = processer?.status in [WorkStatus.Pending, WorkStatus.Started] || processer?.progress?.progress <= 0
console.log({ console.log({
type: "info", type: "info",
processer: processer, processer: processer,
showIndeterminate: showIndeterminate, showIndeterminate: showIndeterminate,
showProgressbar: showProgressbar, showProgressbar: showProgressbar,
isWorking: item.encodeWork?.status == WorkStatus.Working isWorking: data.encodeWork?.status == WorkStatus.Working
}); });
return { return (
tag: item.referenceId, <>
expandElement: (() => { <Typography>{data.encodeWork?.progress?.timeLeft}</Typography>
//const data = transformEventGroups(item); {(showProgressbar) ?
return ( <ProgressbarWithLabel indeterminateText={"Waiting"} progress={progress} /> : null
<> }
<Typography>{item.encodeWork?.progress?.timeLeft}</Typography> </>
{(showProgressbar) ? );
<ProgressbarWithLabel indeterminateText={"Waiting"} progress={progress} /> : null
}
</>
);
})()
};
} }
const columns: Array<TablePropetyConfig> = [ const columns: Array<TablePropetyConfig> = [
@ -279,10 +294,10 @@ export default function EventsPage() {
return ( return (
<> <>
<ExpandableTable items={events.items} columns={columns} cellCustomizer={createCellTable} expandableRender={renderExpandableItem} defaultSort={{ <ExpandableTable items={tableItems} columns={columns} cellCustomizer={createCellTable} expandableRender={renderExpandableItem} defaultSort={{
order: 'desc', order: 'desc',
accessor: "created" accessor: "created"
}} /> }} sorter={sorter} />
</> </>
) )
} }

View File

@ -1,102 +0,0 @@
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useStompClient } from "react-stomp-hooks";
import { Box, Button, Grid, TextField, Typography, useTheme } from '@mui/material';
import { ExplorerItem } from "../../types";
import { ContextMenuItem } from "../features/ContextMenu";
import { RootState } from "../store";
import { useWsSubscription } from "../ws/subscriptions";
import SimpleTable from "../features/table/sortableTable";
import { TableCellCustomizer, TablePropetyConfig } from "../features/table/table";
import MultiListSortedTable from "../features/table/multiListSortedTable";
import { TableTaskGroup, Task, TaskGroup, update } from "../store/tasks-slice";
import SortableGroupedTable from "../features/table/sortableGroupedTable";
import { UnixTimestamp } from "../features/UxTc";
const columns: Array<TablePropetyConfig> = [
{ label: "Name", accessor: "data.inputFile" },
{ label: "Task", accessor: "task" },
{ label: "Status", accessor: "status" },
{ label: "Created", accessor: "created" },
];
const createTableCell: TableCellCustomizer<Task> = (accessor, data) => {
switch (accessor) {
case "created": {
if (typeof data[accessor] === "string") {
return UnixTimestamp({ timestamp: Date.parse(data[accessor]) });
}
return null;
}
case "data.inputFile": {
const parts = data.data?.inputFile.split("/") ?? [];
return <Typography>{parts[parts?.length - 1]}</Typography>
}
default:
return null;
}
};
export default function ProcesserTasksPage() {
const muiTheme = useTheme();
const dispatch = useDispatch();
const client = useStompClient();
const taskGroups = useSelector((state: RootState) => state.tasks);
useWsSubscription<Array<TaskGroup>>("/topic/tasks/all", (response) => {
console.log(response)
dispatch(update(response))
});
useWsSubscription<any>("/topic/processer/encode/progress", (response) => {
console.log(response)
});
useEffect(() => {
client?.publish({
destination: "/app/tasks/all"
});
}, [client, dispatch]);
return (
<>
<Box display="block">
<Grid container sx={{
height: 50,
width: "100%",
maxHeight: "100%",
overflow: "hidden",
display: "flex",
alignItems: "center",
backgroundColor: muiTheme.palette.background.paper
}}>
<Grid item xs={2}>
<Typography variant="h6">Tasks</Typography>
</Grid>
<Grid item xs={10}>
<TextField
hiddenLabel
placeholder="Search"
fullWidth={true}
id="search-field"
variant="filled"
/>
</Grid>
</Grid>
<Box sx={{
display: "block",
height: "calc(100% - 120px)",
overflow: "hidden",
position: "absolute",
width: "100%"
}}>
<SortableGroupedTable items={taskGroups.items ?? []} columns={columns} customizer={createTableCell} />
</Box>
</Box>
</>
)
}

View File

@ -1,10 +1,9 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit" import { createSlice, PayloadAction } from "@reduxjs/toolkit"
import { ExpandableTableItem } from "../features/table/expandableTable";
export interface EventGroup extends ExpandableTableItem { export interface EventGroup {
referenceId: string, referenceId: string,
created: number, created: number,
fileName: string|null, fileName?: string,
events: EventChain[] events: EventChain[]
} }

View File

@ -66,6 +66,7 @@ export interface TaskGroupList {
} }
export interface TableTaskGroup extends TableItemGroup<Task> { export interface TableTaskGroup extends TableItemGroup<Task> {
referenceId: string
title: string title: string
items: Array<Task> items: Array<Task>
} }
@ -84,6 +85,7 @@ const tasksSlice = createSlice({
reducers: { reducers: {
update(state, action: PayloadAction<Array<TaskGroup>>) { update(state, action: PayloadAction<Array<TaskGroup>>) {
state.items = action.payload.map((value) => ({ state.items = action.payload.map((value) => ({
referenceId: value.referenceId,
title: value.referenceId, title: value.referenceId,
items: value.tasks items: value.tasks
})) ?? [] })) ?? []

View File

@ -1,5 +1,4 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit" import { createSlice, PayloadAction } from "@reduxjs/toolkit"
import { ExpandableTableItem } from "../features/table/expandableTable"
export enum WorkStatus { export enum WorkStatus {
Pending = "Pending", Pending = "Pending",
@ -38,7 +37,7 @@ export enum Status {
export interface ContentEventState extends ExpandableTableItem { export interface ContentEventState {
referenceId: string referenceId: string
title: string title: string
encode: Status encode: Status

View File

@ -29,8 +29,8 @@ root.render(
<StompSessionProvider url={wsUrl()} connectHeaders={{}} logRawCommunication={true} <StompSessionProvider url={wsUrl()} connectHeaders={{}} logRawCommunication={true}
debug={(str) => { debug={(str) => {
if (str === "Opening Web Socket...") { if (str === "Opening Web Socket...") {
console.log("Connecting with Web Socket...")
} }
console.log(str);
}} }}
onUnhandledMessage={(val) => { onUnhandledMessage={(val) => {
console.log("Unhandled message", val) console.log("Unhandled message", val)