Add new user and role selectors for pagination

This commit is contained in:
Owen
2026-05-05 20:53:36 -07:00
parent 51f1693dbd
commit a8f4d2b7d1
4 changed files with 42 additions and 1007 deletions

View File

@@ -6,11 +6,9 @@ import { useEnvContext } from "@app/hooks/useEnvContext";
import { useOrgContext } from "@app/hooks/useOrgContext"; import { useOrgContext } from "@app/hooks/useOrgContext";
import { usePaidStatus } from "@app/hooks/usePaidStatus"; import { usePaidStatus } from "@app/hooks/usePaidStatus";
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
import { orgQueries } from "@app/lib/queries"; import { orgQueries } from "@app/lib/queries";
import { build } from "@server/build"; import { build } from "@server/build";
import { tierMatrix } from "@server/lib/billing/tierMatrix"; import { tierMatrix } from "@server/lib/billing/tierMatrix";
import { UserType } from "@server/types/UserTypes";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
@@ -51,12 +49,6 @@ export function EditPolicyForm({
env.server.maxmind_asn_path && env.server.maxmind_asn_path.length > 0 env.server.maxmind_asn_path && env.server.maxmind_asn_path.length > 0
); );
const { data: orgRoles = [], isLoading: isLoadingOrgRoles } = useQuery(
orgQueries.roles({ orgId: org.org.orgId })
);
const { data: orgUsers = [], isLoading: isLoadingOrgUsers } = useQuery(
orgQueries.users({ orgId: org.org.orgId })
);
const { data: orgIdps = [], isLoading: isLoadingOrgIdps } = useQuery( const { data: orgIdps = [], isLoading: isLoadingOrgIdps } = useQuery(
orgQueries.identityProviders({ orgQueries.identityProviders({
orgId: org.org.orgId, orgId: org.org.orgId,
@@ -64,26 +56,6 @@ export function EditPolicyForm({
}) })
); );
const allRoles = useMemo(
() =>
orgRoles
.map((role) => ({
id: role.roleId.toString(),
text: role.name
}))
.filter((role) => role.text !== "Admin"),
[orgRoles]
);
const allUsers = useMemo(
() =>
orgUsers.map((user) => ({
id: user.id.toString(),
text: `${getUserDisplayName({ email: user.email, username: user.username })}${user.type !== UserType.Internal ? ` (${user.idpName})` : ""}`
})),
[orgUsers]
);
const allIdps = useMemo(() => { const allIdps = useMemo(() => {
if (build === "saas") { if (build === "saas") {
if (isPaidUser(tierMatrix.orgOidc)) { if (isPaidUser(tierMatrix.orgOidc)) {
@@ -98,17 +70,18 @@ export function EditPolicyForm({
return []; return [];
}, [orgIdps, isPaidUser]); }, [orgIdps, isPaidUser]);
if (isLoadingOrgRoles || isLoadingOrgUsers || isLoadingOrgIdps) { if (isLoadingOrgIdps) {
return <></>; return <></>;
} }
return ( return (
<SettingsContainer> <SettingsContainer>
{!hidePolicyNameForm && <EditPolicyNameSectionForm readonly={readonly} />} {!hidePolicyNameForm && (
<EditPolicyNameSectionForm readonly={readonly} />
)}
<EditPolicyUsersRolesSectionForm <EditPolicyUsersRolesSectionForm
allRoles={allRoles} orgId={org.org.orgId}
allUsers={allUsers}
allIdps={allIdps} allIdps={allIdps}
readonly={readonly} readonly={readonly}
/> />

View File

@@ -23,8 +23,9 @@ import type { AxiosResponse } from "axios";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { createPolicySchema } from "."; import { createPolicySchema } from ".";
import { RolesSelector } from "@app/components/roles-selector";
import { UsersSelector } from "@app/components/users-selector";
import { SwitchInput } from "@app/components/SwitchInput"; import { SwitchInput } from "@app/components/SwitchInput";
import { Tag, TagInput } from "@app/components/tags/tag-input";
import { Button } from "@app/components/ui/button"; import { Button } from "@app/components/ui/button";
import { import {
Form, Form,
@@ -50,15 +51,13 @@ import { useForm, useWatch } from "react-hook-form";
// ─── PolicyUsersRolesSection ────────────────────────────────────────────────── // ─── PolicyUsersRolesSection ──────────────────────────────────────────────────
type PolicyUsersRolesSectionProps = { type PolicyUsersRolesSectionProps = {
allRoles: { id: string; text: string }[]; orgId: string;
allUsers: { id: string; text: string }[];
allIdps: { id: number; text: string }[]; allIdps: { id: number; text: string }[];
readonly?: boolean; readonly?: boolean;
}; };
export function EditPolicyUsersRolesSectionForm({ export function EditPolicyUsersRolesSectionForm({
allRoles, orgId,
allUsers,
allIdps, allIdps,
readonly readonly
}: PolicyUsersRolesSectionProps) { }: PolicyUsersRolesSectionProps) {
@@ -98,12 +97,6 @@ export function EditPolicyUsersRolesSectionForm({
control: form.control, control: form.control,
name: "skipToIdpId" name: "skipToIdpId"
}); });
const [activeRolesTagIndex, setActiveRolesTagIndex] = useState<
number | null
>(null);
const [activeUsersTagIndex, setActiveUsersTagIndex] = useState<
number | null
>(null);
const [, formAction, isSubmitting] = useActionState(onSubmit, null); const [, formAction, isSubmitting] = useActionState(onSubmit, null);
@@ -190,43 +183,21 @@ export function EditPolicyUsersRolesSectionForm({
{t("roles")} {t("roles")}
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<TagInput <RolesSelector
{...field} orgId={orgId}
activeTagIndex={ selectedRoles={
activeRolesTagIndex field.value
} }
setActiveTagIndex={ onSelectRoles={(
setActiveRolesTagIndex roles
} ) =>
placeholder={t(
"accessRoleSelect2"
)}
size="sm"
tags={
form.getValues()
.roles
}
setTags={(newRoles) => {
form.setValue( form.setValue(
"roles", "roles",
newRoles as [ roles
Tag, )
...Tag[]
]
);
}}
enableAutocomplete={
true
} }
autocompleteOptions={
allRoles
}
allowDuplicates={false}
restrictTagsToAutocompleteOptions={
true
}
sortTags={true}
disabled={readonly} disabled={readonly}
restrictAdminRole
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -247,42 +218,19 @@ export function EditPolicyUsersRolesSectionForm({
{t("users")} {t("users")}
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<TagInput <UsersSelector
{...field} orgId={orgId}
activeTagIndex={ selectedUsers={
activeUsersTagIndex field.value
} }
setActiveTagIndex={ onSelectUsers={(
setActiveUsersTagIndex users
} ) =>
placeholder={t(
"accessUserSelect"
)}
size="sm"
tags={
form.getValues()
.users
}
setTags={(newUsers) => {
form.setValue( form.setValue(
"users", "users",
newUsers as [ users
Tag, )
...Tag[]
]
);
}}
enableAutocomplete={
true
} }
autocompleteOptions={
allUsers
}
allowDuplicates={false}
restrictTagsToAutocompleteOptions={
true
}
sortTags={true}
disabled={readonly} disabled={readonly}
/> />
</FormControl> </FormControl>

View File

@@ -18,12 +18,14 @@ export type UsersSelectorProps = {
orgId: string; orgId: string;
selectedUsers?: SelectedUser[]; selectedUsers?: SelectedUser[];
onSelectUsers: (users: SelectedUser[]) => void; onSelectUsers: (users: SelectedUser[]) => void;
disabled?: boolean;
}; };
export function UsersSelector({ export function UsersSelector({
orgId, orgId,
selectedUsers = [], selectedUsers = [],
onSelectUsers onSelectUsers,
disabled
}: UsersSelectorProps) { }: UsersSelectorProps) {
const t = useTranslations(); const t = useTranslations();
const [userSearchQuery, setUserSearchQuery] = useState(""); const [userSearchQuery, setUserSearchQuery] = useState("");
@@ -58,6 +60,7 @@ export function UsersSelector({
options={usersShown} options={usersShown}
value={selectedUsers} value={selectedUsers}
onChange={onSelectUsers} onChange={onSelectUsers}
disabled={disabled}
/> />
); );
} }