client/web: add copyable components throughout UI

Updates the IP address on home view to open a copyable list of node
addresses on click. And makes various values on the details view
copyable text items, mirroring the machine admin panel table.

As part of these changes, pulls the AddressCard, NiceIP and QuickCopy
components from the admin panel, with the AddressCard slightly modified
to avoid needing to also pull in the CommandLine component.

A new toaster interface is also added, allowing us to display success
and failure toasts throughout the UI. The toaster code is slightly
modified from it's admin form to avoid the need for some excess
libraries.

Updates #10261

Signed-off-by: Sonia Appasamy <sonia@tailscale.com>
This commit is contained in:
Sonia Appasamy
2023-12-05 10:09:33 -05:00
committed by Sonia Appasamy
parent 650c67a0a1
commit a95b3cbfa8
19 changed files with 850 additions and 26 deletions
@@ -6,9 +6,11 @@ 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 NiceIP from "src/components/nice-ip"
import { UpdateAvailableNotification } from "src/components/update-available"
import { NodeData } from "src/hooks/node-data"
import Button from "src/ui/button"
import QuickCopy from "src/ui/quick-copy"
import { useLocation } from "wouter"
export default function DeviceDetailsView({
@@ -69,7 +71,14 @@ export default function DeviceDetailsView({
</tr>
<tr>
<td>Machine name</td>
<td>{node.DeviceName}</td>
<td>
<QuickCopy
primaryActionValue={node.DeviceName}
primaryActionSubject="machine name"
>
{node.DeviceName}
</QuickCopy>
</td>
</tr>
<tr>
<td>OS</td>
@@ -77,7 +86,14 @@ export default function DeviceDetailsView({
</tr>
<tr>
<td>ID</td>
<td>{node.ID}</td>
<td>
<QuickCopy
primaryActionValue={node.ID}
primaryActionSubject="ID"
>
{node.ID}
</QuickCopy>
</td>
</tr>
<tr>
<td>Tailscale version</td>
@@ -101,20 +117,46 @@ export default function DeviceDetailsView({
<tbody>
<tr>
<td>Tailscale IPv4</td>
<td>{node.IP}</td>
<td>
<QuickCopy
primaryActionValue={node.IPv4}
primaryActionSubject="IPv4 address"
>
{node.IPv4}
</QuickCopy>
</td>
</tr>
<tr>
<td>Tailscale IPv6</td>
<td>{node.IPv6}</td>
<td>
<QuickCopy
primaryActionValue={node.IPv6}
primaryActionSubject="IPv6 address"
>
<NiceIP ip={node.IPv6} />
</QuickCopy>
</td>
</tr>
<tr>
<td>Short domain</td>
<td>{node.DeviceName}</td>
<td>
<QuickCopy
primaryActionValue={node.DeviceName}
primaryActionSubject="short domain"
>
{node.DeviceName}
</QuickCopy>
</td>
</tr>
<tr>
<td>Full domain</td>
<td>
{node.DeviceName}.{node.TailnetName}
<QuickCopy
primaryActionValue={`${node.DeviceName}.${node.TailnetName}`}
primaryActionSubject="full domain"
>
{node.DeviceName}.{node.TailnetName}
</QuickCopy>
</td>
</tr>
</tbody>
@@ -125,7 +167,7 @@ export default function DeviceDetailsView({
node={node}
>
Want even more details? Visit{" "}
<Control.AdminLink node={node} path={`/machines/${node.IP}`}>
<Control.AdminLink node={node} path={`/machines/${node.IPv4}`}>
this devices page
</Control.AdminLink>{" "}
in the admin console.
@@ -5,9 +5,10 @@ import cx from "classnames"
import React, { useMemo } from "react"
import { ReactComponent as ArrowRight } from "src/assets/icons/arrow-right.svg"
import { ReactComponent as Machine } from "src/assets/icons/machine.svg"
import AddressCard from "src/components/address-copy-card"
import ExitNodeSelector from "src/components/exit-node-selector"
import { NodeData, NodeUpdaters } from "src/hooks/node-data"
import { pluralize } from "src/util"
import { pluralize } from "src/utils/util"
import { Link, useLocation } from "wouter"
export default function HomeView({
@@ -49,7 +50,13 @@ export default function HomeView({
</p>
</div>
</div>
<p className="text-gray-800 text-lg leading-[25.20px]">{node.IP}</p>
<AddressCard
triggerClassName="text-gray-800 text-lg leading-[25.20px]"
v4Address={node.IPv4}
v6Address={node.IPv6}
shortDomain={node.DeviceName}
fullDomain={`${node.DeviceName}.${node.TailnetName}`}
/>
</div>
{(node.Features["advertise-exit-node"] ||
node.Features["use-exit-node"]) && (
@@ -142,7 +142,7 @@ export default function SubnetRouterView({
node={node}
>
To approve routes, in the admin console go to{" "}
<Control.AdminLink node={node} path={`/machines/${node.IP}`}>
<Control.AdminLink node={node} path={`/machines/${node.IPv4}`}>
the machines route settings
</Control.AdminLink>
.