You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tailscale/client/web/src/components/views/device-details-view.tsx

138 lines
4.3 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
import cx from "classnames"
import React from "react"
import { apiFetch } from "src/api"
import ACLTag from "src/components/acl-tag"
import * as Control from "src/components/control-components"
import { UpdateAvailableNotification } from "src/components/update-available"
import { NodeData } from "src/hooks/node-data"
import { useLocation } from "wouter"
export default function DeviceDetailsView({
readonly,
node,
}: {
readonly: boolean
node: NodeData
}) {
const [, setLocation] = useLocation()
return (
<>
<h1 className="mb-10">Device details</h1>
<div className="flex flex-col gap-4">
<div className="-mx-5 card">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<h1>{node.DeviceName}</h1>
<div
className={cx("w-2.5 h-2.5 rounded-full", {
"bg-emerald-500": node.Status === "Running",
"bg-gray-300": node.Status !== "Running",
})}
/>
</div>
<button
className={cx(
"px-3 py-2 bg-stone-50 rounded shadow border border-stone-200 text-gray-800 text-sm font-medium",
{ "cursor-not-allowed": readonly }
)}
onClick={() =>
apiFetch("/local/v0/logout", "POST")
.then(() => setLocation("/"))
.catch((err) => alert("Logout failed: " + err.message))
}
disabled={readonly}
>
Disconnect
</button>
</div>
</div>
{node.Features["auto-update"] &&
!readonly &&
node.ClientVersion &&
!node.ClientVersion.RunningLatest && (
<UpdateAvailableNotification details={node.ClientVersion} />
)}
<div className="-mx-5 card">
<h2 className="mb-2">General</h2>
<table>
<tbody>
<tr className="flex">
<td>Managed by</td>
<td className="flex gap-1 flex-wrap">
{node.IsTagged
? node.Tags.map((t) => <ACLTag key={t} tag={t} />)
: node.Profile?.DisplayName}
</td>
</tr>
<tr>
<td>Machine name</td>
<td>{node.DeviceName}</td>
</tr>
<tr>
<td>OS</td>
<td>{node.OS}</td>
</tr>
<tr>
<td>ID</td>
<td>{node.ID}</td>
</tr>
<tr>
<td>Tailscale version</td>
<td>{node.IPNVersion}</td>
</tr>
<tr>
<td>Key expiry</td>
<td>
{node.KeyExpired
? "Expired"
: // TODO: present as relative expiry (e.g. "5 months from now")
new Date(node.KeyExpiry).toLocaleString()}
</td>
</tr>
</tbody>
</table>
</div>
<div className="-mx-5 card">
<h2 className="mb-2">Addresses</h2>
<table>
<tbody>
<tr>
<td>Tailscale IPv4</td>
<td>{node.IP}</td>
</tr>
<tr>
<td>Tailscale IPv6</td>
<td>{node.IPv6}</td>
</tr>
<tr>
<td>Short domain</td>
<td>{node.DeviceName}</td>
</tr>
<tr>
<td>Full domain</td>
<td>
{node.DeviceName}.{node.TailnetName}
</td>
</tr>
</tbody>
</table>
</div>
<Control.AdminContainer
className="text-gray-500 text-sm leading-tight text-center"
node={node}
>
Want even more details? Visit{" "}
<Control.AdminLink node={node} path={`/machines/${node.IP}`}>
this devices page
</Control.AdminLink>{" "}
in the admin console.
</Control.AdminContainer>
</div>
</>
)
}