mirror of
https://github.com/tale/headplane.git
synced 2026-09-04 11:15:42 +00:00
feat: switch form fields to base-ui
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import Attribute from "~/components/Attribute";
|
||||
import Attribute from "~/components/attribute";
|
||||
import type { PreAuthKey, User } from "~/types";
|
||||
import { getUserDisplayName } from "~/utils/user";
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import type { Key } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useFetcher } from "react-router";
|
||||
|
||||
import Button from "~/components/button";
|
||||
import Code from "~/components/Code";
|
||||
import Dialog, { DialogPanel } from "~/components/Dialog";
|
||||
import Input from "~/components/Input";
|
||||
import Code from "~/components/code";
|
||||
import Dialog, { DialogPanel } from "~/components/dialog";
|
||||
import Input from "~/components/input";
|
||||
import Link from "~/components/link";
|
||||
import NumberInput from "~/components/NumberInput";
|
||||
import Select from "~/components/Select";
|
||||
import Switch from "~/components/Switch";
|
||||
import Text from "~/components/Text";
|
||||
import Title from "~/components/Title";
|
||||
import NumberInput from "~/components/number-input";
|
||||
import Select from "~/components/select";
|
||||
import Switch from "~/components/switch";
|
||||
import Text from "~/components/text";
|
||||
import Title from "~/components/title";
|
||||
import type { User } from "~/types";
|
||||
import toast from "~/utils/toast";
|
||||
import { getUserDisplayName } from "~/utils/user";
|
||||
@@ -49,7 +48,7 @@ export default function AddAuthKey({
|
||||
const [tagOnly, setTagOnly] = useState(false);
|
||||
const currentUser = selfServiceOnly ? findCurrentUser(users, currentSubject) : null;
|
||||
const availableUsers = selfServiceOnly && currentUser ? [currentUser] : users;
|
||||
const [userId, setUserId] = useState<Key | null>(availableUsers[0]?.id);
|
||||
const [userId, setUserId] = useState<string | null>(availableUsers[0]?.id);
|
||||
const [tags, setTags] = useState("");
|
||||
|
||||
const createdKey = fetcher.data?.success ? fetcher.data.key : null;
|
||||
@@ -138,9 +137,9 @@ export default function AddAuthKey({
|
||||
<Text className="text-sm">Create a key owned by ACL tags instead of a user.</Text>
|
||||
</div>
|
||||
<Switch
|
||||
defaultSelected={tagOnly}
|
||||
defaultChecked={tagOnly}
|
||||
label="Tag-only"
|
||||
onChange={() => setTagOnly(!tagOnly)}
|
||||
onCheckedChange={() => setTagOnly(!tagOnly)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -153,23 +152,23 @@ export default function AddAuthKey({
|
||||
? "You can only create keys for your own user."
|
||||
: "Machines will belong to this user when they authenticate."
|
||||
}
|
||||
isDisabled={selfServiceOnly}
|
||||
isRequired
|
||||
disabled={selfServiceOnly}
|
||||
required
|
||||
label="User"
|
||||
onSelectionChange={(value) => setUserId(value)}
|
||||
onValueChange={(value) => setUserId(value)}
|
||||
placeholder="Select a user"
|
||||
selectedKey={userId}
|
||||
>
|
||||
{availableUsers.map((user) => (
|
||||
<Select.Item key={user.id}>{getUserDisplayName(user)}</Select.Item>
|
||||
))}
|
||||
</Select>
|
||||
value={userId}
|
||||
items={availableUsers.map((user) => ({
|
||||
value: user.id,
|
||||
label: getUserDisplayName(user),
|
||||
}))}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Input
|
||||
className="mb-2"
|
||||
description="Comma-separated tags (e.g. server, prod). The tag: prefix is added automatically."
|
||||
isRequired={tagOnly}
|
||||
required={tagOnly}
|
||||
label="ACL Tags"
|
||||
onChange={(value) => setTags(value)}
|
||||
placeholder="server, prod"
|
||||
@@ -178,15 +177,10 @@ export default function AddAuthKey({
|
||||
<NumberInput
|
||||
defaultValue={90}
|
||||
description="Set this key to expire after a certain number of days."
|
||||
formatOptions={{
|
||||
style: "unit",
|
||||
unit: "day",
|
||||
unitDisplay: "short",
|
||||
}}
|
||||
isRequired
|
||||
required
|
||||
label="Key Expiration"
|
||||
maxValue={365_000}
|
||||
minValue={1}
|
||||
max={365_000}
|
||||
min={1}
|
||||
name="expiry"
|
||||
/>
|
||||
<div className="mt-6 flex items-center justify-between gap-2">
|
||||
@@ -195,9 +189,9 @@ export default function AddAuthKey({
|
||||
<Text className="text-sm">Use this key to authenticate more than one device.</Text>
|
||||
</div>
|
||||
<Switch
|
||||
defaultSelected={reusable}
|
||||
defaultChecked={reusable}
|
||||
label="Reusable"
|
||||
onChange={() => setReusable(!reusable)}
|
||||
onCheckedChange={() => setReusable(!reusable)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-6 flex items-center justify-between gap-2">
|
||||
@@ -212,9 +206,9 @@ export default function AddAuthKey({
|
||||
</Text>
|
||||
</div>
|
||||
<Switch
|
||||
defaultSelected={ephemeral}
|
||||
defaultChecked={ephemeral}
|
||||
label="Ephemeral"
|
||||
onChange={() => setEphemeral(!ephemeral)}
|
||||
onCheckedChange={() => setEphemeral(!ephemeral)}
|
||||
/>
|
||||
</div>
|
||||
</DialogPanel>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Button from "~/components/button";
|
||||
import Dialog, { DialogPanel } from "~/components/Dialog";
|
||||
import Text from "~/components/Text";
|
||||
import Title from "~/components/Title";
|
||||
import Dialog, { DialogPanel } from "~/components/dialog";
|
||||
import Text from "~/components/text";
|
||||
import Title from "~/components/title";
|
||||
import type { PreAuthKey, User } from "~/types";
|
||||
|
||||
interface ExpireAuthKeyProps {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { FileKey2 } from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
import Code from "~/components/Code";
|
||||
import Code from "~/components/code";
|
||||
import Link from "~/components/link";
|
||||
import Notice from "~/components/Notice";
|
||||
import Select from "~/components/Select";
|
||||
import TableList from "~/components/TableList";
|
||||
import Notice from "~/components/notice";
|
||||
import Select from "~/components/select";
|
||||
import TableList from "~/components/table-list";
|
||||
import { usersResource } from "~/server/headscale/live-store";
|
||||
import { Capabilities } from "~/server/web/roles";
|
||||
import type { PreAuthKey } from "~/types";
|
||||
@@ -207,38 +207,36 @@ export default function Page({
|
||||
<div className="mt-4 flex items-center gap-4">
|
||||
<Select
|
||||
className="w-full"
|
||||
defaultSelectedKey="__headplane_all"
|
||||
isDisabled={isDisabled}
|
||||
defaultValue="__headplane_all"
|
||||
disabled={isDisabled}
|
||||
label="User"
|
||||
onSelectionChange={(value) => setSelectedUser(value?.toString() ?? "")}
|
||||
onValueChange={(value) => setSelectedUser(value ?? "")}
|
||||
placeholder="Select a user"
|
||||
>
|
||||
{[
|
||||
<Select.Item key="__headplane_all">All</Select.Item>,
|
||||
items={[
|
||||
{ value: "__headplane_all", label: "All" },
|
||||
...keys
|
||||
.filter((k): k is { user: User; preAuthKeys: PreAuthKey[] } => k.user !== null)
|
||||
.map(({ user }) => (
|
||||
<Select.Item key={user.id}>{getUserDisplayName(user)}</Select.Item>
|
||||
)),
|
||||
.map(({ user }) => ({ value: user.id, label: getUserDisplayName(user) })),
|
||||
...(keys.some(({ user }) => user === null)
|
||||
? [<Select.Item key="__headplane_tag_only">Tag Only</Select.Item>]
|
||||
? [{ value: "__headplane_tag_only", label: "Tag Only" }]
|
||||
: []),
|
||||
]}
|
||||
</Select>
|
||||
/>
|
||||
<Select
|
||||
className="w-full"
|
||||
defaultSelectedKey="active"
|
||||
isDisabled={isDisabled}
|
||||
defaultValue="active"
|
||||
disabled={isDisabled}
|
||||
label="Status"
|
||||
onSelectionChange={(value) => setStatus((value?.toString() ?? "active") as Status)}
|
||||
onValueChange={(value) => setStatus((value ?? "active") as Status)}
|
||||
placeholder="Select a status"
|
||||
>
|
||||
<Select.Item key="all">All</Select.Item>
|
||||
<Select.Item key="active">Active</Select.Item>
|
||||
<Select.Item key="expired">Used/Expired</Select.Item>
|
||||
<Select.Item key="reusable">Reusable</Select.Item>
|
||||
<Select.Item key="ephemeral">Ephemeral</Select.Item>
|
||||
</Select>
|
||||
items={[
|
||||
{ value: "all", label: "All" },
|
||||
{ value: "active", label: "Active" },
|
||||
{ value: "expired", label: "Used/Expired" },
|
||||
{ value: "reusable", label: "Reusable" },
|
||||
{ value: "ephemeral", label: "Ephemeral" },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<TableList className="mt-4">
|
||||
{keys.flatMap(({ preAuthKeys }) => preAuthKeys).length === 0 ? (
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
import Button from "~/components/button";
|
||||
import Dialog, { DialogPanel } from "~/components/Dialog";
|
||||
import Input from "~/components/Input";
|
||||
import Text from "~/components/Text";
|
||||
import Title from "~/components/Title";
|
||||
import Dialog, { DialogPanel } from "~/components/dialog";
|
||||
import Input from "~/components/input";
|
||||
import Text from "~/components/text";
|
||||
import Title from "~/components/title";
|
||||
|
||||
interface AddDomainProps {
|
||||
domains: string[];
|
||||
@@ -50,8 +50,8 @@ export default function AddDomain({ domains, isDisabled }: AddDomainProps) {
|
||||
? `Matches users with <user>@${domain.trim()}`
|
||||
: "Enter a domain to match users with their email addresses."
|
||||
}
|
||||
isInvalid={domain.trim().length === 0 || isInvalid}
|
||||
isRequired
|
||||
invalid={domain.trim().length === 0 || isInvalid}
|
||||
required
|
||||
label="Domain"
|
||||
name="domain"
|
||||
onChange={setDomain}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
import Button from "~/components/button";
|
||||
import Dialog, { DialogPanel } from "~/components/Dialog";
|
||||
import Input from "~/components/Input";
|
||||
import Text from "~/components/Text";
|
||||
import Title from "~/components/Title";
|
||||
import Dialog, { DialogPanel } from "~/components/dialog";
|
||||
import Input from "~/components/input";
|
||||
import Text from "~/components/text";
|
||||
import Title from "~/components/title";
|
||||
|
||||
interface AddGroupProps {
|
||||
groups: string[];
|
||||
@@ -36,8 +36,8 @@ export default function AddGroup({ groups, isDisabled }: AddGroupProps) {
|
||||
<input name="action_id" type="hidden" value="add_group" />
|
||||
<Input
|
||||
description="The group to allow for OIDC authentication."
|
||||
isInvalid={group.trim().length === 0 || isInvalid}
|
||||
isRequired
|
||||
invalid={group.trim().length === 0 || isInvalid}
|
||||
required
|
||||
label="Group"
|
||||
name="group"
|
||||
onChange={setGroup}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
import Button from "~/components/button";
|
||||
import Dialog, { DialogPanel } from "~/components/Dialog";
|
||||
import Input from "~/components/Input";
|
||||
import Text from "~/components/Text";
|
||||
import Title from "~/components/Title";
|
||||
import Dialog, { DialogPanel } from "~/components/dialog";
|
||||
import Input from "~/components/input";
|
||||
import Text from "~/components/text";
|
||||
import Title from "~/components/title";
|
||||
|
||||
interface AddUserProps {
|
||||
users: string[];
|
||||
@@ -36,8 +36,8 @@ export default function AddUser({ users, isDisabled }: AddUserProps) {
|
||||
<input name="action_id" type="hidden" value="add_user" />
|
||||
<Input
|
||||
description="The user to allow for OIDC authentication."
|
||||
isInvalid={user.trim().length === 0 || isInvalid}
|
||||
isRequired
|
||||
invalid={user.trim().length === 0 || isInvalid}
|
||||
required
|
||||
label="User"
|
||||
name="user"
|
||||
onChange={setUser}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { data } from "react-router";
|
||||
|
||||
import Link from "~/components/link";
|
||||
import Notice from "~/components/Notice";
|
||||
import Notice from "~/components/notice";
|
||||
import { Capabilities } from "~/server/web/roles";
|
||||
|
||||
import type { Route } from "./+types/overview";
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from "react";
|
||||
import { Form } from "react-router";
|
||||
|
||||
import Button from "~/components/button";
|
||||
import TableList from "~/components/TableList";
|
||||
import TableList from "~/components/table-list";
|
||||
import cn from "~/utils/cn";
|
||||
|
||||
interface RestrictionProps {
|
||||
|
||||
Reference in New Issue
Block a user