From 0290b837f20f9c5447d893cafa3af16070041d32 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Wed, 13 Nov 2024 14:31:33 +0100 Subject: [PATCH] feat: user table is working as expected --- actions/providers/providers.ts | 1 - actions/users.ts | 39 ------ actions/users/users.ts | 116 ++++++++++++++++++ app/(prowler)/providers/page.tsx | 2 +- app/(prowler)/users/page.tsx | 53 ++++---- app/api/users/route.ts | 10 -- components/users/forms/delete-form.tsx | 2 - .../users/table/data-table-row-actions.tsx | 2 +- types/components.ts | 22 ++++ types/formSchemas.ts | 34 +++++ 10 files changed, 202 insertions(+), 79 deletions(-) delete mode 100644 actions/users.ts create mode 100644 actions/users/users.ts delete mode 100644 app/api/users/route.ts diff --git a/actions/providers/providers.ts b/actions/providers/providers.ts index cf079892b4..29a8a114c7 100644 --- a/actions/providers/providers.ts +++ b/actions/providers/providers.ts @@ -307,7 +307,6 @@ export const deleteProvider = async (formData: FormData) => { const response = await fetch(url.toString(), { method: "DELETE", headers: { - Accept: "application/vnd.api+json", Authorization: `Bearer ${session?.accessToken}`, }, }); diff --git a/actions/users.ts b/actions/users.ts deleted file mode 100644 index c4fa97b981..0000000000 --- a/actions/users.ts +++ /dev/null @@ -1,39 +0,0 @@ -"use server"; - -import { revalidatePath } from "next/cache"; -import { redirect } from "next/navigation"; - -import { parseStringify } from "@/lib"; - -export const getUsers = async ({ page = 1 }) => { - if (isNaN(Number(page)) || page < 1) redirect("/users"); - const keyServer = process.env.SITE_URL; - - try { - const users = await fetch( - `${keyServer}/api/users?page%5Bnumber%5D=${page}`, - ); - const data = await users.json(); - const parsedData = parseStringify(data); - revalidatePath("/users"); - return parsedData; - } catch (error) { - console.error("Error fetching Users:", error); - return undefined; - } -}; - -export const getErrorMessage = (error: unknown): string => { - let message: string; - - if (error instanceof Error) { - message = error.message; - } else if (error && typeof error === "object" && "message" in error) { - message = String(error.message); - } else if (typeof error === "string") { - message = error; - } else { - message = "Oops! Something went wrong."; - } - return message; -}; diff --git a/actions/users/users.ts b/actions/users/users.ts new file mode 100644 index 0000000000..903d0be120 --- /dev/null +++ b/actions/users/users.ts @@ -0,0 +1,116 @@ +"use server"; + +import { revalidatePath } from "next/cache"; +import { redirect } from "next/navigation"; + +import { auth } from "@/auth.config"; +import { getErrorMessage, parseStringify, wait } from "@/lib"; + +export const getUsers = async ({ + page = 1, + query = "", + sort = "", + filters = {}, +}) => { + const session = await auth(); + + if (isNaN(Number(page)) || page < 1) redirect("/users"); + + const keyServer = process.env.API_BASE_URL; + const url = new URL(`${keyServer}/users`); + + if (page) url.searchParams.append("page[number]", page.toString()); + if (query) url.searchParams.append("filter[search]", query); + if (sort) url.searchParams.append("sort", sort); + + // Handle multiple filters + Object.entries(filters).forEach(([key, value]) => { + if (key !== "filter[search]") { + url.searchParams.append(key, String(value)); + } + }); + + try { + const users = await fetch(url.toString(), { + headers: { + Accept: "application/vnd.api+json", + Authorization: `Bearer ${session?.accessToken}`, + }, + }); + const data = await users.json(); + const parsedData = parseStringify(data); + revalidatePath("/users"); + return parsedData; + } catch (error) { + console.error("Error fetching users:", error); + return undefined; + } +}; + +export const updateUser = async (formData: FormData) => { + const session = await auth(); + const keyServer = process.env.API_BASE_URL; + + const userId = formData.get("userId"); + const userName = formData.get("name"); + const userPassword = formData.get("password"); + const userEmail = formData.get("email"); + const userCompanyName = formData.get("company_name"); + + const url = new URL(`${keyServer}/users/${userId}`); + + try { + const response = await fetch(url.toString(), { + method: "PATCH", + headers: { + "Content-Type": "application/vnd.api+json", + Accept: "application/vnd.api+json", + Authorization: `Bearer ${session?.accessToken}`, + }, + body: JSON.stringify({ + data: { + type: "User", + id: userId, + attributes: { + name: userName, + password: userPassword, + email: userEmail, + company_name: userCompanyName, + }, + }, + }), + }); + const data = await response.json(); + revalidatePath("/users"); + return parseStringify(data); + } catch (error) { + console.error(error); + return { + error: getErrorMessage(error), + }; + } +}; + +export const deleteUser = async (formData: FormData) => { + const session = await auth(); + const keyServer = process.env.API_BASE_URL; + + const userId = formData.get("userId"); + const url = new URL(`${keyServer}/users/${userId}`); + try { + const response = await fetch(url.toString(), { + method: "DELETE", + headers: { + Authorization: `Bearer ${session?.accessToken}`, + }, + }); + const data = await response.json(); + await wait(1000); + revalidatePath("/users"); + return parseStringify(data); + } catch (error) { + return { + error: getErrorMessage(error), + }; + } +}; diff --git a/app/(prowler)/providers/page.tsx b/app/(prowler)/providers/page.tsx index 177b403745..3e70554f0c 100644 --- a/app/(prowler)/providers/page.tsx +++ b/app/(prowler)/providers/page.tsx @@ -53,7 +53,7 @@ const SSRDataTable = async ({ const query = (filters["filter[search]"] as string) || ""; const providersData = await getProviders({ query, page, sort, filters }); - + console.log(providersData, "since data table"); return ( -
+
-
-
- -
- - }> - - -
+ + + {/* */} + + + }> + + ); } @@ -43,16 +38,24 @@ const SSRDataTable = async ({ searchParams: SearchParamsProps; }) => { const page = parseInt(searchParams.page?.toString() || "1", 10); - const usersData = await getUsers({ page }); - const [users] = await Promise.all([usersData]); + const sort = searchParams.sort?.toString(); - if (users?.errors) redirect("/users"); + // Extract all filter parameters + const filters = Object.fromEntries( + Object.entries(searchParams).filter(([key]) => key.startsWith("filter[")), + ); + + // Extract query from filters + const query = (filters["filter[search]"] as string) || ""; + + const usersData = await getUsers({ query, page, sort, filters }); return ( - ); }; diff --git a/app/api/users/route.ts b/app/api/users/route.ts deleted file mode 100644 index 9905b5c8c9..0000000000 --- a/app/api/users/route.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { NextResponse } from "next/server"; - -import data from "../../../dataUsers.json"; - -export async function GET() { - // Simulate fetching data with a delay - await new Promise((resolve) => setTimeout(resolve, 2000)); - - return NextResponse.json({ users: data }); -} diff --git a/components/users/forms/delete-form.tsx b/components/users/forms/delete-form.tsx index 36843534b9..7128672903 100644 --- a/components/users/forms/delete-form.tsx +++ b/components/users/forms/delete-form.tsx @@ -37,8 +37,6 @@ export const DeleteForm = ({ Object.entries(values).forEach( ([key, value]) => value !== undefined && formData.append(key, value), ); - - console.log(formData); // client-side validation const data = await deleteUser(formData); diff --git a/components/users/table/data-table-row-actions.tsx b/components/users/table/data-table-row-actions.tsx index 79825bf168..07a00cd6a7 100644 --- a/components/users/table/data-table-row-actions.tsx +++ b/components/users/table/data-table-row-actions.tsx @@ -80,7 +80,7 @@ export function DataTableRowActions({ description="Allows you to edit the user" textValue="Edit User" startContent={} - // onClick={() => setIsEditOpen(true)} + onClick={() => setIsEditOpen(true)} > Edit User diff --git a/types/components.ts b/types/components.ts index fb78990811..d753bac9d9 100644 --- a/types/components.ts +++ b/types/components.ts @@ -101,6 +101,28 @@ export interface ApiError { code: string; } +export interface UserProps { + type: "User"; + id: string; + attributes: { + name: string; + email: string; + company_name: string; + date_joined: string; + }; + relationships: { + memberships: { + meta: { + count: number; + }; + data: Array<{ + type: "Membership"; + id: string; + }>; + }; + }; +} + export interface ProviderProps { id: string; type: "providers"; diff --git a/types/formSchemas.ts b/types/formSchemas.ts index a90ffc8c02..333064cfa4 100644 --- a/types/formSchemas.ts +++ b/types/formSchemas.ts @@ -150,3 +150,37 @@ export const editProviderFormSchema = (currentAlias: string) => .optional(), providerId: z.string(), }); + +export const editUserFormSchema = ( + currentName: string, + currentEmail: string, + currentCompanyName: string, +) => + z.object({ + name: z + .string() + .min(3, { message: "The name must have at least 3 characters." }) + .max(150, { message: "The name cannot exceed 150 characters." }) + .refine((val) => val !== currentName, { + message: "The new name must be different from the current one.", + }) + .optional(), + email: z + .string() + .email({ message: "Please enter a valid email address." }) + .refine((val) => val !== currentEmail, { + message: "The new email must be different from the current one.", + }) + .optional(), + password: z + .string() + .min(1, { message: "The password cannot be empty." }) + .optional(), + company_name: z + .string() + .refine((val) => val !== currentCompanyName, { + message: "The new company name must be different from the current one.", + }) + .optional(), + userId: z.string(), + });