mirror of
https://github.com/tale/headplane.git
synced 2026-07-26 07:48:14 +00:00
chore: combine link usage
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { CircleX } from "lucide-react";
|
||||
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
import cn from "~/utils/cn";
|
||||
|
||||
interface FooterProps {
|
||||
@@ -28,7 +28,7 @@ export default function Footer({ url, debug, healthy }: FooterProps) {
|
||||
<div className={cn("text-xs leading-none", !healthy && "hidden md:block")}>
|
||||
<p>
|
||||
Headplane is free. Please consider{" "}
|
||||
<Link to="https://github.com/sponsors/tale" name="Aarnav's GitHub Sponsors">
|
||||
<Link isExternal to="https://github.com/sponsors/tale" name="Aarnav's GitHub Sponsors">
|
||||
donating
|
||||
</Link>{" "}
|
||||
to support development.{" "}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { ExternalLink } from "lucide-react";
|
||||
|
||||
import cn from "~/utils/cn";
|
||||
|
||||
export interface LinkProps {
|
||||
to: string;
|
||||
name: string;
|
||||
children: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function Link({ to, name: alt, children, className }: LinkProps) {
|
||||
return (
|
||||
<a
|
||||
href={to}
|
||||
aria-label={alt}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={cn(
|
||||
"inline-flex items-center gap-x-0.5",
|
||||
"text-blue-500 hover:text-blue-700",
|
||||
"dark:text-blue-400 dark:hover:text-blue-300",
|
||||
"focus:outline-hidden focus:ring-2 focus:ring-indigo-500/40 focus:ring-offset-1 rounded-md",
|
||||
"dark:focus:ring-indigo-400/40 dark:focus:ring-offset-mist-900",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
<ExternalLink className="w-3.5" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import cn from "~/utils/cn";
|
||||
|
||||
import Card from "./Card";
|
||||
import Code from "./Code";
|
||||
import Link from "./Link";
|
||||
import Link from "./link";
|
||||
|
||||
export function getErrorMessage(error: Error | unknown): {
|
||||
title: string;
|
||||
@@ -137,7 +137,7 @@ export function getErrorMessage(error: Error | unknown): {
|
||||
<Card.Text>
|
||||
An unexpected error occurred which is most likely a bug. Please consider reporting
|
||||
filing an issue on the{" "}
|
||||
<Link name="Headplane GitHub" to="https://github.com/tale/headplane/issues">
|
||||
<Link isExternal name="Headplane GitHub" to="https://github.com/tale/headplane/issues">
|
||||
Headplane GitHub
|
||||
</Link>{" "}
|
||||
repository with the details below.
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import type { JSX } from "react";
|
||||
import { Link as RouterLink } from "react-router";
|
||||
|
||||
import cn from "~/utils/cn";
|
||||
|
||||
const linkStyles = cn(
|
||||
"inline-flex items-center gap-x-0.5",
|
||||
"text-blue-500 hover:text-blue-700",
|
||||
"dark:text-blue-400 dark:hover:text-blue-300",
|
||||
"focus:ring-offset-1 rounded-md",
|
||||
"focus:outline-hidden focus:ring-2 focus:ring-indigo-500/40",
|
||||
"dark:focus:ring-indigo-400/40 dark:focus:ring-offset-mist-900",
|
||||
);
|
||||
|
||||
export type LinkProps =
|
||||
| {
|
||||
isExternal: true;
|
||||
to: string;
|
||||
name: string;
|
||||
children: string;
|
||||
className?: string;
|
||||
}
|
||||
| {
|
||||
isExternal?: false;
|
||||
to: string;
|
||||
children?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export default function Link(props: LinkProps): JSX.Element {
|
||||
if (props.isExternal) {
|
||||
return (
|
||||
<a
|
||||
href={props.to}
|
||||
aria-label={props.name}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={cn(linkStyles, props.className)}
|
||||
>
|
||||
{props.children}
|
||||
<ExternalLink className="w-3.5" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<RouterLink to={props.to} className={cn(linkStyles, props.className)}>
|
||||
{props.children}
|
||||
</RouterLink>
|
||||
);
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { Form } from "react-router";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import Card from "~/components/Card";
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
import Options from "~/components/Options";
|
||||
import toast from "~/utils/toast";
|
||||
|
||||
@@ -58,6 +58,7 @@ export default function NoAccess({ linkedUserName, osValue }: NoAccessProps) {
|
||||
<p className="mt-1 text-center text-xs text-mist-600 dark:text-mist-300">
|
||||
Click this button to copy the command.{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Linux installation script"
|
||||
to="https://github.com/tailscale/tailscale/blob/main/scripts/installer.sh"
|
||||
>
|
||||
@@ -112,6 +113,7 @@ export default function NoAccess({ linkedUserName, osValue }: NoAccessProps) {
|
||||
<br />
|
||||
You can also download Tailscale on the{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="macOS App Store"
|
||||
to="https://apps.apple.com/ca/app/tailscale/id1475387142"
|
||||
>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { AlertCircle, CloudOff } from "lucide-react";
|
||||
|
||||
import Card from "~/components/Card";
|
||||
import Code from "~/components/Code";
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
import { OidcConnectorError } from "~/server/web/oidc-connector";
|
||||
|
||||
export function OidcDiscoveryFailedNotice() {
|
||||
@@ -35,6 +35,7 @@ export function OidcConfigErrorNotice({ errors }: { errors: OidcConnectorError[]
|
||||
))}
|
||||
</ul>{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Headplane OIDC Issues"
|
||||
to="https://headplane.net/configuration/sso#troubleshooting"
|
||||
>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { AlertCircle } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Form, Link as RemixLink, redirect, useSearchParams } from "react-router";
|
||||
import { Form, redirect, useSearchParams } from "react-router";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import Card from "~/components/Card";
|
||||
import Code from "~/components/Code";
|
||||
import Input from "~/components/Input";
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
import { useLiveData } from "~/utils/live-data";
|
||||
|
||||
import type { Route } from "./+types/page";
|
||||
@@ -103,6 +103,7 @@ export default function Page({ loaderData, actionData }: Route.ComponentProps) {
|
||||
Headplane is configured to use secure cookies, but this site is being served over an
|
||||
insecure connection and login will not work correctly.{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Headplane Common Issues"
|
||||
to="https://headplane.net/configuration/common-issues#issue-logging-in-does-not-do-anything"
|
||||
>
|
||||
@@ -138,7 +139,7 @@ export default function Page({ loaderData, actionData }: Route.ComponentProps) {
|
||||
</Button>
|
||||
</Form>
|
||||
{isOidcConnectorEnabled ? (
|
||||
<RemixLink to="/oidc/start">
|
||||
<Link to="/oidc/start">
|
||||
<Button
|
||||
className="mt-2 w-full"
|
||||
isDisabled={oidcErrorCodes.length > 0}
|
||||
@@ -146,7 +147,7 @@ export default function Page({ loaderData, actionData }: Route.ComponentProps) {
|
||||
>
|
||||
Single Sign-On
|
||||
</Button>
|
||||
</RemixLink>
|
||||
</Link>
|
||||
) : undefined}
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Form } from "react-router";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import Code from "~/components/Code";
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
import TableList from "~/components/TableList";
|
||||
import cn from "~/utils/cn";
|
||||
|
||||
@@ -20,7 +20,11 @@ export default function ManageRecords({ records, isDisabled }: Props) {
|
||||
<p>
|
||||
Headscale supports adding custom DNS records to your Tailnet. As of now, only <Code>A</Code>{" "}
|
||||
and <Code>AAAA</Code> records are supported.{" "}
|
||||
<Link name="Headscale DNS Records documentation" to="https://headscale.net/stable/ref/dns">
|
||||
<Link
|
||||
isExternal
|
||||
name="Headscale DNS Records documentation"
|
||||
to="https://headscale.net/stable/ref/dns"
|
||||
>
|
||||
Learn More
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ChevronDown, Copy } from "lucide-react";
|
||||
import { useMemo } from "react";
|
||||
import { Link } from "react-router";
|
||||
|
||||
import Chip from "~/components/Chip";
|
||||
import Link from "~/components/link";
|
||||
import Menu from "~/components/Menu";
|
||||
import StatusCircle from "~/components/StatusCircle";
|
||||
import { ExitNodeTag } from "~/components/tags/ExitNode";
|
||||
|
||||
@@ -2,13 +2,12 @@ import { Plus, TagsIcon, X } from "lucide-react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useFetcher } from "react-router";
|
||||
|
||||
import type { Machine } from "~/types";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import Dialog from "~/components/Dialog";
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
import Select from "~/components/Select";
|
||||
import TableList from "~/components/TableList";
|
||||
import type { Machine } from "~/types";
|
||||
import cn from "~/utils/cn";
|
||||
|
||||
interface TagsProps {
|
||||
@@ -73,7 +72,11 @@ export default function Tags({ machine, isOpen, setIsOpen, existingTags }: TagsP
|
||||
<Dialog.Title>Edit ACL tags for {machine.givenName}</Dialog.Title>
|
||||
<Dialog.Text>
|
||||
ACL tags can be used to reference machines in your ACL policies. See the{" "}
|
||||
<Link name="Tailscale documentation" to="https://tailscale.com/kb/1068/acl-tags">
|
||||
<Link
|
||||
isExternal
|
||||
name="Tailscale documentation"
|
||||
to="https://tailscale.com/kb/1068/acl-tags"
|
||||
>
|
||||
Tailscale documentation
|
||||
</Link>{" "}
|
||||
for more information.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { CheckCircle, CircleSlash, Info, UserCircle } from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { data, Link as RemixLink } from "react-router";
|
||||
import { data } from "react-router";
|
||||
|
||||
import Attribute from "~/components/Attribute";
|
||||
import Button from "~/components/Button";
|
||||
import Card from "~/components/Card";
|
||||
import Chip from "~/components/Chip";
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
import StatusCircle from "~/components/StatusCircle";
|
||||
import Tooltip from "~/components/Tooltip";
|
||||
import cn from "~/utils/cn";
|
||||
@@ -75,9 +75,9 @@ export default function Page({
|
||||
return (
|
||||
<div>
|
||||
<p className="text-md mb-8">
|
||||
<RemixLink className="font-medium" to="/machines">
|
||||
<Link className="font-medium" to="/machines">
|
||||
All Machines
|
||||
</RemixLink>
|
||||
</Link>
|
||||
<span className="mx-2">/</span>
|
||||
{node.givenName}
|
||||
</p>
|
||||
@@ -129,7 +129,11 @@ export default function Page({
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<p>
|
||||
Subnets let you expose physical network routes onto Tailscale.{" "}
|
||||
<Link name="Tailscale Subnets Documentation" to="https://tailscale.com/kb/1019/subnets">
|
||||
<Link
|
||||
isExternal
|
||||
name="Tailscale Subnets Documentation"
|
||||
to="https://tailscale.com/kb/1019/subnets"
|
||||
>
|
||||
Learn More
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useMemo, useState } from "react";
|
||||
|
||||
import Code from "~/components/Code";
|
||||
import Input from "~/components/Input";
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
import Tooltip from "~/components/Tooltip";
|
||||
import { Capabilities } from "~/server/web/roles";
|
||||
import cn from "~/utils/cn";
|
||||
@@ -151,6 +151,7 @@ export default function Page({ loaderData }: Route.ComponentProps) {
|
||||
<p>
|
||||
Manage the devices connected to your Tailnet.{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Tailscale Manage Devices Documentation"
|
||||
to="https://tailscale.com/kb/1372/manage-devices"
|
||||
>
|
||||
|
||||
@@ -5,7 +5,7 @@ import Button from "~/components/Button";
|
||||
import Code from "~/components/Code";
|
||||
import Dialog from "~/components/Dialog";
|
||||
import Input from "~/components/Input";
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
import NumberInput from "~/components/NumberInput";
|
||||
import Select from "~/components/Select";
|
||||
import Switch from "~/components/Switch";
|
||||
@@ -204,6 +204,7 @@ export default function AddAuthKey({
|
||||
Devices authenticated with this key will be automatically removed once they go
|
||||
offline.{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Tailscale Ephemeral Nodes Documentation"
|
||||
to="https://tailscale.com/kb/1111/ephemeral-nodes"
|
||||
>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { FileKey2 } from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { Link as RemixLink } from "react-router";
|
||||
|
||||
import Code from "~/components/Code";
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
import Notice from "~/components/Notice";
|
||||
import Select from "~/components/Select";
|
||||
import TableList from "~/components/TableList";
|
||||
@@ -167,9 +166,9 @@ export default function Page({
|
||||
return (
|
||||
<div className="flex flex-col md:w-2/3">
|
||||
<p className="text-md mb-8">
|
||||
<RemixLink className="font-medium" to="/settings">
|
||||
<Link className="font-medium" to="/settings">
|
||||
Settings
|
||||
</RemixLink>
|
||||
</Link>
|
||||
<span className="mx-2">/</span> Pre-Auth Keys
|
||||
</p>
|
||||
{!access ? (
|
||||
@@ -194,6 +193,7 @@ export default function Page({
|
||||
Headscale fully supports pre-authentication keys in order to easily add devices to your
|
||||
Tailnet. To learn more about using pre-authentication keys, visit the{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Tailscale Auth Keys documentation"
|
||||
to="https://tailscale.com/kb/1085/auth-keys/"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { Link as RemixLink } from "react-router";
|
||||
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
|
||||
import type { Route } from "./+types/overview";
|
||||
|
||||
@@ -30,6 +29,7 @@ export default function Page({ loaderData: { config, isOidcEnabled } }: Route.Co
|
||||
Headscale fully supports pre-authentication keys in order to easily add devices to your
|
||||
Tailnet. To learn more about using pre-authentication keys, visit the{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Tailscale Auth Keys documentation"
|
||||
to="https://tailscale.com/kb/1085/auth-keys/"
|
||||
>
|
||||
@@ -37,12 +37,12 @@ export default function Page({ loaderData: { config, isOidcEnabled } }: Route.Co
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
<RemixLink to="/settings/auth-keys">
|
||||
<Link to="/settings/auth-keys">
|
||||
<div className="flex items-center text-lg font-medium">
|
||||
Manage Auth Keys
|
||||
<ArrowRight className="ml-2 h-5 w-5" />
|
||||
</div>
|
||||
</RemixLink>
|
||||
</Link>
|
||||
{config && isOidcEnabled ? (
|
||||
<>
|
||||
<div className="flex w-full flex-col sm:w-2/3">
|
||||
@@ -53,6 +53,7 @@ export default function Page({ loaderData: { config, isOidcEnabled } }: Route.Co
|
||||
Tailnet to only certain users or groups and Headplane will also respect these settings
|
||||
when authenticating.{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Headscale OIDC documentation"
|
||||
to="https://headscale.net/stable/ref/oidc/#basic-configuration"
|
||||
>
|
||||
@@ -60,12 +61,12 @@ export default function Page({ loaderData: { config, isOidcEnabled } }: Route.Co
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
<RemixLink to="/settings/restrictions">
|
||||
<Link to="/settings/restrictions">
|
||||
<div className="flex items-center text-lg font-medium">
|
||||
Manage Restrictions
|
||||
<ArrowRight className="ml-2 h-5 w-5" />
|
||||
</div>
|
||||
</RemixLink>
|
||||
</Link>
|
||||
</>
|
||||
) : undefined}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { data, Link as RemixLink } from "react-router";
|
||||
import { data } from "react-router";
|
||||
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
import Notice from "~/components/Notice";
|
||||
import { Capabilities } from "~/server/web/roles";
|
||||
|
||||
@@ -46,9 +46,9 @@ export default function Page({ loaderData: { access, writable, settings } }: Rou
|
||||
<div className="flex max-w-(--breakpoint-lg) flex-col gap-4">
|
||||
<div className="flex w-full flex-col sm:w-2/3">
|
||||
<p className="text-md mb-4">
|
||||
<RemixLink className="font-medium" to="/settings">
|
||||
<Link className="font-medium" to="/settings">
|
||||
Settings
|
||||
</RemixLink>
|
||||
</Link>
|
||||
<span className="mx-2">/</span> Authentication Restrictions
|
||||
</p>
|
||||
{!access ? (
|
||||
@@ -70,6 +70,7 @@ export default function Page({ loaderData: { access, writable, settings } }: Rou
|
||||
certain users or groups and Headplane will also respect these settings when
|
||||
authenticating.{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Headscale OIDC documentation"
|
||||
to="https://headscale.net/stable/ref/oidc/#basic-configuration"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Building2, House, Key } from "lucide-react";
|
||||
|
||||
import Card from "~/components/Card";
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
|
||||
import CreateUser from "../dialogs/create-user";
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function ManageBanner({ oidc, isDisabled }: ManageBannerProps) {
|
||||
{oidc ? (
|
||||
<>
|
||||
Users are managed through your{" "}
|
||||
<Link name="OIDC Provider" to={oidc.issuer}>
|
||||
<Link isExternal name="OIDC Provider" to={oidc.issuer}>
|
||||
OpenID Connect provider
|
||||
</Link>
|
||||
{". "}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Form, NavLink } from "react-router";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import Card from "~/components/Card";
|
||||
import Link from "~/components/Link";
|
||||
import Link from "~/components/link";
|
||||
import Options from "~/components/Options";
|
||||
import StatusCircle from "~/components/StatusCircle";
|
||||
import { findHeadscaleUserBySubject } from "~/server/web/headscale-identity";
|
||||
@@ -216,6 +216,7 @@ export default function Page({
|
||||
<p className="mt-1 text-center text-xs text-mist-600 dark:text-mist-300">
|
||||
Click this button to copy the command.{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Linux installation script"
|
||||
to="https://github.com/tailscale/tailscale/blob/main/scripts/installer.sh"
|
||||
>
|
||||
@@ -270,6 +271,7 @@ export default function Page({
|
||||
<br />
|
||||
You can also download Tailscale on the{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="macOS App Store"
|
||||
to="https://apps.apple.com/ca/app/tailscale/id1475387142"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user