diff --git a/apps/channels/migrations/0017_alter_channel_channel_number_alter_channelgroup_name.py b/apps/channels/migrations/0017_alter_channelgroup_name.py
similarity index 52%
rename from apps/channels/migrations/0017_alter_channel_channel_number_alter_channelgroup_name.py
rename to apps/channels/migrations/0017_alter_channelgroup_name.py
index 1bb7d2e7..03043d65 100644
--- a/apps/channels/migrations/0017_alter_channel_channel_number_alter_channelgroup_name.py
+++ b/apps/channels/migrations/0017_alter_channelgroup_name.py
@@ -1,4 +1,4 @@
-# Generated by Django 5.1.6 on 2025-04-19 12:08
+# Generated by Django 5.1.6 on 2025-04-21 20:47
from django.db import migrations, models
@@ -10,14 +10,9 @@ class Migration(migrations.Migration):
]
operations = [
- migrations.AlterField(
- model_name='channel',
- name='channel_number',
- field=models.IntegerField(db_index=True),
- ),
migrations.AlterField(
model_name='channelgroup',
name='name',
- field=models.CharField(db_index=True, max_length=100, unique=True),
+ field=models.TextField(db_index=True, unique=True),
),
]
diff --git a/apps/channels/models.py b/apps/channels/models.py
index 249343e9..0b66c468 100644
--- a/apps/channels/models.py
+++ b/apps/channels/models.py
@@ -27,7 +27,7 @@ def get_total_viewers(channel_id):
return 0
class ChannelGroup(models.Model):
- name = models.CharField(max_length=100, unique=True, db_index=True)
+ name = models.TextField(unique=True, db_index=True)
def related_channels(self):
# local import if needed to avoid cyc. Usually fine in a single file though
diff --git a/frontend/src/components/tables/ChannelsTable.jsx b/frontend/src/components/tables/ChannelsTable.jsx
index 1bb276d3..8568b52c 100644
--- a/frontend/src/components/tables/ChannelsTable.jsx
+++ b/frontend/src/components/tables/ChannelsTable.jsx
@@ -58,18 +58,12 @@ import {
UnstyledButton,
CopyButton,
} from '@mantine/core';
-import {
- useReactTable,
- getCoreRowModel,
- getPaginationRowModel,
- getSortedRowModel,
- getFilteredRowModel,
- flexRender,
-} from '@tanstack/react-table';
+import { getCoreRowModel, flexRender } from '@tanstack/react-table';
import './table.css';
import useChannelsTableStore from '../../store/channelsTable';
import ChannelTableStreams from './ChannelTableStreams';
import useLocalStorage from '../../hooks/useLocalStorage';
+import { CustomTable, useTable } from './CustomTable';
const m3uUrlBase = `${window.location.protocol}//${window.location.host}/output/m3u`;
const epgUrlBase = `${window.location.protocol}//${window.location.host}/output/epg`;
@@ -292,6 +286,7 @@ const ChannelsTable = ({}) => {
const env_mode = useSettingsStore((s) => s.environment.env_mode);
+ const [allRowIds, setAllRowIds] = useState([]);
const [channel, setChannel] = useState(null);
const [channelModalOpen, setChannelModalOpen] = useState(false);
const [recordingModalOpen, setRecordingModalOpen] = useState(false);
@@ -299,12 +294,9 @@ const ChannelsTable = ({}) => {
const [selectedProfile, setSelectedProfile] = useState(
profiles[selectedProfileId]
);
+ const pagination = useChannelsTableStore((s) => s.pagination);
+ const setPagination = useChannelsTableStore((s) => s.setPagination);
const [paginationString, setPaginationString] = useState('');
- const [pagination, setPagination] = useState({
- pageIndex: 0,
- pageSize: tablePrefs.pageSize,
- });
- const [initialDataCount, setInitialDataCount] = useState(null);
const [filters, setFilters] = useState({
name: '',
channel_group: '',
@@ -312,10 +304,9 @@ const ChannelsTable = ({}) => {
const debouncedFilters = useDebounce(filters, 500);
const [isLoading, setIsLoading] = useState(true);
const [selectedChannelIds, setSelectedChannelIds] = useState([]);
- const [sorting, setSorting] = useState([
- { id: 'channel_number', desc: false },
- ]);
- const [expandedRowId, setExpandedRowId] = useState(null);
+ const sorting = useChannelsTableStore((s) => s.sorting);
+ const setSorting = useChannelsTableStore((s) => s.setSorting);
+ const [expandedRowIds, setExpandedRowIds] = useState([]);
const [hdhrUrl, setHDHRUrl] = useState(hdhrUrlBase);
const [epgUrl, setEPGUrl] = useState(epgUrlBase);
@@ -339,6 +330,7 @@ const ChannelsTable = ({}) => {
});
const results = await API.queryChannels(params);
+ const ids = await API.getAllChannelIds(params);
const startItem = pagination.pageIndex * pagination.pageSize + 1; // +1 to start from 1, not 0
const endItem = Math.min(
@@ -346,15 +338,12 @@ const ChannelsTable = ({}) => {
results.count
);
- if (initialDataCount === null) {
- setInitialDataCount(results.count);
- }
-
// Generate the string
setPaginationString(`${startItem} to ${endItem} of ${results.count}`);
setTablePrefs({
pageSize: pagination.pageSize,
});
+ setAllRowIds(ids);
}, [pagination, sorting, debouncedFilters]);
useEffect(() => {
@@ -386,10 +375,6 @@ const ChannelsTable = ({}) => {
...prev,
[name]: value,
}));
- setPagination({
- pageIndex: 0,
- pageSize: pagination.pageSize,
- });
}, []);
const handleGroupChange = (value) => {
@@ -397,10 +382,6 @@ const ChannelsTable = ({}) => {
...prev,
channel_group: value ? value : '',
}));
- setPagination({
- pageIndex: 0,
- pageSize: pagination.pageSize,
- });
};
const hdhrUrlRef = useRef(null);
@@ -440,49 +421,49 @@ const ChannelsTable = ({}) => {
showVideo(getChannelURL(channel));
}
- const onRowSelectionChange = (updater) => {
- setRowSelection((prevRowSelection) => {
- const newRowSelection =
- typeof updater === 'function' ? updater(prevRowSelection) : updater;
+ // const onRowSelectionChange = (updater) => {
+ // setRowSelection((prevRowSelection) => {
+ // const newRowSelection =
+ // typeof updater === 'function' ? updater(prevRowSelection) : updater;
- const updatedSelected = new Set([...selectedChannelIds]);
- getRowModel().rows.forEach((row) => {
- if (newRowSelection[row.id] === undefined || !newRowSelection[row.id]) {
- updatedSelected.delete(row.original.id);
- } else {
- updatedSelected.add(row.original.id);
- }
- });
- const newSelection = [...updatedSelected];
- setSelectedChannelIds(newSelection);
- setSelectedTableIds(newSelection);
+ // const updatedSelected = new Set([...selectedChannelIds]);
+ // getRowModel().rows.forEach((row) => {
+ // if (newRowSelection[row.id] === undefined || !newRowSelection[row.id]) {
+ // updatedSelected.delete(row.original.id);
+ // } else {
+ // updatedSelected.add(row.original.id);
+ // }
+ // });
+ // const newSelection = [...updatedSelected];
+ // setSelectedChannelIds(newSelection);
+ // setSelectedTableIds(newSelection);
- return newRowSelection;
- });
- };
+ // return newRowSelection;
+ // });
+ // };
- const onSelectAllChange = async (e) => {
- const selectAll = e.target.checked;
- if (selectAll) {
- // Get all channel IDs for current view
- const params = new URLSearchParams();
- Object.entries(debouncedFilters).forEach(([key, value]) => {
- if (value) params.append(key, value);
- });
- const ids = await API.getAllChannelIds(params);
- setSelectedTableIds(ids);
- setSelectedChannelIds(ids);
- } else {
- setSelectedTableIds([]);
- setSelectedChannelIds([]);
- }
+ // const onSelectAllChange = async (e) => {
+ // const selectAll = e.target.checked;
+ // if (selectAll) {
+ // // Get all channel IDs for current view
+ // const params = new URLSearchParams();
+ // Object.entries(debouncedFilters).forEach(([key, value]) => {
+ // if (value) params.append(key, value);
+ // });
+ // const ids = await API.getAllChannelIds(params);
+ // setSelectedTableIds(ids);
+ // setSelectedChannelIds(ids);
+ // } else {
+ // setSelectedTableIds([]);
+ // setSelectedChannelIds([]);
+ // }
- const newSelection = {};
- getRowModel().rows.forEach((item, index) => {
- newSelection[index] = selectAll;
- });
- setRowSelection(newSelection);
- };
+ // const newSelection = {};
+ // getRowModel().rows.forEach((item, index) => {
+ // newSelection[index] = selectAll;
+ // });
+ // setRowSelection(newSelection);
+ // };
const onPageSizeChange = (e) => {
setPagination({
@@ -798,50 +779,9 @@ const ChannelsTable = ({}) => {
enableSorting: false,
},
],
- [selectedProfileId, data]
+ [selectedProfileId, data, channelGroups]
);
- const { getHeaderGroups, getRowModel } = useReactTable({
- data,
- columns: columns,
- defaultColumn: {
- size: undefined,
- minSize: 0,
- },
- pageCount,
- state: {
- data,
- rowCount,
- sorting,
- filters,
- pagination,
- rowSelection,
- },
- manualPagination: true,
- manualSorting: true,
- manualFiltering: true,
- enableRowSelection: true,
- onRowSelectionChange: onRowSelectionChange,
- getCoreRowModel: getCoreRowModel(),
- getFilteredRowModel: getFilteredRowModel(),
- getSortedRowModel: getSortedRowModel(),
- getPaginationRowModel: getPaginationRowModel(),
- // debugTable: true,
- });
-
- const rows = getRowModel().rows;
-
- const onRowExpansion = (row) => {
- let isExpanded = false;
- setExpandedRowId((prev) => {
- isExpanded = prev === row.original.id ? null : row.original.id;
- return isExpanded;
- });
- setRowSelection({ [row.index]: true });
- setSelectedChannelIds([row.original.id]);
- setSelectedTableIds([row.original.id]);
- };
-
const renderHeaderCell = (header) => {
let sortingIcon = ArrowUpDown;
if (sorting[0]?.id == header.id) {
@@ -853,11 +793,6 @@ const ChannelsTable = ({}) => {
}
switch (header.id) {
- case 'select':
- return ChannelRowSelectHeader({
- selectedChannelIds,
- });
-
case 'enabled':
if (selectedProfileId !== '0' && selectedChannelIds.length > 0) {
// return EnabledHeaderSwitch();
@@ -923,22 +858,85 @@ const ChannelsTable = ({}) => {
}
};
- const renderBodyCell = (cell) => {
- switch (cell.column.id) {
- case 'select':
- return ChannelRowSelectCell({ row: cell.row });
+ const table = useTable({
+ data,
+ columns,
+ allRowIds,
+ defaultColumn: {
+ size: undefined,
+ minSize: 0,
+ },
+ pageCount,
+ // state: {
+ // data,
+ // rowCount,
+ // sorting,
+ // filters,
+ // pagination,
+ // rowSelection,
+ // },
+ filters,
+ pagination,
+ sorting,
+ expandedRowIds,
+ manualPagination: true,
+ manualSorting: true,
+ manualFiltering: true,
+ enableRowSelection: true,
+ // onRowSelectionChange: onRowSelectionChange,
+ getCoreRowModel: getCoreRowModel(),
+ // getFilteredRowModel: getFilteredRowModel(),
+ // getSortedRowModel: getSortedRowModel(),
+ // getPaginationRowModel: getPaginationRowModel(),
+ // debugTable: true,
+ expandedRowRenderer: ({ row }) => {
+ return (
+
+
+
+ );
+ },
+ headerCellRenderFns: {
+ name: renderHeaderCell,
+ enabled: () => (
+
+
+
+ ),
+ },
+ });
- case 'expand':
- return ChannelExpandCell({ row: cell.row });
-
- default:
- return flexRender(cell.column.columnDef.cell, cell.getContext());
- }
+ const onRowExpansion = (row) => {
+ let isExpanded = false;
+ setExpandedRowIds((prev) => {
+ isExpanded = prev === row.original.id ? null : row.original.id;
+ return isExpanded;
+ });
+ setRowSelection({ [row.index]: true });
+ setSelectedChannelIds([row.original.id]);
+ setSelectedTableIds([row.original.id]);
};
+ // const renderBodyCell = (cell) => {
+ // switch (cell.column.id) {
+ // case 'select':
+ // return ChannelRowSelectCell({ row: cell.row });
+
+ // case 'expand':
+ // return ChannelExpandCell({ row: cell.row });
+
+ // default:
+ // return flexRender(cell.column.columnDef.cell, cell.getContext());
+ // }
+ // };
+
const ChannelExpandCell = useCallback(
({ row }) => {
- const isExpanded = expandedRowId === row.original.id;
+ const isExpanded = expandedRowIds === row.original.id;
return (
{
);
},
- [expandedRowId]
+ [expandedRowIds]
);
- const ChannelRowSelectCell = useCallback(
- ({ row }) => {
- return (
-
-
-
- );
- },
- [rows]
- );
+ // const ChannelRowSelectCell = useCallback(
+ // ({ row }) => {
+ // return (
+ //
+ //
+ //
+ // );
+ // },
+ // [rows]
+ // );
- const ChannelRowSelectHeader = useCallback(
- ({ selectedChannelIds }) => {
- return (
-
- 0 &&
- selectedChannelIds.length !== rowCount
- }
- onChange={onSelectAllChange}
- />
-
- );
- },
- [rows]
- );
+ // const ChannelRowSelectHeader = useCallback(
+ // ({ selectedChannelIds }) => {
+ // return (
+ //
+ // 0 &&
+ // selectedChannelIds.length !== rowCount
+ // }
+ // onChange={onSelectAllChange}
+ // />
+ //
+ // );
+ // },
+ // [rows]
+ // );
return (
@@ -1218,7 +1216,7 @@ const ChannelsTable = ({}) => {
{/* Table or ghost empty state inside Paper */}
- {initialDataCount === 0 && data.length === 0 && (
+ {Object.keys(channels).length === 0 && (
{
)}
- {data.length > 0 && (
+ {Object.keys(channels).length > 0 && (
{
borderRadius: 'var(--mantine-radius-default)',
}}
>
-
-
- {getHeaderGroups().map((headerGroup) => (
-
- {headerGroup.headers.map((header) => {
- const width = header.getSize();
- return (
-
-
- {renderHeaderCell(header)}
-
-
- );
- })}
-
- ))}
-
-
- {getRowModel().rows.map((row) => (
-
-
- {row.getVisibleCells().map((cell) => {
- const width = cell.column.getSize();
- return (
-
-
- {renderBodyCell(cell)}
-
-
- );
- })}
-
- {row.original.id === expandedRowId && (
-
-
-
- )}
-
- ))}
-
-
+
{
- const [expandedRowId, setExpandedRowId] = useState(null);
-
- const rows = table.getRowModel().rows;
-
- const ChannelExpandCell = useCallback(
- ({ row }) => {
- const isExpanded = expandedRowId === row.original.id;
-
- return (
- {
- setExpandedRowId((prev) =>
- prev === row.original.id ? null : row.original.id
- );
- }}
- >
- {isExpanded ? : }
-
- );
- },
- [expandedRowId]
- );
-
- const ChannelRowSelectCell = useCallback(
- ({ row }) => {
- return (
-
-
-
- );
- },
- [rows]
- );
-
- const bodyCellRenderer = (cell) => {
- if (bodyCellRenderFns[cell.column.id]) {
- return bodyCellRenderFns(cell);
- }
-
- switch (cell.column.id) {
- case 'select':
- return ChannelRowSelectCell({ row: cell.row });
-
- case 'expand':
- return ChannelExpandCell({ row: cell.row });
-
- default:
- return flexRender(cell.column.columnDef.cell, cell.getContext());
- }
- };
-
+const CustomTable = ({ table }) => {
return (
+
-
- {table.getRowModel().rows.map((row) => (
-
-
- {row.getVisibleCells().map((cell) => {
- return (
-
-
- {bodyCellRenderer(cell)}
-
-
- );
- })}
-
- {row.original.id === expandedRowId && (
-
-
-
- )}
-
- ))}
-
);
};
diff --git a/frontend/src/components/tables/CustomTable/CustomTableBody.jsx b/frontend/src/components/tables/CustomTable/CustomTableBody.jsx
new file mode 100644
index 00000000..c2a26f2d
--- /dev/null
+++ b/frontend/src/components/tables/CustomTable/CustomTableBody.jsx
@@ -0,0 +1,65 @@
+import { Box, Flex } from '@mantine/core';
+import { flexRender } from '@tanstack/react-table';
+
+const CustomTableBody = ({
+ getRowModel,
+ bodyCellRenderFns,
+ expandedRowIds,
+ expandedRowRenderer,
+}) => {
+ const renderExpandedRow = (row) => {
+ if (expandedRowRenderer) {
+ return expandedRowRenderer({ row });
+ }
+
+ return <>>;
+ };
+
+ return (
+
+ {getRowModel().rows.map((row) => (
+
+
+ {row.getVisibleCells().map((cell) => {
+ return (
+
+
+ {bodyCellRenderFns[cell.column.id]
+ ? bodyCellRenderFns[cell.column.id](cell)
+ : flexRender(
+ cell.column.columnDef.cell,
+ cell.getContext()
+ )}
+
+
+ );
+ })}
+
+ {expandedRowIds.includes(row.original.id) && renderExpandedRow(row)}
+
+ ))}
+
+ );
+};
+
+export default CustomTableBody;
diff --git a/frontend/src/components/tables/CustomTable/CustomTableHeader.jsx b/frontend/src/components/tables/CustomTable/CustomTableHeader.jsx
index 50a173d2..7f71e04d 100644
--- a/frontend/src/components/tables/CustomTable/CustomTableHeader.jsx
+++ b/frontend/src/components/tables/CustomTable/CustomTableHeader.jsx
@@ -1,120 +1,39 @@
-import { Box, Flex } from '@mantine/core';
-import {
- ArrowDownWideNarrow,
- ArrowUpDown,
- ArrowUpNarrowWide,
-} from 'lucide-react';
+import { Box, Center, Checkbox, Flex } from '@mantine/core';
+import { flexRender } from '@tanstack/react-table';
import { useCallback } from 'react';
const CustomTableHeader = ({
- table,
+ getHeaderGroups,
+ allRowIds,
+ selectedTableIds,
headerCellRenderFns,
- rowCount,
onSelectAllChange,
}) => {
- const ChannelRowSelectHeader = useCallback(
- ({ selectedChannelIds }) => {
- return (
-
- 0 &&
- selectedChannelIds.length !== rowCount
- }
- onChange={onSelectAllChange}
- />
-
- );
- },
- [rows, rowCount]
- );
-
- const onSelectAll = (e) => {
- if (onSelectAllChange) {
- onSelectAllChange(e);
- }
- };
-
- const headerCellRenderer = (header) => {
- let sortingIcon = ArrowUpDown;
- if (sorting[0]?.id == header.id) {
- if (sorting[0].desc === false) {
- sortingIcon = ArrowUpNarrowWide;
- } else {
- sortingIcon = ArrowDownWideNarrow;
- }
+ const renderHeaderCell = (header) => {
+ if (headerCellRenderFns[header.id]) {
+ return headerCellRenderFns[header.id](header);
}
switch (header.id) {
case 'select':
- return ChannelRowSelectHeader({
- selectedChannelIds,
- });
-
- case 'enabled':
- if (selectedProfileId !== '0' && selectedChannelIds.length > 0) {
- // return EnabledHeaderSwitch();
- }
return (
-
+ 0 &&
+ selectedTableIds.length !== allRowIds.length
+ }
+ onChange={onSelectAllChange}
+ />
);
- // case 'channel_number':
- // return (
- //
- // #
- // {/*
- // {React.createElement(sortingIcon, {
- // onClick: () => onSortingChange('name'),
- // size: 14,
- // })}
- // */}
- //
- // );
-
- // case 'name':
- // return (
- //
- // e.stopPropagation()}
- // onChange={handleFilterChange}
- // size="xs"
- // variant="unstyled"
- // className="table-input-header"
- // />
- //
- // {React.createElement(sortingIcon, {
- // onClick: () => onSortingChange('name'),
- // size: 14,
- // })}
- //
- //
- // );
-
- // case 'channel_group':
- // return (
- //
- // );
-
default:
return flexRender(header.column.columnDef.header, header.getContext());
}
@@ -130,7 +49,7 @@ const CustomTableHeader = ({
zIndex: 10,
}}
>
- {table.getHeaderGroups().map((headerGroup) => (
+ {getHeaderGroups().map((headerGroup) => (
- {headerCellRenderer(header)}
+ {renderHeaderCell(header)}
);
diff --git a/frontend/src/components/tables/CustomTable/index.jsx b/frontend/src/components/tables/CustomTable/index.jsx
new file mode 100644
index 00000000..514938cd
--- /dev/null
+++ b/frontend/src/components/tables/CustomTable/index.jsx
@@ -0,0 +1,202 @@
+import { Center, Checkbox } from '@mantine/core';
+import CustomTable from './CustomTable';
+import CustomTableHeader from './CustomTableHeader';
+
+import {
+ useReactTable,
+ getCoreRowModel,
+ flexRender,
+} from '@tanstack/react-table';
+import { useCallback, useMemo, useState } from 'react';
+import { ChevronDown, ChevronRight } from 'lucide-react';
+
+const useTable = ({
+ allRowIds,
+ headerCellRenderFns = {},
+ filters = {},
+ pagination = {},
+ sorting = [],
+ expandedRowRenderer = () => <>>,
+ ...options
+}) => {
+ const [selectedTableIds, setSelectedTableIds] = useState([]);
+ const [expandedRowIds, setExpandedRowIds] = useState([]);
+
+ const rowCount = allRowIds.length;
+
+ const onRowSelectionChange = (updater) => {
+ const newRowSelection =
+ typeof updater === 'function' ? updater(rowSelection) : updater;
+
+ const updatedSelected = new Set(selectedTableIds);
+
+ const allChangedRowIds = new Set([
+ ...Object.keys(rowSelection),
+ ...Object.keys(newRowSelection),
+ ]);
+
+ for (const rowId of allChangedRowIds) {
+ const wasSelected = !!rowSelection[rowId];
+ const isSelected = !!newRowSelection[rowId];
+
+ if (wasSelected !== isSelected) {
+ const row = table.getRow(rowId);
+ if (!row) continue;
+
+ const originalId = row.original.id;
+ if (isSelected) {
+ updatedSelected.add(originalId);
+ } else {
+ updatedSelected.delete(originalId);
+ }
+ }
+ }
+
+ setSelectedTableIds([...updatedSelected]);
+ };
+
+ const table = useReactTable({
+ ...options,
+ state: {
+ data: options.data,
+ selectedTableIds,
+ },
+ onRowSelectionChange,
+ getCoreRowModel: options.getCoreRowModel ?? getCoreRowModel(),
+ });
+
+ const selectedTableIdsSet = useMemo(
+ () => new Set(selectedTableIds),
+ [selectedTableIds]
+ );
+
+ const rowSelection = useMemo(() => {
+ const selection = {};
+ table.getRowModel().rows.forEach((row) => {
+ if (selectedTableIdsSet.has(row.original.id)) {
+ selection[row.id] = true;
+ }
+ });
+ return selection;
+ }, [selectedTableIdsSet, table.getRowModel().rows]);
+
+ const onSelectAllChange = async (e) => {
+ const selectAll = e.target.checked;
+ if (selectAll) {
+ setSelectedTableIds(allRowIds);
+ } else {
+ setSelectedTableIds([]);
+ }
+ };
+
+ const rows = table.getRowModel().rows;
+
+ const onRowExpansion = (row) => {
+ let isExpanded = false;
+ setExpandedRowIds((prev) => {
+ isExpanded = prev.includes(row.original.id) ? [] : [row.original.id];
+ return isExpanded;
+ });
+ setSelectedTableIds([row.original.id]);
+ };
+
+ const renderHeaderCell = useCallback(
+ (header) => {
+ if (table.headerCellRenderFns && table.headerCellRenderFns[header.id]) {
+ return table.headerCellRenderFns[header.id](header);
+ }
+
+ switch (header.id) {
+ case 'select':
+ return (
+
+ 0 &&
+ selectedTableIds.length !== rowCount
+ }
+ onChange={onSelectAllChange}
+ />
+
+ );
+
+ default:
+ return flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ );
+ }
+ },
+ [filters, selectedTableIds, rowCount, onSelectAllChange, sorting]
+ );
+
+ const bodyCellRenderFns = {
+ select: useCallback(
+ ({ row }) => {
+ return (
+
+ {
+ const newSet = new Set(selectedTableIds);
+ if (e.target.checked) {
+ newSet.add(row.original.id);
+ } else {
+ newSet.delete(row.original.id);
+ }
+ setSelectedTableIds([...newSet]);
+ }}
+ />
+
+ );
+ },
+ [rows, selectedTableIdsSet]
+ ),
+ expand: useCallback(({ row }) => {
+ const isExpanded = expandedRowIds.includes(row.original.id);
+
+ return (
+ {
+ onRowExpansion(row);
+ }}
+ >
+ {isExpanded ? : }
+
+ );
+ }),
+ };
+
+ // Return both the table instance and your custom methods
+ const tableInstance = useMemo(
+ () => ({
+ ...table,
+ ...options,
+ sorting,
+ selectedTableIds,
+ setSelectedTableIds,
+ rowSelection,
+ allRowIds,
+ onSelectAllChange,
+ selectedTableIdsSet,
+ expandedRowIds,
+ expandedRowRenderer,
+ }),
+ [selectedTableIdsSet, expandedRowIds]
+ );
+
+ return {
+ ...tableInstance,
+ headerCellRenderFns,
+ renderHeaderCell,
+ bodyCellRenderFns,
+ };
+};
+
+export { useTable, CustomTable, CustomTableHeader };
diff --git a/frontend/src/components/tables/table.css b/frontend/src/components/tables/table.css
index c1c43f20..044c5c87 100644
--- a/frontend/src/components/tables/table.css
+++ b/frontend/src/components/tables/table.css
@@ -41,7 +41,7 @@
}
.td {
- height: 21px;
+ height: 28px;
border-bottom: solid 1px rgb(68,68,68);
}
diff --git a/frontend/src/store/channelsTable b/frontend/src/store/channelsTable.jsx
similarity index 72%
rename from frontend/src/store/channelsTable
rename to frontend/src/store/channelsTable.jsx
index 2a230e84..76941e4f 100644
--- a/frontend/src/store/channelsTable
+++ b/frontend/src/store/channelsTable.jsx
@@ -6,7 +6,11 @@ import API from '../api';
const useChannelsTableStore = create((set, get) => ({
channels: [],
count: 0,
- pageCount: 0,
+ sorting: [{ id: 'channel_number', desc: false }],
+ pagination: {
+ pageIndex: 0,
+ pageCount: 50,
+ },
selectedChannelIds: [],
queryChannels: ({ results, count }, params) => {
@@ -29,6 +33,18 @@ const useChannelsTableStore = create((set, get) => ({
const channel = get().channels.find((c) => c.id === id);
return channel?.streams ?? [];
},
+
+ setPagination: (pagination) => {
+ set((state) => ({
+ pagination,
+ }));
+ },
+
+ setSorting: (sorting) => {
+ set((state) => ({
+ sorting,
+ }));
+ },
}));
export default useChannelsTableStore;