chore: add change role to the user's invitations

This commit is contained in:
Pablo Lara
2024-12-13 12:38:08 +01:00
parent 858c04b0b0
commit 6a5bc75252
6 changed files with 175 additions and 69 deletions
+25 -11
View File
@@ -104,9 +104,33 @@ export const updateInvite = async (formData: FormData) => {
const invitationId = formData.get("invitationId");
const invitationEmail = formData.get("invitationEmail");
const expiresAt = formData.get("expires_at");
const role = formData.get("role");
const url = new URL(`${keyServer}/tenants/invitations/${invitationId}`);
const body = JSON.stringify({
data: {
type: "invitations",
id: invitationId,
attributes: {
email: invitationEmail,
...(expiresAt && { expires_at: expiresAt }),
},
relationships: {
roles: {
data: role
? [
{
id: role,
type: "role",
},
]
: [],
},
},
},
});
try {
const response = await fetch(url.toString(), {
method: "PATCH",
@@ -115,22 +139,12 @@ export const updateInvite = async (formData: FormData) => {
Accept: "application/vnd.api+json",
Authorization: `Bearer ${session?.accessToken}`,
},
body: JSON.stringify({
data: {
type: "invitations",
id: invitationId,
attributes: {
email: invitationEmail,
...(expiresAt && { expires_at: expiresAt }),
},
},
}),
body,
});
const data = await response.json();
revalidatePath("/invitations");
return parseStringify(data);
} catch (error) {
// eslint-disable-next-line no-console
console.error("Error updating invitation:", error);
return {
error: getErrorMessage(error),
+37 -3
View File
@@ -1,7 +1,8 @@
import { Spacer } from "@nextui-org/react";
import { Suspense } from "react";
import React, { Suspense } from "react";
import { getInvitations } from "@/actions/invitations/invitation";
import { getRoles } from "@/actions/roles";
import { FilterControls } from "@/components/filters";
import { filterInvitations } from "@/components/filters/data-filters";
import { SendInvitationButton } from "@/components/invitations";
@@ -11,7 +12,7 @@ import {
} from "@/components/invitations/table";
import { Header } from "@/components/ui";
import { DataTable, DataTableFilterCustom } from "@/components/ui/table";
import { SearchParamsProps } from "@/types";
import { InvitationProps, Role, SearchParamsProps } from "@/types";
export default async function Invitations({
searchParams,
@@ -54,12 +55,45 @@ const SSRDataTable = async ({
// Extract query from filters
const query = (filters["filter[search]"] as string) || "";
// Fetch invitations and roles
const invitationsData = await getInvitations({ query, page, sort, filters });
const rolesData = await getRoles({});
// Create a dictionary for roles by invitation ID
const roleDict = rolesData?.data?.reduce(
(acc: Record<string, Role>, role: Role) => {
role.relationships.invitations.data.forEach((invitation: any) => {
acc[invitation.id] = role;
});
return acc;
},
{},
);
// Expand invitations with role information
const expandedInvitations = invitationsData?.data?.map(
(invitation: InvitationProps) => {
const role = roleDict[invitation.id];
return {
...invitation,
relationships: {
...invitation.relationships,
role,
},
};
},
);
// Create the expanded response
const expandedResponse = {
...invitationsData,
data: expandedInvitations,
};
return (
<DataTable
columns={ColumnsInvitation}
data={invitationsData?.data || []}
data={expandedResponse?.data || []}
metadata={invitationsData?.meta}
/>
);
+35 -5
View File
@@ -1,8 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { Select, SelectItem } from "@nextui-org/react";
import { Dispatch, SetStateAction } from "react";
import { useForm } from "react-hook-form";
import { Controller, useForm } from "react-hook-form";
import * as z from "zod";
import { updateInvite } from "@/actions/invitations/invitation";
@@ -15,10 +14,14 @@ import { editInviteFormSchema } from "@/types";
export const EditForm = ({
invitationId,
invitationEmail,
roles = [],
defaultRole = "",
setIsOpen,
}: {
invitationId: string;
invitationEmail?: string;
roles: Array<{ id: string; name: string }>;
defaultRole?: string;
setIsOpen: Dispatch<SetStateAction<boolean>>;
}) => {
const formSchema = editInviteFormSchema;
@@ -27,7 +30,8 @@ export const EditForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
invitationId,
invitationEmail: invitationEmail,
invitationEmail: invitationEmail || "",
role: defaultRole,
},
});
@@ -37,7 +41,6 @@ export const EditForm = ({
const onSubmitClient = async (values: z.infer<typeof formSchema>) => {
const formData = new FormData();
console.log(values);
Object.entries(values).forEach(
([key, value]) => value !== undefined && formData.append(key, value),
@@ -83,6 +86,33 @@ export const EditForm = ({
isInvalid={!!form.formState.errors.invitationEmail}
/>
</div>
<div>
<Controller
name="role"
control={form.control}
render={({ field }) => (
<Select
{...field}
label="Role"
placeholder="Select a role"
variant="bordered"
selectedKeys={[field.value]}
onSelectionChange={(selected) =>
field.onChange(selected?.currentKey || "")
}
>
{roles.map((role) => (
<SelectItem key={role.id}>{role.name}</SelectItem>
))}
</Select>
)}
/>
{form.formState.errors.role && (
<p className="mt-2 text-sm text-red-600">
{form.formState.errors.role.message}
</p>
)}
</div>
<input type="hidden" name="invitationId" value={invitationId} />
<div className="flex w-full justify-center sm:space-x-6">
@@ -31,6 +31,15 @@ export const ColumnsInvitation: ColumnDef<InvitationProps>[] = [
return <p className="font-semibold">{state}</p>;
},
},
{
accessorKey: "role",
header: () => <div className="text-left">Role</div>,
cell: ({ row }) => {
const roleName =
row.original.relationships?.role?.attributes?.name || "No Role";
return <p className="font-semibold">{roleName}</p>;
},
},
{
accessorKey: "inserted_at",
header: ({ column }) => (
@@ -60,7 +69,6 @@ export const ColumnsInvitation: ColumnDef<InvitationProps>[] = [
return <DateWithTime dateTime={expires_at} showTime={false} />;
},
},
{
accessorKey: "actions",
header: () => <div className="text-right">Actions</div>,
+68 -49
View File
@@ -222,6 +222,72 @@ export interface InvitationProps {
id: string;
};
};
role?: {
data: {
type: "roles";
id: string;
};
attributes?: {
name: string;
manage_users?: boolean;
manage_account?: boolean;
manage_billing?: boolean;
manage_providers?: boolean;
manage_integrations?: boolean;
manage_scans?: boolean;
permission_state?: "unlimited" | "limited" | "none";
};
};
};
links: {
self: string;
};
}
export interface Role {
type: "role";
id: string;
attributes: {
name: string;
manage_users: boolean;
manage_account: boolean;
manage_billing: boolean;
manage_providers: boolean;
manage_integrations: boolean;
manage_scans: boolean;
unlimited_visibility: boolean;
permission_state: "unlimited" | "limited" | "none";
inserted_at: string;
updated_at: string;
};
relationships: {
provider_groups: {
meta: {
count: number;
};
data: {
type: string;
id: string;
}[];
};
users: {
meta: {
count: number;
};
data: {
type: string;
id: string;
}[];
};
invitations: {
meta: {
count: number;
};
data: {
type: string;
id: string;
}[];
};
};
links: {
self: string;
@@ -235,55 +301,7 @@ export interface RolesProps {
next: string | null;
prev: string | null;
};
data: {
type: "role";
id: string;
attributes: {
name: string;
manage_users: boolean;
manage_account: boolean;
manage_billing: boolean;
manage_providers: boolean;
manage_integrations: boolean;
manage_scans: boolean;
unlimited_visibility: boolean;
permission_state: "unlimited" | "limited" | "none";
inserted_at: string;
updated_at: string;
};
relationships: {
provider_groups: {
meta: {
count: number;
};
data: {
type: string;
id: string;
}[];
};
users: {
meta: {
count: number;
};
data: {
type: string;
id: string;
}[];
};
invitations: {
meta: {
count: number;
};
data: {
type: string;
id: string;
}[];
};
};
links: {
self: string;
};
}[];
data: Role[];
meta: {
pagination: {
page: number;
@@ -293,6 +311,7 @@ export interface RolesProps {
version: string;
};
}
export interface UserProfileProps {
data: {
type: "users";
+1
View File
@@ -180,6 +180,7 @@ export const editInviteFormSchema = z.object({
invitationId: z.string().uuid(),
invitationEmail: z.string().email(),
expires_at: z.string().optional(),
role: z.string().optional(),
});
export const editUserFormSchema = () =>