mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-20 06:29:07 +00:00
Client remote control (#930)
--------- Co-authored-by: kellhogs <fabianpos99@gmail.com> Co-authored-by: Carlos Valente <carlosvalente@pm.me> Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
70e8fbf327
commit
a15eb0db4c
+24
@@ -0,0 +1,24 @@
|
||||
.fullWidth {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.actionButtons {
|
||||
display: flex;
|
||||
gap: $element-spacing;
|
||||
}
|
||||
|
||||
.halfWidth {
|
||||
width: 50%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pathList {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.badgeList {
|
||||
width: 100%;
|
||||
* {
|
||||
margin-right: $element-spacing;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import * as Panel from '../PanelUtils';
|
||||
|
||||
import ClientList from './ClientList';
|
||||
|
||||
export default function ClientControlPanel() {
|
||||
return (
|
||||
<Panel.Section>
|
||||
<Panel.Card>
|
||||
<Panel.SubHeader>Manage clients</Panel.SubHeader>
|
||||
<Panel.Divider />
|
||||
<ClientList />
|
||||
</Panel.Card>
|
||||
</Panel.Section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import { useState } from 'react';
|
||||
import { Badge, Button, useDisclosure } from '@chakra-ui/react';
|
||||
|
||||
import { RedirectClientModal } from '../../../../common/components/client-modal/RedirectClientModal';
|
||||
import { RenameClientModal } from '../../../../common/components/client-modal/RenameClientModal';
|
||||
import { setClientRemote } from '../../../../common/hooks/useSocket';
|
||||
import { useClientStore } from '../../../../common/stores/clientStore';
|
||||
import * as Panel from '../PanelUtils';
|
||||
|
||||
import style from './ClientControlPanel.module.scss';
|
||||
|
||||
export default function ClientList() {
|
||||
const id = useClientStore((store) => store.id);
|
||||
const clients = useClientStore((store) => store.clients);
|
||||
const { isOpen: isOpenRedirect, onOpen: onOpenRedirect, onClose: onCloseRedirect } = useDisclosure();
|
||||
const { isOpen: isOpenRename, onOpen: onOpenRename, onClose: onCloseRename } = useDisclosure();
|
||||
const { setIdentify } = setClientRemote;
|
||||
|
||||
const [targetId, setTargetId] = useState('');
|
||||
|
||||
const openRename = (targetId: string) => {
|
||||
setTargetId(targetId);
|
||||
onOpenRename();
|
||||
};
|
||||
|
||||
const openRedirect = (targetId: string) => {
|
||||
setTargetId(targetId);
|
||||
onOpenRedirect();
|
||||
};
|
||||
|
||||
const ontimeClients = Object.entries(clients).filter(([_, { type }]) => type === 'ontime');
|
||||
const otherClients = Object.entries(clients).filter(([_, { type }]) => type !== 'ontime');
|
||||
|
||||
const targetClient = clients[targetId];
|
||||
|
||||
return (
|
||||
<>
|
||||
{isOpenRedirect && (
|
||||
<RedirectClientModal
|
||||
id={targetId}
|
||||
name={targetClient?.name}
|
||||
path={targetClient?.path}
|
||||
isOpen={isOpenRedirect}
|
||||
onClose={onCloseRedirect}
|
||||
/>
|
||||
)}
|
||||
{isOpenRename && (
|
||||
<RenameClientModal id={targetId} name={targetClient?.name} isOpen={isOpenRename} onClose={onCloseRename} />
|
||||
)}
|
||||
<Panel.Section>
|
||||
<Panel.Title>Ontime Clients ({ontimeClients.length})</Panel.Title>
|
||||
<Panel.Table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td className={style.fullWidth}>Client Name (Connection ID)</td>
|
||||
<td className={style.fullWidth}>Path</td>
|
||||
<td />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ontimeClients.map(([key, client]) => {
|
||||
const { identify, name, path } = client;
|
||||
const isCurrent = id === key;
|
||||
return (
|
||||
<tr key={key}>
|
||||
<td className={style.badgeList}>
|
||||
<Badge variant='outline' size='xs'>
|
||||
{key}
|
||||
</Badge>
|
||||
{isCurrent && (
|
||||
<Badge variant='outline' colorScheme='yellow' size='xs'>
|
||||
self
|
||||
</Badge>
|
||||
)}
|
||||
{name}
|
||||
</td>
|
||||
{isCurrent ? <td /> : <td className={style.pathList}>{path}</td>}
|
||||
<td className={style.actionButtons}>
|
||||
<Button
|
||||
size='xs'
|
||||
className={`${identify ? style.blink : ''}`}
|
||||
isDisabled={isCurrent}
|
||||
variant={identify ? 'ontime-filled' : 'ontime-subtle'}
|
||||
data-testid={isCurrent ? '' : 'not-self-identify'}
|
||||
onClick={() => {
|
||||
setIdentify({ target: key, identify: !identify });
|
||||
}}
|
||||
>
|
||||
Identify
|
||||
</Button>
|
||||
<Button
|
||||
size='xs'
|
||||
variant='ontime-subtle'
|
||||
data-testid={isCurrent ? '' : 'not-self-rename'}
|
||||
onClick={() => openRename(key)}
|
||||
>
|
||||
Rename
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
size='xs'
|
||||
variant='ontime-subtle'
|
||||
isDisabled={isCurrent}
|
||||
data-testid={isCurrent ? '' : 'not-self-redirect'}
|
||||
onClick={() => openRedirect(key)}
|
||||
>
|
||||
Redirect
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</Panel.Table>
|
||||
</Panel.Section>
|
||||
<Panel.Divider />
|
||||
<Panel.Section>
|
||||
<Panel.Title>Other Clients ({otherClients.length})</Panel.Title>
|
||||
<Panel.Table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td className={style.halfWidth}>Client Name (Connection ID)</td>
|
||||
<td className={style.halfWidth}>Client type</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{otherClients.map(([key, client]) => {
|
||||
const { name, type } = client;
|
||||
|
||||
return (
|
||||
<tr key={key}>
|
||||
<td className={style.badgeList}>
|
||||
<Badge variant='outline' size='sx'>
|
||||
{key}
|
||||
</Badge>
|
||||
{name}
|
||||
</td>
|
||||
<td>{type}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</Panel.Table>
|
||||
</Panel.Section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user