mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-29 04:50:20 +00:00
merged in updated ui changes
This commit is contained in:
commit
656a9c06e8
6 changed files with 629 additions and 67 deletions
|
|
@ -98,14 +98,14 @@ const App = () => {
|
|||
toggleDrawer={toggleDrawer}
|
||||
/>
|
||||
|
||||
{/* Main content area, no AppBar, so no marginTop */}
|
||||
{/* Main content area */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
ml: `${open ? drawerWidth : miniDrawerWidth}px`,
|
||||
transition: 'margin-left 0.3s',
|
||||
backgroundColor: '#18181b',
|
||||
backgroundColor: 'background.default',
|
||||
minHeight: '100vh',
|
||||
color: 'text.primary',
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
ListItemIcon,
|
||||
ListItemText,
|
||||
} from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
ListOrdered,
|
||||
Play,
|
||||
|
|
@ -25,17 +26,14 @@ const navItems = [
|
|||
{ label: 'Channels', icon: <ListOrdered size={20} />, path: '/channels' },
|
||||
{ label: 'M3U', icon: <Play size={20} />, path: '/m3u' },
|
||||
{ label: 'EPG', icon: <Database size={20} />, path: '/epg' },
|
||||
{
|
||||
label: 'Stream Profiles',
|
||||
icon: <SlidersHorizontal size={20} />,
|
||||
path: '/stream-profiles',
|
||||
},
|
||||
{ label: 'Stream Profiles', icon: <SlidersHorizontal size={20} />, path: '/stream-profiles' },
|
||||
{ label: 'TV Guide', icon: <LayoutGrid size={20} />, path: '/guide' },
|
||||
{ label: 'Settings', icon: <LucideSettings size={20} />, path: '/settings' },
|
||||
];
|
||||
|
||||
const Sidebar = ({ open, drawerWidth, miniDrawerWidth, toggleDrawer }) => {
|
||||
const location = useLocation();
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
|
|
@ -45,7 +43,7 @@ const Sidebar = ({ open, drawerWidth, miniDrawerWidth, toggleDrawer }) => {
|
|||
width: open ? drawerWidth : miniDrawerWidth,
|
||||
overflowX: 'hidden',
|
||||
transition: 'width 0.3s',
|
||||
backgroundColor: '#18181b',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: 'text.primary',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
|
|
@ -118,16 +116,18 @@ const Sidebar = ({ open, drawerWidth, miniDrawerWidth, toggleDrawer }) => {
|
|||
borderRadius: 1,
|
||||
width: open ? '208px' : 'auto',
|
||||
transition: 'all 0.2s ease',
|
||||
bgcolor: isActive ? 'rgba(21, 69, 62, 0.67)' : 'transparent',
|
||||
bgcolor: isActive
|
||||
? theme.custom.sidebar.activeBackground
|
||||
: 'transparent',
|
||||
border: isActive
|
||||
? '1px solid #14917e'
|
||||
? `1px solid ${theme.custom.sidebar.activeBorder}`
|
||||
: '1px solid transparent',
|
||||
color: 'text.primary',
|
||||
px: 1,
|
||||
py: 0.25,
|
||||
'&:hover': {
|
||||
bgcolor: '#27272a',
|
||||
border: '1px solid #3f3f46',
|
||||
bgcolor: theme.custom.sidebar.hoverBackground,
|
||||
border: `1px solid ${theme.custom.sidebar.hoverBorder}`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
|
@ -148,9 +148,10 @@ const Sidebar = ({ open, drawerWidth, miniDrawerWidth, toggleDrawer }) => {
|
|||
sx: {
|
||||
fontSize: '14px',
|
||||
fontWeight: 400,
|
||||
color: isActive ? '##d4d4d8' : '##d4d4d8',
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontFamily: theme.custom.sidebar.fontFamily,
|
||||
letterSpacing: '-0.3px',
|
||||
// Keeping the text color as it is in your original
|
||||
color: isActive ? '#d4d4d8' : '#d4d4d8',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
TextField,
|
||||
Autocomplete,
|
||||
InputAdornment,
|
||||
Paper,
|
||||
} from '@mui/material';
|
||||
import useChannelsStore from '../../store/channels';
|
||||
import {
|
||||
|
|
@ -28,6 +29,11 @@ import {
|
|||
ContentCopy,
|
||||
Tv as TvIcon,
|
||||
Clear as ClearIcon,
|
||||
IndeterminateCheckBox,
|
||||
CompareArrows,
|
||||
Code,
|
||||
AddBox,
|
||||
Hd as HdIcon,
|
||||
} from '@mui/icons-material';
|
||||
import API from '../../api';
|
||||
import ChannelForm from '../forms/Channel';
|
||||
|
|
@ -37,6 +43,9 @@ import logo from '../../images/logo.png';
|
|||
import useVideoStore from '../../store/useVideoStore';
|
||||
import useSettingsStore from '../../store/settings';
|
||||
import usePlaylistsStore from '../../store/playlists';
|
||||
import { Tv2, ScreenShare, Scroll } from 'lucide-react';
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import ghostImage from '../../images/ghost.svg';
|
||||
|
||||
const ChannelStreams = ({ channel, isExpanded }) => {
|
||||
const channelStreams = useChannelsStore(
|
||||
|
|
@ -45,12 +54,10 @@ const ChannelStreams = ({ channel, isExpanded }) => {
|
|||
const { playlists } = usePlaylistsStore();
|
||||
|
||||
const removeStream = async (stream) => {
|
||||
let streamSet = new Set(channelStreams);
|
||||
streamSet.delete(stream);
|
||||
streamSet = Array.from(streamSet);
|
||||
const newStreamList = channelStreams.filter((s) => s.id !== stream.id);
|
||||
await API.updateChannel({
|
||||
...channel,
|
||||
stream_ids: streamSet.map((stream) => stream.id),
|
||||
stream_ids: newStreamList.map((s) => s.id),
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -109,11 +116,11 @@ const ChannelStreams = ({ channel, isExpanded }) => {
|
|||
renderRowActions: ({ row }) => (
|
||||
<>
|
||||
<IconButton
|
||||
size="small" // Makes the button smaller
|
||||
color="error" // Red color for delete actions
|
||||
size="small"
|
||||
color="error"
|
||||
onClick={() => removeStream(row.original)}
|
||||
>
|
||||
<DeleteIcon fontSize="small" /> {/* Small icon size */}
|
||||
<DeleteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</>
|
||||
),
|
||||
|
|
@ -137,6 +144,63 @@ const ChannelStreams = ({ channel, isExpanded }) => {
|
|||
);
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------
|
||||
2) Custom-styled "chip" buttons for HDHR, M3U, EPG
|
||||
------------------------------------------------------------ */
|
||||
const HDHRButton = styled(Button)(() => ({
|
||||
border: '1px solid #a3d977',
|
||||
color: '#a3d977',
|
||||
backgroundColor: 'transparent',
|
||||
textTransform: 'none',
|
||||
fontSize: '0.85rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
padding: '2px 8px',
|
||||
minWidth: 'auto',
|
||||
'&:hover': {
|
||||
borderColor: '#c2e583',
|
||||
color: '#c2e583',
|
||||
backgroundColor: 'rgba(163,217,119,0.1)',
|
||||
},
|
||||
}));
|
||||
|
||||
const M3UButton = styled(Button)(() => ({
|
||||
border: '1px solid #5f6dc6',
|
||||
color: '#5f6dc6',
|
||||
backgroundColor: 'transparent',
|
||||
textTransform: 'none',
|
||||
fontSize: '0.85rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
padding: '2px 8px',
|
||||
minWidth: 'auto',
|
||||
'&:hover': {
|
||||
borderColor: '#7f8de6',
|
||||
color: '#7f8de6',
|
||||
backgroundColor: 'rgba(95,109,198,0.1)',
|
||||
},
|
||||
}));
|
||||
|
||||
const EPGButton = styled(Button)(() => ({
|
||||
border: '1px solid #707070',
|
||||
color: '#a0a0a0',
|
||||
backgroundColor: 'transparent',
|
||||
textTransform: 'none',
|
||||
fontSize: '0.85rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
padding: '2px 8px',
|
||||
minWidth: 'auto',
|
||||
'&:hover': {
|
||||
borderColor: '#a0a0a0',
|
||||
color: '#c0c0c0',
|
||||
backgroundColor: 'rgba(112,112,112,0.1)',
|
||||
},
|
||||
}));
|
||||
|
||||
const ChannelsTable = ({}) => {
|
||||
const [channel, setChannel] = useState(null);
|
||||
const [channelModalOpen, setChannelModalOpen] = useState(false);
|
||||
|
|
@ -150,6 +214,8 @@ const ChannelsTable = ({}) => {
|
|||
|
||||
const [filterValues, setFilterValues] = useState({});
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const { showVideo } = useVideoStore();
|
||||
const {
|
||||
channels,
|
||||
|
|
@ -193,7 +259,7 @@ const ChannelsTable = ({}) => {
|
|||
header: 'Name',
|
||||
accessorKey: 'channel_name',
|
||||
muiTableHeadCellProps: {
|
||||
sx: { textAlign: 'center' }, // Center-align the header
|
||||
sx: { textAlign: 'center' },
|
||||
},
|
||||
Header: ({ column }) => (
|
||||
<TextField
|
||||
|
|
@ -206,8 +272,8 @@ const ChannelsTable = ({}) => {
|
|||
fullWidth
|
||||
sx={
|
||||
{
|
||||
// '& .MuiInputBase-root': { fontSize: '0.875rem' }, // Text size
|
||||
// '& .MuiInputLabel-root': { fontSize: '0.75rem' }, // Label size
|
||||
// '& .MuiInputBase-root': { fontSize: '0.875rem' },
|
||||
// '& .MuiInputLabel-root': { fontSize: '0.75rem' },
|
||||
// width: '200px', // Optional: Adjust width
|
||||
}
|
||||
}
|
||||
|
|
@ -216,7 +282,7 @@ const ChannelsTable = ({}) => {
|
|||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
onClick={() => handleFilterChange(column.id, '')} // Clear text on click
|
||||
onClick={() => handleFilterChange(column.id, '')}
|
||||
edge="end"
|
||||
size="small"
|
||||
>
|
||||
|
|
@ -254,8 +320,8 @@ const ChannelsTable = ({}) => {
|
|||
onClick={(e) => e.stopPropagation()}
|
||||
sx={{
|
||||
pb: 0.8,
|
||||
// '& .MuiInputBase-root': { fontSize: '0.875rem' }, // Text size
|
||||
// '& .MuiInputLabel-root': { fontSize: '0.75rem' }, // Label size
|
||||
// '& .MuiInputBase-root': { fontSize: '0.875rem' },
|
||||
// '& .MuiInputLabel-root': { fontSize: '0.75rem' },
|
||||
// width: '200px', // Optional: Adjust width
|
||||
}}
|
||||
/>
|
||||
|
|
@ -475,7 +541,7 @@ const ChannelsTable = ({}) => {
|
|||
sorting,
|
||||
rowSelection,
|
||||
},
|
||||
rowVirtualizerInstanceRef, // optional
|
||||
rowVirtualizerInstanceRef,
|
||||
rowVirtualizerOptions: { overscan: 5 },
|
||||
initialState: {
|
||||
density: 'compact',
|
||||
|
|
@ -484,10 +550,10 @@ const ChannelsTable = ({}) => {
|
|||
enableExpandAll: false,
|
||||
displayColumnDefOptions: {
|
||||
'mrt-row-select': {
|
||||
size: 50, // Set custom width (default is ~40px)
|
||||
size: 50,
|
||||
},
|
||||
'mrt-row-expand': {
|
||||
size: 10, // Set custom width (default is ~40px)
|
||||
size: 10,
|
||||
header: '',
|
||||
muiTableHeadCellProps: {
|
||||
sx: { width: 38, minWidth: 38, maxWidth: 38, height: '100%' },
|
||||
|
|
@ -497,13 +563,13 @@ const ChannelsTable = ({}) => {
|
|||
},
|
||||
},
|
||||
'mrt-row-actions': {
|
||||
size: 68, // Set custom width (default is ~40px)
|
||||
size: 68,
|
||||
},
|
||||
},
|
||||
muiExpandButtonProps: ({ row, table }) => ({
|
||||
onClick: () => {
|
||||
setRowSelection({ [row.index]: true });
|
||||
table.setExpanded({ [row.id]: !row.getIsExpanded() }); //only 1 detail panel open at a time
|
||||
table.setExpanded({ [row.id]: !row.getIsExpanded() });
|
||||
},
|
||||
sx: {
|
||||
transform: row.getIsExpanded() ? 'rotate(180deg)' : 'rotate(-90deg)',
|
||||
|
|
@ -629,42 +695,445 @@ const ChannelsTable = ({}) => {
|
|||
|
||||
return (
|
||||
<Box>
|
||||
<MaterialReactTable table={table} />
|
||||
{/* Header Row: outside the Paper */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', pb: 1 }}>
|
||||
<Typography
|
||||
sx={{
|
||||
width: 88,
|
||||
height: 24,
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontWeight: 500,
|
||||
fontSize: '20px',
|
||||
lineHeight: 1,
|
||||
letterSpacing: '-0.3px',
|
||||
color: theme.palette.text.secondary,
|
||||
mb: 0,
|
||||
}}
|
||||
>
|
||||
Channels
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
width: 43,
|
||||
height: 25,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
ml: 3,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
width: 37,
|
||||
height: 17,
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontWeight: 400,
|
||||
fontSize: '14px',
|
||||
lineHeight: 1,
|
||||
letterSpacing: '-0.3px',
|
||||
color: theme.palette.text.secondary,
|
||||
}}
|
||||
>
|
||||
Links:
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', gap: '6px', ml: 0.75 }}>
|
||||
{/* HDHR Button */}
|
||||
<Button
|
||||
onClick={copyHDHRUrl}
|
||||
sx={{
|
||||
width: '71px',
|
||||
height: '25px',
|
||||
borderRadius: '4px',
|
||||
border: `1px solid ${theme.palette.custom.greenMain}`,
|
||||
backgroundColor: 'transparent',
|
||||
color: theme.palette.custom.greenMain,
|
||||
fontSize: '14px',
|
||||
lineHeight: 1,
|
||||
letterSpacing: '-0.3px',
|
||||
textTransform: 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '6px',
|
||||
padding: 0,
|
||||
minWidth: 0,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.custom.greenHoverBg,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: 14,
|
||||
height: 14,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Tv2 size={14} color={theme.palette.custom.greenMain} />
|
||||
</Box>
|
||||
HDHR
|
||||
</Button>
|
||||
|
||||
{/* M3U Button */}
|
||||
<Button
|
||||
onClick={copyM3UUrl}
|
||||
sx={{
|
||||
width: '64px',
|
||||
height: '25px',
|
||||
borderRadius: '4px',
|
||||
border: `1px solid ${theme.palette.custom.indigoMain}`,
|
||||
backgroundColor: 'transparent',
|
||||
color: theme.palette.custom.indigoMain,
|
||||
fontSize: '14px',
|
||||
lineHeight: 1,
|
||||
letterSpacing: '-0.3px',
|
||||
textTransform: 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '6px',
|
||||
padding: 0,
|
||||
minWidth: 0,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.custom.indigoHoverBg,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: 14,
|
||||
height: 14,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<ScreenShare size={14} color={theme.palette.custom.indigoMain} />
|
||||
</Box>
|
||||
M3U
|
||||
</Button>
|
||||
|
||||
{/* EPG Button */}
|
||||
<Button
|
||||
onClick={copyEPGUrl}
|
||||
sx={{
|
||||
width: '60px',
|
||||
height: '25px',
|
||||
borderRadius: '4px',
|
||||
border: `1px solid ${theme.palette.custom.greyBorder}`,
|
||||
backgroundColor: 'transparent',
|
||||
color: theme.palette.custom.greyText,
|
||||
fontSize: '14px',
|
||||
lineHeight: 1,
|
||||
letterSpacing: '-0.3px',
|
||||
textTransform: 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '6px',
|
||||
padding: 0,
|
||||
minWidth: 0,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.custom.greyHoverBg,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: 14,
|
||||
height: 14,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Scroll size={14} color={theme.palette.custom.greyText} />
|
||||
</Box>
|
||||
EPG
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Paper container: contains top toolbar and table (or ghost state) */}
|
||||
<Paper
|
||||
sx={{
|
||||
bgcolor: theme.palette.background.paper,
|
||||
borderRadius: 2,
|
||||
overflow: 'hidden',
|
||||
height: 'calc(100vh - 75px)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
{/* Top toolbar with Remove, Assign, Auto-match, and Add buttons */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
justifyContent: 'flex-end',
|
||||
p: 1,
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Tooltip title="Remove">
|
||||
<Button
|
||||
onClick={deleteChannels}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
startIcon={
|
||||
<IndeterminateCheckBox
|
||||
sx={{ fontSize: 16, color: theme.palette.text.secondary }}
|
||||
/>
|
||||
}
|
||||
sx={{
|
||||
borderColor: theme.palette.custom.borderDefault,
|
||||
borderRadius: '4px',
|
||||
borderWidth: '1px',
|
||||
borderStyle: 'solid',
|
||||
height: '25px',
|
||||
opacity: 0.4,
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: '0.85rem',
|
||||
px: 1,
|
||||
py: 0.5,
|
||||
'&:hover': {
|
||||
borderColor: theme.palette.custom.borderHover,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title="Assign">
|
||||
<Button
|
||||
onClick={assignChannels}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
startIcon={
|
||||
<CompareArrows
|
||||
sx={{ fontSize: 16, color: theme.palette.text.secondary }}
|
||||
/>
|
||||
}
|
||||
sx={{
|
||||
borderColor: theme.palette.custom.borderDefault,
|
||||
borderRadius: '4px',
|
||||
borderWidth: '1px',
|
||||
borderStyle: 'solid',
|
||||
height: '25px',
|
||||
opacity: 0.4,
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: '0.85rem',
|
||||
px: 1,
|
||||
py: 0.5,
|
||||
'&:hover': {
|
||||
borderColor: theme.palette.custom.borderHover,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Assign
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title="Auto-match">
|
||||
<Button
|
||||
onClick={matchEpg}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
startIcon={
|
||||
<Code
|
||||
sx={{ fontSize: 16, color: theme.palette.text.secondary }}
|
||||
/>
|
||||
}
|
||||
sx={{
|
||||
minWidth: '106px',
|
||||
borderColor: theme.palette.custom.borderDefault,
|
||||
borderRadius: '4px',
|
||||
borderWidth: '1px',
|
||||
borderStyle: 'solid',
|
||||
height: '25px',
|
||||
opacity: 0.4,
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: '0.85rem',
|
||||
px: 1,
|
||||
py: 0.5,
|
||||
'&:hover': {
|
||||
borderColor: theme.palette.custom.borderHover,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Auto-match
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title="Add Channel">
|
||||
<Button
|
||||
onClick={() => editChannel()}
|
||||
variant="contained"
|
||||
size="small"
|
||||
startIcon={
|
||||
<AddBox
|
||||
sx={{
|
||||
fontSize: 16,
|
||||
color: theme.palette.custom.successIcon,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
sx={{
|
||||
minWidth: '57px',
|
||||
height: '25px',
|
||||
borderRadius: '4px',
|
||||
borderColor: theme.palette.custom.successBorder,
|
||||
borderWidth: '1px',
|
||||
borderStyle: 'solid',
|
||||
backgroundColor: theme.palette.custom.successBg,
|
||||
color: '#fff',
|
||||
fontSize: '0.85rem',
|
||||
px: 1,
|
||||
py: 0.5,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.custom.successBgHover,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Table or ghost empty state inside Paper */}
|
||||
<Box sx={{ flex: 1, position: 'relative' }}>
|
||||
{filteredData.length === 0 ? (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
bgcolor: theme.palette.background.paper,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="img"
|
||||
src={ghostImage}
|
||||
alt="Ghost"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
width: '120px',
|
||||
height: 'auto',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
opacity: 0.2,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '25%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
textAlign: 'center',
|
||||
zIndex: 2,
|
||||
width: 467,
|
||||
px: 2,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontWeight: 400,
|
||||
fontSize: '20px',
|
||||
lineHeight: '28px',
|
||||
letterSpacing: '-0.3px',
|
||||
color: theme.palette.text.secondary,
|
||||
mb: 1,
|
||||
}}
|
||||
>
|
||||
It’s recommended to create channels after adding your M3U or
|
||||
streams.
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontWeight: 400,
|
||||
fontSize: '16px',
|
||||
lineHeight: '24px',
|
||||
letterSpacing: '-0.2px',
|
||||
color: theme.palette.text.secondary,
|
||||
mb: 2,
|
||||
}}
|
||||
>
|
||||
You can still create channels without streams if you’d like,
|
||||
and map them later.
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => editChannel()}
|
||||
startIcon={<AddIcon sx={{ fontSize: 16 }} />}
|
||||
sx={{
|
||||
minWidth: '127px',
|
||||
height: '25px',
|
||||
borderRadius: '4px',
|
||||
borderWidth: '1px',
|
||||
borderStyle: 'solid',
|
||||
color: theme.palette.text.secondary,
|
||||
borderColor: theme.palette.custom.borderHover,
|
||||
backgroundColor: '#1f1f23',
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontWeight: 400,
|
||||
fontSize: '0.85rem',
|
||||
letterSpacing: '-0.2px',
|
||||
textTransform: 'none',
|
||||
px: 1,
|
||||
py: 0.5,
|
||||
'&:hover': {
|
||||
borderColor: theme.palette.custom.borderDefault,
|
||||
backgroundColor: '#17171B',
|
||||
},
|
||||
}}
|
||||
>
|
||||
Create channel
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<Box sx={{ flex: 1, overflow: 'auto' }}>
|
||||
<MaterialReactTable table={table} />
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
{/* Channel Form Modal */}
|
||||
<ChannelForm
|
||||
channel={channel}
|
||||
isOpen={channelModalOpen}
|
||||
onClose={closeChannelForm}
|
||||
/>
|
||||
|
||||
{/* Popover for the "copy" URLs */}
|
||||
<Popover
|
||||
open={openPopover}
|
||||
anchorEl={anchorEl}
|
||||
onClose={closePopover}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
|
||||
>
|
||||
<div style={{ padding: 16, display: 'flex', alignItems: 'center' }}>
|
||||
<Box sx={{ p: 2, display: 'flex', alignItems: 'center' }}>
|
||||
<TextField
|
||||
id="output-url"
|
||||
value={textToCopy}
|
||||
variant="standard"
|
||||
// disabled
|
||||
size="small"
|
||||
sx={{ marginRight: 1 }}
|
||||
ref={outputUrlRef}
|
||||
sx={{ mr: 1 }}
|
||||
inputRef={outputUrlRef}
|
||||
/>
|
||||
<IconButton onClick={handleCopy} color="primary">
|
||||
<ContentCopy />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Box>
|
||||
</Popover>
|
||||
|
||||
{/* Snackbar for feedback */}
|
||||
<Snackbar
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
open={snackbarOpen}
|
||||
|
|
|
|||
66
frontend/src/images/ghost.svg
Normal file
66
frontend/src/images/ghost.svg
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="961.698874pt" height="1252.160897pt" viewBox="0 0 961.698874 1252.160897"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
|
||||
<g transform="translate(-521.000000,1664.561816) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M10300 16643 c-314 -15 -716 -106 -967 -219 -158 -71 -235 -109 -308
|
||||
-151 -623 -361 -1100 -916 -1393 -1623 -62 -150 -88 -222 -117 -325 -10 -33
|
||||
-23 -80 -30 -105 -7 -25 -18 -65 -23 -90 -6 -25 -18 -74 -26 -110 -25 -106
|
||||
-42 -209 -76 -465 -5 -38 -16 -275 -25 -525 -15 -461 -39 -871 -56 -990 -5
|
||||
-36 -16 -117 -24 -180 -21 -162 -77 -466 -110 -605 -86 -359 -182 -695 -272
|
||||
-950 -170 -483 -375 -929 -618 -1345 -131 -227 -160 -271 -451 -706 -533 -795
|
||||
-594 -914 -594 -1156 0 -295 169 -481 495 -544 106 -20 450 -24 555 -6 36 6
|
||||
110 18 165 28 55 9 195 43 310 76 458 131 515 140 587 85 141 -107 181 -295
|
||||
157 -743 -18 -354 -8 -504 42 -619 23 -52 97 -152 138 -184 49 -39 140 -80
|
||||
195 -87 90 -13 231 35 340 113 88 64 193 171 348 355 82 98 160 185 174 194
|
||||
24 16 26 16 60 -17 75 -72 141 -219 234 -519 92 -297 161 -505 171 -515 5 -5
|
||||
9 -15 9 -23 0 -22 109 -242 150 -303 129 -189 329 -291 507 -259 108 19 216
|
||||
76 304 162 128 123 327 410 665 958 144 235 219 330 260 330 33 0 66 -36 196
|
||||
-211 195 -263 341 -411 463 -472 166 -81 375 -47 505 83 133 133 186 305 210
|
||||
685 26 408 50 542 121 691 57 118 138 184 225 184 44 0 194 -47 289 -90 366
|
||||
-168 662 -267 943 -316 140 -25 343 -16 439 18 81 29 186 99 232 155 58 71
|
||||
110 191 121 282 30 234 -26 391 -454 1284 -36 76 -66 140 -66 142 0 3 -11 28
|
||||
-24 57 -285 623 -511 1324 -632 1963 -15 80 -18 97 -49 295 -71 453 -96 775
|
||||
-102 1295 -5 429 3 644 39 1030 19 203 16 790 -5 957 -23 181 -43 315 -63 413
|
||||
-9 44 -18 91 -21 105 -24 119 -106 379 -174 548 -134 336 -378 728 -609 977
|
||||
-317 343 -639 580 -1017 749 -213 95 -366 141 -648 196 -164 31 -503 53 -695
|
||||
43z m400 -414 c206 -19 408 -61 592 -125 124 -43 392 -172 520 -250 461 -283
|
||||
863 -763 1073 -1279 20 -49 40 -99 45 -110 23 -49 90 -260 113 -355 92 -375
|
||||
108 -518 108 -945 -1 -181 -3 -343 -6 -360 -6 -34 -32 -427 -45 -675 -31 -593
|
||||
13 -1315 120 -1950 45 -268 105 -561 150 -730 5 -19 23 -89 40 -155 17 -66 34
|
||||
-131 39 -145 4 -14 16 -50 25 -80 26 -90 52 -166 111 -335 31 -88 60 -171 65
|
||||
-185 16 -53 94 -251 157 -400 101 -241 146 -341 308 -675 74 -153 124 -263
|
||||
246 -531 32 -71 69 -191 69 -224 0 -47 -36 -107 -82 -137 -38 -26 -47 -27
|
||||
-142 -26 -189 3 -420 72 -911 271 -333 135 -539 176 -676 136 -121 -35 -258
|
||||
-140 -338 -257 -51 -75 -116 -222 -144 -327 -33 -120 -68 -395 -82 -635 -9
|
||||
-139 -23 -272 -36 -322 -13 -49 -72 -123 -98 -123 -49 0 -183 144 -356 385
|
||||
-96 133 -240 281 -314 323 -104 59 -241 63 -345 10 -46 -23 -175 -149 -238
|
||||
-233 -37 -50 -115 -167 -293 -445 -12 -19 -62 -100 -111 -180 -126 -207 -240
|
||||
-368 -369 -518 -27 -32 -78 -62 -104 -62 -56 0 -132 62 -170 137 -33 67 -48
|
||||
111 -141 413 -47 155 -128 397 -155 465 -64 163 -87 216 -120 285 -81 165
|
||||
-193 299 -290 344 -43 21 -69 26 -129 26 -146 0 -231 -64 -560 -420 -153 -165
|
||||
-219 -229 -252 -245 -32 -15 -37 -15 -59 -1 -38 25 -50 121 -36 285 16 189 14
|
||||
474 -4 620 -34 274 -106 444 -244 573 -181 171 -406 182 -868 43 -369 -112
|
||||
-653 -155 -885 -134 -136 12 -178 28 -223 81 -25 29 -27 38 -23 98 3 54 12 84
|
||||
49 161 54 111 202 351 331 534 463 660 782 1207 981 1680 30 72 64 151 75 175
|
||||
62 139 215 576 292 830 29 96 114 426 135 522 24 113 70 359 86 463 32 211 43
|
||||
294 74 545 19 155 51 640 70 1060 8 190 20 379 25 420 14 98 38 245 50 295 74
|
||||
312 132 504 205 675 48 114 181 367 250 479 184 298 468 595 753 789 67 45
|
||||
124 82 127 82 2 0 28 15 57 34 94 60 335 170 493 224 67 24 264 73 365 91 162
|
||||
30 450 39 650 20z"/>
|
||||
<path d="M9079 13621 c-58 -12 -122 -56 -190 -130 -77 -82 -121 -153 -165
|
||||
-266 -80 -200 -107 -358 -107 -625 1 -183 3 -219 27 -335 50 -244 125 -405
|
||||
236 -509 127 -119 249 -112 386 22 131 127 228 326 274 561 19 95 40 312 40
|
||||
406 -1 213 -57 471 -138 631 -96 189 -222 275 -363 245z"/>
|
||||
<path d="M11395 13541 c-145 -38 -289 -221 -372 -471 -91 -275 -111 -549 -63
|
||||
-870 39 -260 149 -494 272 -578 309 -212 686 347 688 1019 1 425 -132 778
|
||||
-331 879 -44 23 -146 34 -194 21z"/>
|
||||
<path d="M9829 11141 c-75 -14 -125 -48 -157 -106 -25 -44 -23 -378 2 -525 30
|
||||
-171 82 -306 152 -390 62 -73 186 -100 271 -59 128 62 240 251 303 514 37 156
|
||||
52 241 57 332 l6 92 -34 34 c-39 39 -122 73 -227 93 -91 17 -311 26 -373 15z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
|
|
@ -10,10 +10,10 @@ const ChannelsPage = () => {
|
|||
<Box
|
||||
sx={{
|
||||
height: '100vh', // Full viewport height
|
||||
paddingTop: 1, // Top padding
|
||||
paddingTop: 0, // Top padding
|
||||
paddingBottom: 1, // Bottom padding
|
||||
paddingRight: 0.5,
|
||||
paddingLeft: 1,
|
||||
paddingLeft: 0,
|
||||
boxSizing: 'border-box', // Include padding in height calculation
|
||||
overflow: 'hidden', // Prevent parent scrolling
|
||||
}}
|
||||
|
|
@ -25,9 +25,9 @@ const ChannelsPage = () => {
|
|||
<Box
|
||||
sx={{
|
||||
height: '100vh', // Full viewport height
|
||||
paddingTop: 1, // Top padding
|
||||
paddingTop: 0, // Top padding
|
||||
paddingBottom: 1, // Bottom padding
|
||||
paddingRight: 1,
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0.5,
|
||||
boxSizing: 'border-box', // Include padding in height calculation
|
||||
overflow: 'hidden', // Prevent parent scrolling
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
// frontend/src/theme.js
|
||||
// src/theme.js
|
||||
import { createTheme } from '@mui/material/styles';
|
||||
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
background: {
|
||||
default: '#2B2C30', // Dark background
|
||||
paper: '#333539', // Slightly lighter panel background
|
||||
default: '#18181b', // Global background color (Tailwind zinc-900)
|
||||
paper: '#27272a', // Paper background (Tailwind zinc-800)
|
||||
},
|
||||
primary: {
|
||||
// Adjust accent color if your Figma calls for a different highlight
|
||||
main: '#4A90E2',
|
||||
contrastText: '#FFFFFF',
|
||||
},
|
||||
|
|
@ -19,20 +18,40 @@ const theme = createTheme({
|
|||
},
|
||||
text: {
|
||||
primary: '#FFFFFF',
|
||||
secondary: '#C3C3C3',
|
||||
secondary: '#d4d4d8', // Updated secondary text color (Tailwind zinc-300)
|
||||
},
|
||||
// Custom colors for components (chip buttons, borders, etc.)
|
||||
custom: {
|
||||
// For chip buttons:
|
||||
greenMain: '#90C43E',
|
||||
greenHoverBg: 'rgba(144,196,62,0.1)',
|
||||
|
||||
indigoMain: '#4F39F6',
|
||||
indigoHoverBg: 'rgba(79,57,246,0.1)',
|
||||
|
||||
greyBorder: '#707070',
|
||||
greyHoverBg: 'rgba(112,112,112,0.1)',
|
||||
greyText: '#a0a0a0',
|
||||
|
||||
// Common border colors:
|
||||
borderDefault: '#3f3f46', // Tailwind zinc-700
|
||||
borderHover: '#5f5f66', // Approximate Tailwind zinc-600
|
||||
|
||||
// For the "Add" button:
|
||||
successBorder: '#00a63e',
|
||||
successBg: '#0d542b',
|
||||
successBgHover: '#0a4020',
|
||||
successIcon: '#05DF72',
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: ['Roboto', 'Helvetica', 'Arial', 'sans-serif'].join(','),
|
||||
// Example typography tweaks
|
||||
h6: {
|
||||
fontWeight: 500,
|
||||
fontSize: '0.95rem',
|
||||
},
|
||||
body1: {
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
// Set Inter as the global font
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
h1: { fontSize: '2.5rem', fontWeight: 700 },
|
||||
h2: { fontSize: '2rem', fontWeight: 700 },
|
||||
body1: { fontSize: '1rem' },
|
||||
},
|
||||
spacing: 8,
|
||||
components: {
|
||||
MuiButton: {
|
||||
styleOverrides: {
|
||||
|
|
@ -46,20 +65,27 @@ const theme = createTheme({
|
|||
MuiDrawer: {
|
||||
styleOverrides: {
|
||||
paper: {
|
||||
backgroundColor: '#333539',
|
||||
backgroundColor: '#27272a', // Use the same paper color
|
||||
color: '#FFFFFF',
|
||||
},
|
||||
},
|
||||
},
|
||||
// We remove the AppBar override since we won't be using it in App.js anymore
|
||||
MuiAppBar: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: '#2B2C30',
|
||||
backgroundColor: '#18181b',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Feel free to override more MUI components as needed...
|
||||
},
|
||||
custom: {
|
||||
sidebar: {
|
||||
activeBackground: 'rgba(21, 69, 62, 0.67)',
|
||||
activeBorder: '#14917e',
|
||||
hoverBackground: '#27272a',
|
||||
hoverBorder: '#3f3f46',
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue