mirror of
https://github.com/tale/headplane.git
synced 2026-08-24 19:46:29 +00:00
feat: switch to a new form hook
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { type } from "arktype";
|
||||
|
||||
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 { useForm } from "~/hooks/use-form";
|
||||
|
||||
const domainSchema = type({
|
||||
domain: "string > 0",
|
||||
});
|
||||
|
||||
interface AddDomainProps {
|
||||
domains: string[];
|
||||
@@ -12,27 +17,29 @@ interface AddDomainProps {
|
||||
}
|
||||
|
||||
export default function AddDomain({ domains, isDisabled }: AddDomainProps) {
|
||||
const [domain, setDomain] = useState("");
|
||||
const form = useForm({
|
||||
schema: domainSchema,
|
||||
validate: (values) => {
|
||||
const domain = (values.domain as string).trim();
|
||||
if (domain.length === 0) return undefined;
|
||||
|
||||
const isInvalid = useMemo(() => {
|
||||
if (!domain || domain.trim().length === 0) {
|
||||
// Empty domain is invalid, but no error shown
|
||||
return false;
|
||||
}
|
||||
if (domains.includes(domain)) {
|
||||
return { domain: "This domain already exists in the list." };
|
||||
}
|
||||
|
||||
if (domains.includes(domain.trim())) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
const url = new URL(`http://${domain}`);
|
||||
if (url.hostname !== domain) {
|
||||
return { domain: "This is not a valid domain." };
|
||||
}
|
||||
} catch {
|
||||
return { domain: "This is not a valid domain." };
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if domain is a valid FQDN
|
||||
const url = new URL(`http://${domain.trim()}`);
|
||||
return url.hostname !== domain.trim();
|
||||
} catch (e) {
|
||||
// If URL constructor fails, it's not a valid domain
|
||||
return true;
|
||||
}
|
||||
}, [domain, domains]);
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
const domain = (form.values.domain as string).trim();
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
@@ -45,23 +52,16 @@ export default function AddDomain({ domains, isDisabled }: AddDomainProps) {
|
||||
</Text>
|
||||
<input name="action_id" type="hidden" value="add_domain" />
|
||||
<Input
|
||||
{...form.field("domain")}
|
||||
description={
|
||||
domain.trim().length > 0
|
||||
? `Matches users with <user>@${domain.trim()}`
|
||||
domain.length > 0
|
||||
? `Matches users with <user>@${domain}`
|
||||
: "Enter a domain to match users with their email addresses."
|
||||
}
|
||||
invalid={domain.trim().length === 0 || isInvalid}
|
||||
required
|
||||
label="Domain"
|
||||
name="domain"
|
||||
onChange={setDomain}
|
||||
placeholder="example.com"
|
||||
/>
|
||||
{isInvalid && (
|
||||
<p className="mt-2 text-sm text-red-500">
|
||||
The domain you entered is invalid or already exists in the list.
|
||||
</p>
|
||||
)}
|
||||
</DialogPanel>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { type } from "arktype";
|
||||
|
||||
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 { useForm } from "~/hooks/use-form";
|
||||
|
||||
const groupSchema = type({
|
||||
group: "string > 0",
|
||||
});
|
||||
|
||||
interface AddGroupProps {
|
||||
groups: string[];
|
||||
@@ -12,18 +17,19 @@ interface AddGroupProps {
|
||||
}
|
||||
|
||||
export default function AddGroup({ groups, isDisabled }: AddGroupProps) {
|
||||
const [group, setGroup] = useState("");
|
||||
const form = useForm({
|
||||
schema: groupSchema,
|
||||
validate: (values) => {
|
||||
const group = (values.group as string).trim();
|
||||
if (group.length === 0) return undefined;
|
||||
|
||||
const isInvalid = useMemo(() => {
|
||||
if (!group || group.trim().length === 0) {
|
||||
// Empty group is invalid, but no error shown
|
||||
return false;
|
||||
}
|
||||
if (groups.includes(group)) {
|
||||
return { group: "This group already exists in the list." };
|
||||
}
|
||||
|
||||
if (groups.includes(group.trim())) {
|
||||
return true;
|
||||
}
|
||||
}, [group, groups]);
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
@@ -35,19 +41,12 @@ export default function AddGroup({ groups, isDisabled }: AddGroupProps) {
|
||||
</Text>
|
||||
<input name="action_id" type="hidden" value="add_group" />
|
||||
<Input
|
||||
{...form.field("group")}
|
||||
description="The group to allow for OIDC authentication."
|
||||
invalid={group.trim().length === 0 || isInvalid}
|
||||
required
|
||||
label="Group"
|
||||
name="group"
|
||||
onChange={setGroup}
|
||||
placeholder="admin"
|
||||
/>
|
||||
{isInvalid && (
|
||||
<p className="mt-2 text-sm text-red-500">
|
||||
The group you entered already exists in the list of allowed groups.
|
||||
</p>
|
||||
)}
|
||||
</DialogPanel>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { type } from "arktype";
|
||||
|
||||
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 { useForm } from "~/hooks/use-form";
|
||||
|
||||
const userSchema = type({
|
||||
user: "string > 0",
|
||||
});
|
||||
|
||||
interface AddUserProps {
|
||||
users: string[];
|
||||
@@ -12,18 +17,19 @@ interface AddUserProps {
|
||||
}
|
||||
|
||||
export default function AddUser({ users, isDisabled }: AddUserProps) {
|
||||
const [user, setUser] = useState("");
|
||||
const form = useForm({
|
||||
schema: userSchema,
|
||||
validate: (values) => {
|
||||
const user = (values.user as string).trim();
|
||||
if (user.length === 0) return undefined;
|
||||
|
||||
const isInvalid = useMemo(() => {
|
||||
if (!user || user.trim().length === 0) {
|
||||
// Empty user is invalid, but no error shown
|
||||
return false;
|
||||
}
|
||||
if (users.includes(user)) {
|
||||
return { user: "This user already exists in the list." };
|
||||
}
|
||||
|
||||
if (users.includes(user.trim())) {
|
||||
return true;
|
||||
}
|
||||
}, [user, users]);
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
@@ -35,19 +41,12 @@ export default function AddUser({ users, isDisabled }: AddUserProps) {
|
||||
</Text>
|
||||
<input name="action_id" type="hidden" value="add_user" />
|
||||
<Input
|
||||
{...form.field("user")}
|
||||
description="The user to allow for OIDC authentication."
|
||||
invalid={user.trim().length === 0 || isInvalid}
|
||||
required
|
||||
label="User"
|
||||
name="user"
|
||||
onChange={setUser}
|
||||
placeholder="john_doe"
|
||||
/>
|
||||
{isInvalid && (
|
||||
<p className="mt-2 text-sm text-red-500">
|
||||
The user you entered already exists in the list of allowed users.
|
||||
</p>
|
||||
)}
|
||||
</DialogPanel>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user