client/web: small UI cleanups

Updates:
* Card component used throughout instead of custom card class
* SSH toggle changed to non-editable text/status icon in readonly
* Red error text on subnet route input when route post failed

Updates #10261

Signed-off-by: Sonia Appasamy <sonia@tailscale.com>
This commit is contained in:
Sonia Appasamy
2023-12-07 20:00:36 -05:00
committed by Sonia Appasamy
parent e5e5ebda44
commit d5d42d0293
7 changed files with 130 additions and 94 deletions
@@ -1,6 +1,7 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
import cx from "classnames"
import React, { useCallback, useMemo, useState } from "react"
import { useAPI } from "src/api"
import { ReactComponent as CheckCircle } from "src/assets/icons/check-circle.svg"
@@ -21,6 +22,7 @@ export default function SubnetRouterView({
node: NodeData
}) {
const api = useAPI()
const [advertisedRoutes, hasRoutes, hasUnapprovedRoutes] = useMemo(() => {
const routes = node.AdvertisedRoutes || []
return [routes, routes.length > 0, routes.find((r) => !r.Approved)]
@@ -30,9 +32,11 @@ export default function SubnetRouterView({
advertisedRoutes.length === 0 && !readonly
)
const [inputText, setInputText] = useState<string>("")
const [postError, setPostError] = useState<string>()
const resetInput = useCallback(() => {
setInputText("")
setPostError("")
setInputOpen(false)
}, [])
@@ -52,7 +56,7 @@ export default function SubnetRouterView({
</p>
{!readonly &&
(inputOpen ? (
<div className="-mx-5 card !border-0 shadow-popover">
<Card noPadding className="-mx-5 p-5 !border-0 shadow-popover">
<p className="font-medium leading-snug mb-3">
Advertise new routes
</p>
@@ -61,10 +65,19 @@ export default function SubnetRouterView({
className="text-sm"
placeholder="192.168.0.0/24"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
onChange={(e) => {
setPostError("")
setInputText(e.target.value)
}}
/>
<p className="my-2 h-6 text-gray-500 text-sm leading-tight">
Add multiple routes by providing a comma-separated list.
<p
className={cx("my-2 h-6 text-sm leading-tight", {
"text-gray-500": !postError,
"text-red-400": postError,
})}
>
{postError ||
"Add multiple routes by providing a comma-separated list."}
</p>
<div className="flex gap-3">
<Button
@@ -78,15 +91,17 @@ export default function SubnetRouterView({
.split(",")
.map((r) => ({ Route: r, Approved: false })),
],
}).then(resetInput)
})
.then(resetInput)
.catch((err: Error) => setPostError(err.message))
}
disabled={!inputText}
disabled={!inputText || postError !== ""}
>
Advertise {hasRoutes && "new "}routes
</Button>
{hasRoutes && <Button onClick={resetInput}>Cancel</Button>}
</div>
</div>
</Card>
) : (
<Button
intent="primary"
@@ -99,7 +114,7 @@ export default function SubnetRouterView({
<div className="-mx-5 mt-10">
{hasRoutes ? (
<>
<div className="px-5 py-3 bg-white rounded-lg border border-gray-200">
<Card noPadding className="px-5 py-3">
{advertisedRoutes.map((r) => (
<div
className="flex justify-between items-center pb-2.5 mb-2.5 border-b border-b-gray-200 last:pb-0 last:mb-0 last:border-b-0"
@@ -141,7 +156,7 @@ export default function SubnetRouterView({
</div>
</div>
))}
</div>
</Card>
{hasUnapprovedRoutes && (
<Control.AdminContainer
className="mt-3 w-full text-center text-gray-500 text-sm leading-tight"