mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-03 05:19:08 +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
|
// interfaces outside localhost wont have access
|
||||||
if (nif.name === 'localhost' && !isLocalhost) return null;
|
if (nif.name === 'localhost' && !isLocalhost) return null;
|
||||||
const address = linkToOtherHost(nif.address);
|
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 (
|
return (
|
||||||
<CopyTag key={nif.name} copyValue={address} onClick={() => handleClick(address)}>
|
<CopyTag key={`${nif.name}-${nif.address}`} copyValue={address} onClick={() => handleClick(address)}>
|
||||||
{`${nif.name} - ${nif.address}`} <IoArrowUp className={style.goIcon} />
|
{`${nif.name} - ${label}`} <IoArrowUp className={style.goIcon} />
|
||||||
</CopyTag>
|
</CopyTag>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -5,16 +5,18 @@ import Tag from '../../../../common/components/tag/Tag';
|
|||||||
import useScrollIntoView from '../../../../common/hooks/useScrollIntoView';
|
import useScrollIntoView from '../../../../common/hooks/useScrollIntoView';
|
||||||
import { usePing } from '../../../../common/hooks/useSocket';
|
import { usePing } from '../../../../common/hooks/useSocket';
|
||||||
import { sendSocket } from '../../../../common/utils/socket';
|
import { sendSocket } from '../../../../common/utils/socket';
|
||||||
import { isDocker } from '../../../../externals';
|
import { isDocker, isOntimeCloud } from '../../../../externals';
|
||||||
import type { PanelBaseProps } from '../../panel-list/PanelList';
|
import type { PanelBaseProps } from '../../panel-list/PanelList';
|
||||||
import * as Panel from '../../panel-utils/PanelUtils';
|
import * as Panel from '../../panel-utils/PanelUtils';
|
||||||
import ClientControlPanel from './client-control/ClientControlPanel';
|
import ClientControlPanel from './client-control/ClientControlPanel';
|
||||||
|
import InfoNif from './NetworkInterfaces';
|
||||||
import LogExport from './NetworkLogExport';
|
import LogExport from './NetworkLogExport';
|
||||||
|
|
||||||
/** ping values above this are flagged to the user (ms) */
|
/** ping values above this are flagged to the user (ms) */
|
||||||
const slowPingThreshold = 100;
|
const slowPingThreshold = 100;
|
||||||
|
|
||||||
export default function NetworkLogPanel({ location }: PanelBaseProps) {
|
export default function NetworkLogPanel({ location }: PanelBaseProps) {
|
||||||
|
const interfacesRef = useScrollIntoView<HTMLDivElement>('interfaces', location);
|
||||||
const clientsRef = useScrollIntoView<HTMLDivElement>('clients', location);
|
const clientsRef = useScrollIntoView<HTMLDivElement>('clients', location);
|
||||||
const logRef = useScrollIntoView<HTMLDivElement>('log', location);
|
const logRef = useScrollIntoView<HTMLDivElement>('log', location);
|
||||||
|
|
||||||
@@ -22,6 +24,21 @@ export default function NetworkLogPanel({ location }: PanelBaseProps) {
|
|||||||
<>
|
<>
|
||||||
<Panel.Header>Network</Panel.Header>
|
<Panel.Header>Network</Panel.Header>
|
||||||
{isDocker && <OntimeCloudStats />}
|
{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}>
|
<div ref={logRef}>
|
||||||
<LogExport />
|
<LogExport />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+9
@@ -7,3 +7,12 @@
|
|||||||
cursor: text;
|
cursor: text;
|
||||||
user-select: 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 Button from '../../../../../common/components/buttons/Button';
|
||||||
import { RedirectClientModal } from '../../../../../common/components/client-modal/RedirectClientModal';
|
import { RedirectClientModal } from '../../../../../common/components/client-modal/RedirectClientModal';
|
||||||
import { RenameClientModal } from '../../../../../common/components/client-modal/RenameClientModal';
|
import { RenameClientModal } from '../../../../../common/components/client-modal/RenameClientModal';
|
||||||
|
import CopyTag from '../../../../../common/components/copy-tag/CopyTag';
|
||||||
import Tag from '../../../../../common/components/tag/Tag';
|
import Tag from '../../../../../common/components/tag/Tag';
|
||||||
|
import Tooltip from '../../../../../common/components/tooltip/Tooltip';
|
||||||
import { setClientRemote } from '../../../../../common/hooks/useSocket';
|
import { setClientRemote } from '../../../../../common/hooks/useSocket';
|
||||||
import { useClientStore } from '../../../../../common/stores/clientStore';
|
import { useClientStore } from '../../../../../common/stores/clientStore';
|
||||||
|
import { openLink } from '../../../../../common/utils/linkUtils';
|
||||||
import * as Panel from '../../../panel-utils/PanelUtils';
|
import * as Panel from '../../../panel-utils/PanelUtils';
|
||||||
|
|
||||||
import style from './ClientControlPanel.module.scss';
|
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() {
|
export default function ClientList() {
|
||||||
const id = useClientStore((store) => store.id);
|
const id = useClientStore((store) => store.id);
|
||||||
const clients = useClientStore((store) => store.clients);
|
const clients = useClientStore((store) => store.clients);
|
||||||
@@ -69,6 +98,12 @@ export default function ClientList() {
|
|||||||
)}
|
)}
|
||||||
<Panel.Section>
|
<Panel.Section>
|
||||||
<Panel.Title>Ontime Clients ({ontimeClients.length})</Panel.Title>
|
<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>
|
<Panel.Table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -88,7 +123,9 @@ export default function ClientList() {
|
|||||||
{isCurrent && <Tag>SELF</Tag>}
|
{isCurrent && <Tag>SELF</Tag>}
|
||||||
{name}
|
{name}
|
||||||
</Panel.InlineElements>
|
</Panel.InlineElements>
|
||||||
<td className={style.copiable}>{path}</td>
|
<td>
|
||||||
|
<ClientPath origin={client.origin} path={path} />
|
||||||
|
</td>
|
||||||
<Panel.InlineElements relation='inner' as='td'>
|
<Panel.InlineElements relation='inner' as='td'>
|
||||||
<Button
|
<Button
|
||||||
size='small'
|
size='small'
|
||||||
@@ -127,6 +164,12 @@ export default function ClientList() {
|
|||||||
<Panel.Divider />
|
<Panel.Divider />
|
||||||
<Panel.Section>
|
<Panel.Section>
|
||||||
<Panel.Title>Other Clients ({otherClients.length})</Panel.Title>
|
<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>
|
<Panel.Table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -67,6 +67,10 @@ const staticOptions = [
|
|||||||
id: 'network',
|
id: 'network',
|
||||||
label: 'Network',
|
label: 'Network',
|
||||||
secondary: [
|
secondary: [
|
||||||
|
{
|
||||||
|
id: 'network__interfaces',
|
||||||
|
label: 'Network interfaces',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'network__log',
|
id: 'network__log',
|
||||||
label: 'Event log',
|
label: 'Event log',
|
||||||
|
|||||||
Reference in New Issue
Block a user