mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-20 01:59:50 +00:00
Frontend updates with new stream group changes
This commit is contained in:
parent
8cdf9a40cf
commit
19aa76e55d
3 changed files with 115 additions and 83 deletions
|
|
@ -5,16 +5,18 @@ import * as Yup from 'yup';
|
|||
import API from '../../api';
|
||||
import useStreamProfilesStore from '../../store/streamProfiles';
|
||||
import { Modal, TextInput, Select, Button, Flex } from '@mantine/core';
|
||||
import useChannelsStore from '../../store/channels';
|
||||
|
||||
const Stream = ({ stream = null, isOpen, onClose }) => {
|
||||
const streamProfiles = useStreamProfilesStore((state) => state.profiles);
|
||||
const { channelGroups } = useChannelsStore();
|
||||
const [selectedStreamProfile, setSelectedStreamProfile] = useState('');
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
name: '',
|
||||
url: '',
|
||||
group_name: '',
|
||||
channel_group: null,
|
||||
stream_profile_id: '',
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
|
|
@ -23,6 +25,7 @@ const Stream = ({ stream = null, isOpen, onClose }) => {
|
|||
// stream_profile_id: Yup.string().required('Stream profile is required'),
|
||||
}),
|
||||
onSubmit: async (values, { setSubmitting, resetForm }) => {
|
||||
console.log(values);
|
||||
if (stream?.id) {
|
||||
await API.updateStream({ id: stream.id, ...values });
|
||||
} else {
|
||||
|
|
@ -40,7 +43,7 @@ const Stream = ({ stream = null, isOpen, onClose }) => {
|
|||
formik.setValues({
|
||||
name: stream.name,
|
||||
url: stream.url,
|
||||
group_name: stream.group_name,
|
||||
channel_group: stream.channel_group,
|
||||
stream_profile_id: stream.stream_profile_id,
|
||||
});
|
||||
} else {
|
||||
|
|
@ -73,13 +76,19 @@ const Stream = ({ stream = null, isOpen, onClose }) => {
|
|||
error={formik.errors.url}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
id="group_name"
|
||||
name="group_name"
|
||||
<Select
|
||||
id="channel_group"
|
||||
name="channel_group"
|
||||
label="Group"
|
||||
value={formik.values.group_name}
|
||||
onChange={formik.handleChange}
|
||||
error={formik.errors.group_name}
|
||||
value={formik.values.channel_group}
|
||||
onChange={(value) => {
|
||||
formik.setFieldValue('channel_group', value); // Update Formik's state with the new value
|
||||
}}
|
||||
error={formik.errors.channel_group}
|
||||
data={channelGroups.map((group) => ({
|
||||
label: group.name,
|
||||
value: `${group.id}`,
|
||||
}))}
|
||||
/>
|
||||
|
||||
<Select
|
||||
|
|
@ -87,8 +96,10 @@ const Stream = ({ stream = null, isOpen, onClose }) => {
|
|||
name="stream_profile_id"
|
||||
label="Stream Profile"
|
||||
placeholder="Optional"
|
||||
value={selectedStreamProfile}
|
||||
onChange={setSelectedStreamProfile}
|
||||
value={formik.values.stream_profile_id}
|
||||
onChange={(value) => {
|
||||
formik.setFieldValue('stream_profile_id', value); // Update Formik's state with the new value
|
||||
}}
|
||||
error={formik.errors.stream_profile_id}
|
||||
data={streamProfiles.map((profile) => ({
|
||||
label: profile.name,
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ const StreamsTable = ({}) => {
|
|||
});
|
||||
const [filters, setFilters] = useState({
|
||||
name: '',
|
||||
group_name: '',
|
||||
channel_group: '',
|
||||
m3u_account: '',
|
||||
});
|
||||
const debouncedFilters = useDebounce(filters, 500);
|
||||
|
|
@ -83,7 +83,7 @@ const StreamsTable = ({}) => {
|
|||
* Stores
|
||||
*/
|
||||
const { playlists } = usePlaylistsStore();
|
||||
const { channelsPageSelection } = useChannelsStore();
|
||||
const { channelGroups, channelsPageSelection } = useChannelsStore();
|
||||
const channelSelectionStreams = useChannelsStore(
|
||||
(state) => state.channels[state.channelsPageSelection[0]?.id]?.streams
|
||||
);
|
||||
|
|
@ -133,7 +133,8 @@ const StreamsTable = ({}) => {
|
|||
},
|
||||
{
|
||||
header: 'Group',
|
||||
accessorKey: 'group_name',
|
||||
accessorFn: (row) =>
|
||||
channelGroups.find((group) => group.id === row.channel_group)?.name,
|
||||
size: 100,
|
||||
Header: ({ column }) => (
|
||||
<Box onClick={handleSelectClick}>
|
||||
|
|
@ -200,7 +201,7 @@ const StreamsTable = ({}) => {
|
|||
const handleGroupChange = (value) => {
|
||||
setFilters((prev) => ({
|
||||
...prev,
|
||||
group_name: value ? value : '',
|
||||
channel_group: value ? value : '',
|
||||
}));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,18 @@
|
|||
import React, { useMemo, useState, useEffect, useCallback } from 'react';
|
||||
import { ActionIcon, Box, Center, Grid, Text } from '@mantine/core';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Card,
|
||||
Center,
|
||||
Flex,
|
||||
Grid,
|
||||
Group,
|
||||
SimpleGrid,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table';
|
||||
import { TableHelper } from '../helpers';
|
||||
import API from '../api';
|
||||
|
|
@ -15,7 +28,32 @@ import {
|
|||
Binary,
|
||||
ArrowDown01,
|
||||
SquareX,
|
||||
Timer,
|
||||
} from 'lucide-react';
|
||||
import dayjs from 'dayjs';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
|
||||
dayjs.extend(duration);
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
const getStartDate = (uptime) => {
|
||||
// Get the current date and time
|
||||
const currentDate = new Date();
|
||||
// Calculate the start date by subtracting uptime (in milliseconds)
|
||||
const startDate = new Date(currentDate.getTime() - uptime * 1000);
|
||||
// Format the date as a string (you can adjust the format as needed)
|
||||
return startDate.toLocaleString({
|
||||
weekday: 'short', // optional, adds day of the week
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: true, // 12-hour format with AM/PM
|
||||
});
|
||||
};
|
||||
|
||||
const ChannelsPage = () => {
|
||||
const { channels, channelsByUUID, stats: channelStats } = useChannelsStore();
|
||||
|
|
@ -146,32 +184,6 @@ const ChannelsPage = () => {
|
|||
data: clients,
|
||||
columns: useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'logo',
|
||||
header: 'Logo',
|
||||
accessorKey: 'channel.logo_url',
|
||||
size: 50,
|
||||
Cell: ({ cell }) => (
|
||||
<Center>
|
||||
<img
|
||||
src={cell.getValue() || logo}
|
||||
width="20"
|
||||
alt="channel logo"
|
||||
/>
|
||||
</Center>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Channel',
|
||||
accessorKey: 'channel.name',
|
||||
size: 100,
|
||||
mantineTableBodyCellProps: {
|
||||
style: {
|
||||
whiteSpace: 'nowrap',
|
||||
maxWidth: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'User-Agent',
|
||||
accessorKey: 'user_agent',
|
||||
|
|
@ -180,6 +192,8 @@ const ChannelsPage = () => {
|
|||
style: {
|
||||
whiteSpace: 'nowrap',
|
||||
maxWidth: 400,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -243,48 +257,54 @@ const ChannelsPage = () => {
|
|||
}, [channelStats]);
|
||||
|
||||
return (
|
||||
<Grid style={{ padding: 18 }}>
|
||||
<Grid.Col span={6}>
|
||||
<Text
|
||||
w={88}
|
||||
h={24}
|
||||
style={{
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontWeight: 500,
|
||||
fontSize: '20px',
|
||||
lineHeight: 1,
|
||||
letterSpacing: '-0.3px',
|
||||
color: 'gray.6', // Adjust this to match MUI's theme.palette.text.secondary
|
||||
marginBottom: 0,
|
||||
}}
|
||||
>
|
||||
Channels
|
||||
</Text>
|
||||
<Box style={{ paddingTop: 10 }}>
|
||||
<MantineReactTable table={channelsTable} />
|
||||
</Box>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<Text
|
||||
w={88}
|
||||
h={24}
|
||||
style={{
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontWeight: 500,
|
||||
fontSize: '20px',
|
||||
lineHeight: 1,
|
||||
letterSpacing: '-0.3px',
|
||||
color: 'gray.6', // Adjust this to match MUI's theme.palette.text.secondary
|
||||
marginBottom: 0,
|
||||
}}
|
||||
>
|
||||
Clients
|
||||
</Text>
|
||||
<Box style={{ paddingTop: 10 }}>
|
||||
<MantineReactTable table={clientsTable} />
|
||||
</Box>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<SimpleGrid cols={2} spacing="md" style={{ padding: 10 }}>
|
||||
{activeChannels.map((channel) => (
|
||||
<Card shadow="sm" padding="lg" radius="md" withBorder>
|
||||
<Stack>
|
||||
<Flex justify="space-between" align="center">
|
||||
<Group>
|
||||
<Title order={5}>{channel.name}</Title>
|
||||
<img
|
||||
src={channel.logo_url || logo}
|
||||
width="20"
|
||||
alt="channel logo"
|
||||
/>
|
||||
</Group>
|
||||
|
||||
<Group>
|
||||
<Box>
|
||||
<Tooltip label={getStartDate(channel.uptime)}>
|
||||
<Center>
|
||||
<Timer style={{ paddingRight: 5 }} />
|
||||
{dayjs.duration(channel.uptime, 'seconds').humanize()}
|
||||
</Center>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Center>
|
||||
<Tooltip label="Stop Channel">
|
||||
<ActionIcon variant="transparent" color="red.9">
|
||||
<SquareX size="24" />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Center>
|
||||
</Group>
|
||||
</Flex>
|
||||
|
||||
<Box>
|
||||
<Flex
|
||||
justify="space-between"
|
||||
align="center"
|
||||
style={{ paddingRight: 10, paddingLeft: 10 }}
|
||||
>
|
||||
<Text>Clients</Text>
|
||||
<Text>{channel.client_count}</Text>
|
||||
</Flex>
|
||||
<MantineReactTable table={clientsTable} />
|
||||
</Box>
|
||||
</Stack>
|
||||
</Card>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue