mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-11 02:13:48 +00:00
feat(network): help users orient themselves in the network panel
- add a network interfaces card to the network panel. The addresses users need to reach Ontime from other devices were only available in the sharing panel - show the port along with the address, the bare IP is not enough to reach the server - explain what each section does and how the client actions behave - the client path was selectable text but could not be opened or copied. It now uses a copy tag with the full client URL, which also makes the client origin visible in setups with several interfaces Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NrhcEgY8mqaxZi8veMPrrx
This commit is contained in:
@@ -19,10 +19,12 @@ export default function InfoNif() {
|
||||
// interfaces outside localhost wont have access
|
||||
if (nif.name === 'localhost' && !isLocalhost) return null;
|
||||
const address = linkToOtherHost(nif.address);
|
||||
// we show the address as it would be typed in a browser, including the port
|
||||
const label = new URL(address).host;
|
||||
|
||||
return (
|
||||
<CopyTag key={nif.name} copyValue={address} onClick={() => handleClick(address)}>
|
||||
{`${nif.name} - ${nif.address}`} <IoArrowUp className={style.goIcon} />
|
||||
<CopyTag key={`${nif.name}-${nif.address}`} copyValue={address} onClick={() => handleClick(address)}>
|
||||
{`${nif.name} - ${label}`} <IoArrowUp className={style.goIcon} />
|
||||
</CopyTag>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -5,16 +5,18 @@ import Tag from '../../../../common/components/tag/Tag';
|
||||
import useScrollIntoView from '../../../../common/hooks/useScrollIntoView';
|
||||
import { usePing } from '../../../../common/hooks/useSocket';
|
||||
import { sendSocket } from '../../../../common/utils/socket';
|
||||
import { isDocker } from '../../../../externals';
|
||||
import { isDocker, isOntimeCloud } from '../../../../externals';
|
||||
import type { PanelBaseProps } from '../../panel-list/PanelList';
|
||||
import * as Panel from '../../panel-utils/PanelUtils';
|
||||
import ClientControlPanel from './client-control/ClientControlPanel';
|
||||
import InfoNif from './NetworkInterfaces';
|
||||
import LogExport from './NetworkLogExport';
|
||||
|
||||
/** ping values above this are flagged to the user (ms) */
|
||||
const slowPingThreshold = 100;
|
||||
|
||||
export default function NetworkLogPanel({ location }: PanelBaseProps) {
|
||||
const interfacesRef = useScrollIntoView<HTMLDivElement>('interfaces', location);
|
||||
const clientsRef = useScrollIntoView<HTMLDivElement>('clients', location);
|
||||
const logRef = useScrollIntoView<HTMLDivElement>('log', location);
|
||||
|
||||
@@ -22,6 +24,21 @@ export default function NetworkLogPanel({ location }: PanelBaseProps) {
|
||||
<>
|
||||
<Panel.Header>Network</Panel.Header>
|
||||
{isDocker && <OntimeCloudStats />}
|
||||
{!isOntimeCloud && (
|
||||
<div ref={interfacesRef}>
|
||||
<Panel.Section>
|
||||
<Panel.Card>
|
||||
<Panel.SubHeader>Network interfaces</Panel.SubHeader>
|
||||
<Panel.Divider />
|
||||
<Panel.Paragraph>
|
||||
Addresses which can be used to reach Ontime from other devices in the network. <br />
|
||||
Click an address to open it in a new window, or use the icon to copy it to the clipboard.
|
||||
</Panel.Paragraph>
|
||||
<InfoNif />
|
||||
</Panel.Card>
|
||||
</Panel.Section>
|
||||
</div>
|
||||
)}
|
||||
<div ref={logRef}>
|
||||
<LogExport />
|
||||
</div>
|
||||
|
||||
+9
@@ -7,3 +7,12 @@
|
||||
cursor: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: $label-gray;
|
||||
}
|
||||
|
||||
// align section notes with the table contents
|
||||
.note {
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
+44
-1
@@ -5,13 +5,42 @@ import { useState } from 'react';
|
||||
import Button from '../../../../../common/components/buttons/Button';
|
||||
import { RedirectClientModal } from '../../../../../common/components/client-modal/RedirectClientModal';
|
||||
import { RenameClientModal } from '../../../../../common/components/client-modal/RenameClientModal';
|
||||
import CopyTag from '../../../../../common/components/copy-tag/CopyTag';
|
||||
import Tag from '../../../../../common/components/tag/Tag';
|
||||
import Tooltip from '../../../../../common/components/tooltip/Tooltip';
|
||||
import { setClientRemote } from '../../../../../common/hooks/useSocket';
|
||||
import { useClientStore } from '../../../../../common/stores/clientStore';
|
||||
import { openLink } from '../../../../../common/utils/linkUtils';
|
||||
import * as Panel from '../../../panel-utils/PanelUtils';
|
||||
|
||||
import style from './ClientControlPanel.module.scss';
|
||||
|
||||
/**
|
||||
* Shows the path a client is showing
|
||||
* The full URL is available for copying or opening in a new window
|
||||
*/
|
||||
function ClientPath({ origin, path }: { origin: string; path: string }) {
|
||||
// clients report their path on connection, we may not have it yet
|
||||
if (!path) {
|
||||
return <span className={style.muted}>unknown</span>;
|
||||
}
|
||||
|
||||
// origin is empty for clients which have not reported their location
|
||||
if (!origin) {
|
||||
return <span className={style.copiable}>{path}</span>;
|
||||
}
|
||||
|
||||
const fullUrl = `${origin}${path}`;
|
||||
|
||||
return (
|
||||
<Tooltip text={fullUrl} render={<span />}>
|
||||
<CopyTag copyValue={fullUrl} size='small' onClick={() => openLink(fullUrl)}>
|
||||
{path}
|
||||
</CopyTag>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ClientList() {
|
||||
const id = useClientStore((store) => store.id);
|
||||
const clients = useClientStore((store) => store.clients);
|
||||
@@ -69,6 +98,12 @@ export default function ClientList() {
|
||||
)}
|
||||
<Panel.Section>
|
||||
<Panel.Title>Ontime Clients ({ontimeClients.length})</Panel.Title>
|
||||
<div className={style.note}>
|
||||
<Panel.Description>
|
||||
Ontime views connected to this server. Identify shows a marker in the client screen until you turn it off,
|
||||
redirect sends the client to a different view.
|
||||
</Panel.Description>
|
||||
</div>
|
||||
<Panel.Table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -88,7 +123,9 @@ export default function ClientList() {
|
||||
{isCurrent && <Tag>SELF</Tag>}
|
||||
{name}
|
||||
</Panel.InlineElements>
|
||||
<td className={style.copiable}>{path}</td>
|
||||
<td>
|
||||
<ClientPath origin={client.origin} path={path} />
|
||||
</td>
|
||||
<Panel.InlineElements relation='inner' as='td'>
|
||||
<Button
|
||||
size='small'
|
||||
@@ -127,6 +164,12 @@ export default function ClientList() {
|
||||
<Panel.Divider />
|
||||
<Panel.Section>
|
||||
<Panel.Title>Other Clients ({otherClients.length})</Panel.Title>
|
||||
<div className={style.note}>
|
||||
<Panel.Description>
|
||||
Connections which are not Ontime views, such as integrations and custom clients. These cannot be controlled
|
||||
from Ontime.
|
||||
</Panel.Description>
|
||||
</div>
|
||||
<Panel.Table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -67,6 +67,10 @@ const staticOptions = [
|
||||
id: 'network',
|
||||
label: 'Network',
|
||||
secondary: [
|
||||
{
|
||||
id: 'network__interfaces',
|
||||
label: 'Network interfaces',
|
||||
},
|
||||
{
|
||||
id: 'network__log',
|
||||
label: 'Event log',
|
||||
|
||||
Reference in New Issue
Block a user