diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index f1caa9f6..d8da7f76 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -36,6 +36,7 @@
"react-draggable": "^4.4.6",
"react-pro-sidebar": "^1.1.0",
"react-router-dom": "^7.3.0",
+ "react-virtualized": "^9.22.6",
"react-virtualized-auto-sizer": "^1.0.26",
"react-window": "^1.8.11",
"recharts": "^2.15.1",
@@ -3207,6 +3208,12 @@
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"license": "MIT"
},
+ "node_modules/react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==",
+ "license": "MIT"
+ },
"node_modules/react-number-format": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.3.tgz",
@@ -3390,6 +3397,24 @@
"react-dom": ">=16.6.0"
}
},
+ "node_modules/react-virtualized": {
+ "version": "9.22.6",
+ "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.6.tgz",
+ "integrity": "sha512-U5j7KuUQt3AaMatlMJ0UJddqSiX+Km0YJxSqbAzIiGw5EmNz0khMyqP2hzgu4+QUtm+QPIrxzUX4raJxmVJnHg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.7.2",
+ "clsx": "^1.0.4",
+ "dom-helpers": "^5.1.3",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.7.2",
+ "react-lifecycles-compat": "^3.0.4"
+ },
+ "peerDependencies": {
+ "react": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react-virtualized-auto-sizer": {
"version": "1.0.26",
"resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.26.tgz",
@@ -3400,6 +3425,15 @@
"react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
+ "node_modules/react-virtualized/node_modules/clsx": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/react-window": {
"version": "1.8.11",
"resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.11.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 7af7ff89..3b287d79 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -38,6 +38,7 @@
"react-draggable": "^4.4.6",
"react-pro-sidebar": "^1.1.0",
"react-router-dom": "^7.3.0",
+ "react-virtualized": "^9.22.6",
"react-virtualized-auto-sizer": "^1.0.26",
"react-window": "^1.8.11",
"recharts": "^2.15.1",
diff --git a/frontend/src/components/tables/ChannelsTable.jsx b/frontend/src/components/tables/ChannelsTable.jsx
index e819f13a..f12d555b 100644
--- a/frontend/src/components/tables/ChannelsTable.jsx
+++ b/frontend/src/components/tables/ChannelsTable.jsx
@@ -523,7 +523,7 @@ const ChannelsTable = ({}) => {
header: () => #,
cell: ({ getValue }) => (
- {getValue()}
+ {getValue()}
),
},
@@ -545,7 +545,7 @@ const ChannelsTable = ({}) => {
{
id: 'channel_group',
accessorFn: (row) =>
- row.channel_group_id && channelGroups
+ channelGroups[row.channel_group_id]
? channelGroups[row.channel_group_id].name
: '',
cell: ({ getValue }) => (
@@ -556,7 +556,7 @@ const ChannelsTable = ({}) => {
textOverflow: 'ellipsis',
}}
>
- {getValue()}
+ {getValue()}
),
},
@@ -596,7 +596,7 @@ const ChannelsTable = ({}) => {
),
},
],
- [selectedProfileId, data, channelGroups]
+ [selectedProfileId]
);
const renderHeaderCell = (header) => {
@@ -685,7 +685,9 @@ const ChannelsTable = ({}) => {
manualFiltering: true,
enableRowSelection: true,
onRowSelectionChange: onRowSelectionChange,
- getCoreRowModel: getCoreRowModel(),
+ getExpandedRowHeight: (row) => {
+ return 20 + 28 * row.original.streams.length;
+ },
expandedRowRenderer: ({ row }) => {
return (
{
className="divTable table-striped"
style={{
width: '100%',
+ height: '100%',
display: 'flex',
flexDirection: 'column',
}}
@@ -31,6 +32,7 @@ const CustomTable = ({ table }) => {
expandedRowIds={table.expandedRowIds}
expandedRowRenderer={table.expandedRowRenderer}
renderBodyCell={table.renderBodyCell}
+ getExpandedRowHeight={table.getExpandedRowHeight}
/>
);
diff --git a/frontend/src/components/tables/CustomTable/CustomTableBody.jsx b/frontend/src/components/tables/CustomTable/CustomTableBody.jsx
index ad523a93..ac7000d3 100644
--- a/frontend/src/components/tables/CustomTable/CustomTableBody.jsx
+++ b/frontend/src/components/tables/CustomTable/CustomTableBody.jsx
@@ -1,11 +1,13 @@
import { Box, Flex } from '@mantine/core';
-import { flexRender } from '@tanstack/react-table';
+import { VariableSizeList as List } from 'react-window';
+import AutoSizer from 'react-virtualized-auto-sizer';
const CustomTableBody = ({
getRowModel,
expandedRowIds,
expandedRowRenderer,
renderBodyCell,
+ getExpandedRowHeight,
}) => {
const renderExpandedRow = (row) => {
if (expandedRowRenderer) {
@@ -17,46 +19,98 @@ const CustomTableBody = ({
const rows = getRowModel().rows;
- return (
-
- {rows.map((row, index) => (
-
-
- {row.getVisibleCells().map((cell) => {
+ const renderTableBodyContents = () => {
+ const virtualized = false;
+
+ if (virtualized) {
+ return (
+
+
+ {({ height }) => {
+ const getItemSize = (index) => {
+ const row = rows[index];
+ const isExpanded = expandedRowIds.includes(row.original.id);
+ console.log(isExpanded);
+
+ // Default row height
+ let rowHeight = 28;
+
+ if (isExpanded && getExpandedRowHeight) {
+ // If row is expanded, adjust the height to be larger (based on your logic)
+ // You can get this height from your state, or calculate based on number of items in the expanded row
+ rowHeight += getExpandedRowHeight(row); // This function would calculate the expanded row's height
+ }
+
+ return rowHeight;
+ };
+
return (
-
-
- {renderBodyCell({ row, cell })}
-
-
+ {({ index, style }) => {
+ const row = rows[index];
+ return renderTableBodyRow(row, index, style);
+ }}
+
);
- })}
-
- {expandedRowIds.includes(row.original.id) && renderExpandedRow(row)}
+ }}
+
- ))}
-
- );
+ );
+ }
+
+ return (
+
+ {rows.map((row, index) => renderTableBodyRow(row, index))}
+
+ );
+ };
+
+ const renderTableBodyRow = (row, index, style = {}) => {
+ return (
+
+
+ {row.getVisibleCells().map((cell) => {
+ return (
+
+
+ {renderBodyCell({ row, cell })}
+
+
+ );
+ })}
+
+ {expandedRowIds.includes(row.original.id) && renderExpandedRow(row)}
+
+ );
+ };
+
+ return renderTableBodyContents();
};
export default CustomTableBody;
diff --git a/frontend/src/components/tables/CustomTable/index.jsx b/frontend/src/components/tables/CustomTable/index.jsx
index 2e12ea30..7be3a8e7 100644
--- a/frontend/src/components/tables/CustomTable/index.jsx
+++ b/frontend/src/components/tables/CustomTable/index.jsx
@@ -16,6 +16,7 @@ const useTable = ({
bodyCellRenderFns = {},
expandedRowRenderer = () => <>>,
onRowSelectionChange = null,
+ getExpandedRowHeight = null,
...options
}) => {
const [selectedTableIds, setSelectedTableIds] = useState([]);
@@ -145,6 +146,7 @@ const useTable = ({
headerCellRenderFns,
bodyCellRenderFns,
renderBodyCell,
+ getExpandedRowHeight,
};
};
diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx
index 74329880..d9f716db 100644
--- a/frontend/src/pages/Settings.jsx
+++ b/frontend/src/pages/Settings.jsx
@@ -19,6 +19,7 @@ import {
import { isNotEmpty, useForm } from '@mantine/form';
import UserAgentsTable from '../components/tables/UserAgentsTable';
import StreamProfilesTable from '../components/tables/StreamProfilesTable';
+import { useLocalStorage } from '@mantine/hooks';
const SettingsPage = () => {
const settings = useSettingsStore((s) => s.settings);