mirror of
https://github.com/tale/headplane.git
synced 2026-08-26 04:16:49 +00:00
feat: switch to new dialog across all code
This commit is contained in:
@@ -34,7 +34,7 @@ export default function Auth({ magic }: Props) {
|
||||
You can add, remove, and rename users here.
|
||||
</p>
|
||||
<div className="flex items-center gap-2 mt-4">
|
||||
<Add magic={magic} />
|
||||
<Add />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function Oidc({ oidc, magic }: Props) {
|
||||
manage users through your OIDC provider.
|
||||
</p>
|
||||
<div className="flex items-center gap-2 mt-4">
|
||||
<Add magic={magic} />
|
||||
<Add />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,61 +1,24 @@
|
||||
import { Form, useSubmit } from 'react-router';
|
||||
import { useState } from 'react';
|
||||
|
||||
import Code from '~/components/Code';
|
||||
import Dialog from '~/components/Dialog';
|
||||
import TextField from '~/components/TextField';
|
||||
|
||||
interface Props {
|
||||
magic?: string;
|
||||
}
|
||||
|
||||
export default function Add({ magic }: Props) {
|
||||
const [username, setUsername] = useState('');
|
||||
const submit = useSubmit();
|
||||
import Input from '~/components/Input';
|
||||
|
||||
export default function Add() {
|
||||
return (
|
||||
<Dialog>
|
||||
<Dialog.Button>Add a new user</Dialog.Button>
|
||||
|
||||
<Dialog.Panel>
|
||||
{(close) => (
|
||||
<>
|
||||
<Dialog.Title>Add a new user</Dialog.Title>
|
||||
<Dialog.Text className="mb-8">
|
||||
Enter a username to create a new user.{' '}
|
||||
{magic ? (
|
||||
<>
|
||||
Since Magic DNS is enabled, machines will be accessible via{' '}
|
||||
<Code>[machine]. .{magic}</Code>.
|
||||
</>
|
||||
) : undefined}
|
||||
</Dialog.Text>
|
||||
<Form
|
||||
method="POST"
|
||||
onSubmit={(event) => {
|
||||
submit(event.currentTarget);
|
||||
setUsername('');
|
||||
}}
|
||||
>
|
||||
<input type="hidden" name="_method" value="create" />
|
||||
<TextField
|
||||
label="Username"
|
||||
placeholder="my-new-user"
|
||||
name="username"
|
||||
state={[username, setUsername]}
|
||||
className="my-2"
|
||||
/>
|
||||
<Dialog.Gutter>
|
||||
<Dialog.Action variant="cancel" onPress={close}>
|
||||
Cancel
|
||||
</Dialog.Action>
|
||||
<Dialog.Action variant="confirm" onPress={close}>
|
||||
Create
|
||||
</Dialog.Action>
|
||||
</Dialog.Gutter>
|
||||
</Form>
|
||||
</>
|
||||
)}
|
||||
<Dialog.Title>Add a new user</Dialog.Title>
|
||||
<Dialog.Text className="mb-8">
|
||||
Enter a username to create a new user. Usernames can be addressed when
|
||||
managing ACL policies.
|
||||
</Dialog.Text>
|
||||
<input type="hidden" name="_method" value="create" />
|
||||
<Input
|
||||
isRequired
|
||||
name="username"
|
||||
label="Username"
|
||||
placeholder="my-new-user"
|
||||
/>
|
||||
</Dialog.Panel>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import { X } from 'lucide-react';
|
||||
import { Form, useSubmit } from 'react-router';
|
||||
import { useState } from 'react';
|
||||
|
||||
import IconButton from '~/components/IconButton';
|
||||
import Code from '~/components/Code';
|
||||
import Dialog from '~/components/Dialog';
|
||||
|
||||
@@ -11,47 +7,20 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function Remove({ username }: Props) {
|
||||
const submit = useSubmit();
|
||||
const [dialog, setDialog] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
label={`Delete ${username}`}
|
||||
onPress={() => setDialog(true)}
|
||||
>
|
||||
<Dialog>
|
||||
<Dialog.IconButton label={`Delete ${username}`}>
|
||||
<X className="p-0.5" />
|
||||
</IconButton>
|
||||
<Dialog control={[dialog, setDialog]}>
|
||||
<Dialog.Panel control={[dialog, setDialog]}>
|
||||
{(close) => (
|
||||
<>
|
||||
<Dialog.Title>Delete {username}?</Dialog.Title>
|
||||
<Dialog.Text className="mb-8">
|
||||
Are you sure you want to delete {username}? A deleted user
|
||||
cannot be recovered.
|
||||
</Dialog.Text>
|
||||
<Form
|
||||
method="POST"
|
||||
onSubmit={(event) => {
|
||||
submit(event.currentTarget);
|
||||
}}
|
||||
>
|
||||
<input type="hidden" name="_method" value="delete" />
|
||||
<input type="hidden" name="username" value={username} />
|
||||
<div className="mt-6 flex justify-end gap-2 mt-6">
|
||||
<Dialog.Action variant="cancel" onPress={close}>
|
||||
Cancel
|
||||
</Dialog.Action>
|
||||
<Dialog.Action variant="confirm" onPress={close}>
|
||||
Delete
|
||||
</Dialog.Action>
|
||||
</div>
|
||||
</Form>
|
||||
</>
|
||||
)}
|
||||
</Dialog.Panel>
|
||||
</Dialog>
|
||||
</>
|
||||
</Dialog.IconButton>
|
||||
<Dialog.Panel>
|
||||
<Dialog.Title>Delete {username}?</Dialog.Title>
|
||||
<Dialog.Text className="mb-8">
|
||||
Are you sure you want to delete {username}? A deleted user cannot be
|
||||
recovered.
|
||||
</Dialog.Text>
|
||||
<input type="hidden" name="_method" value="delete" />
|
||||
<input type="hidden" name="username" value={username} />
|
||||
</Dialog.Panel>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,65 +1,37 @@
|
||||
import { Pencil } from 'lucide-react';
|
||||
import { Form, useSubmit } from 'react-router';
|
||||
import { useState } from 'react';
|
||||
|
||||
import IconButton from '~/components/IconButton';
|
||||
import Dialog from '~/components/Dialog';
|
||||
import TextField from '~/components/TextField';
|
||||
import Input from '~/components/Input';
|
||||
|
||||
interface Props {
|
||||
username: string;
|
||||
magic?: string;
|
||||
}
|
||||
|
||||
export default function Rename({ username, magic }: Props) {
|
||||
const submit = useSubmit();
|
||||
const [dialog, setDialog] = useState(false);
|
||||
// TODO: Server side validation before submitting
|
||||
export default function Rename({ username }: Props) {
|
||||
const [newName, setNewName] = useState(username);
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
label={`Rename ${username}`}
|
||||
onPress={() => setDialog(true)}
|
||||
>
|
||||
<Dialog>
|
||||
<Dialog.IconButton label={`Rename ${username}`}>
|
||||
<Pencil className="p-1" />
|
||||
</IconButton>
|
||||
<Dialog control={[dialog, setDialog]}>
|
||||
<Dialog.Panel control={[dialog, setDialog]}>
|
||||
{(close) => (
|
||||
<>
|
||||
<Dialog.Title>Rename {username}?</Dialog.Title>
|
||||
<Dialog.Text className="mb-8">
|
||||
Enter a new username for {username}
|
||||
</Dialog.Text>
|
||||
<Form
|
||||
method="POST"
|
||||
onSubmit={(event) => {
|
||||
submit(event.currentTarget);
|
||||
}}
|
||||
>
|
||||
<input type="hidden" name="_method" value="rename" />
|
||||
<input type="hidden" name="old" value={username} />
|
||||
<TextField
|
||||
label="Username"
|
||||
placeholder="my-new-name"
|
||||
name="new"
|
||||
state={[newName, setNewName]}
|
||||
className="my-2"
|
||||
/>
|
||||
<div className="mt-6 flex justify-end gap-2 mt-6">
|
||||
<Dialog.Action variant="cancel" onPress={close}>
|
||||
Cancel
|
||||
</Dialog.Action>
|
||||
<Dialog.Action variant="confirm" onPress={close}>
|
||||
Rename
|
||||
</Dialog.Action>
|
||||
</div>
|
||||
</Form>
|
||||
</>
|
||||
)}
|
||||
</Dialog.Panel>
|
||||
</Dialog>
|
||||
</>
|
||||
</Dialog.IconButton>
|
||||
<Dialog.Panel>
|
||||
<Dialog.Title>Rename {username}?</Dialog.Title>
|
||||
<Dialog.Text className="mb-8">
|
||||
Enter a new username for {username}. Changing a username will not
|
||||
update any ACL policies that may refer to this user by their old
|
||||
username.
|
||||
</Dialog.Text>
|
||||
<input type="hidden" name="_method" value="rename" />
|
||||
<input type="hidden" name="old" value={username} />
|
||||
<Input
|
||||
isRequired
|
||||
name="new"
|
||||
label="Username"
|
||||
placeholder="my-new-name"
|
||||
/>
|
||||
</Dialog.Panel>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,23 @@
|
||||
import {
|
||||
DataRef,
|
||||
DndContext,
|
||||
useDraggable,
|
||||
useDroppable,
|
||||
} from '@dnd-kit/core';
|
||||
import { DataRef, DndContext, useDraggable, useDroppable } from '@dnd-kit/core';
|
||||
import { PersonIcon } from '@primer/octicons-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router';
|
||||
import { useActionData, useLoaderData, useSubmit } from 'react-router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ClientOnly } from 'remix-utils/client-only';
|
||||
|
||||
import Attribute from '~/components/Attribute';
|
||||
import Card from '~/components/Card';
|
||||
import StatusCircle from '~/components/StatusCircle';
|
||||
import { ErrorPopup } from '~/components/Error';
|
||||
import StatusCircle from '~/components/StatusCircle';
|
||||
import { toast } from '~/components/Toaster';
|
||||
import type { Machine, User } from '~/types';
|
||||
import { cn } from '~/utils/cn';
|
||||
import { loadContext } from '~/utils/config/headplane';
|
||||
import { loadConfig } from '~/utils/config/headscale';
|
||||
import { del, post, pull } from '~/utils/headscale';
|
||||
import { send } from '~/utils/res';
|
||||
import { getSession } from '~/utils/sessions.server';
|
||||
import { useLiveData } from '~/utils/useLiveData';
|
||||
import { send } from '~/utils/res';
|
||||
|
||||
import Auth from './components/auth';
|
||||
import Oidc from './components/oidc';
|
||||
@@ -305,7 +300,7 @@ function UserCard({ user, magic }: CardProps) {
|
||||
<span className="text-lg font-mono">{user.name}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Rename username={user.name} magic={magic} />
|
||||
<Rename username={user.name} />
|
||||
{user.machines.length === 0 ? (
|
||||
<Remove username={user.name} />
|
||||
) : undefined}
|
||||
@@ -322,7 +317,5 @@ function UserCard({ user, magic }: CardProps) {
|
||||
}
|
||||
|
||||
export function ErrorBoundary() {
|
||||
return (
|
||||
<ErrorPopup type="embedded" />
|
||||
)
|
||||
return <ErrorPopup type="embedded" />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user