mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
chore: add change role to the user's invitations
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -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
@@ -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";
|
||||
|
||||
@@ -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 = () =>
|
||||
|
||||
Reference in New Issue
Block a user