feat: user table is working as expected

This commit is contained in:
Pablo Lara
2024-11-13 14:31:33 +01:00
parent 833bf0520c
commit 0290b837f2
10 changed files with 202 additions and 79 deletions
-1
View File
@@ -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}`,
},
});
-39
View File
@@ -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;
};
+116
View File
@@ -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),
};
}
};
+1 -1
View File
@@ -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 (
<DataTable
columns={ColumnProviders}
+28 -25
View File
@@ -1,16 +1,12 @@
import { Spacer } from "@nextui-org/react";
import { redirect } from "next/navigation";
import { Suspense } from "react";
import { getUsers } from "@/actions/users";
// import { auth } from "@/auth.config";
import { getUsers } from "@/actions/users/users";
import { FilterControls } from "@/components/filters";
import { filterUsers } from "@/components/filters/data-filters";
import { Header } from "@/components/ui";
import {
AddUserModal,
ColumnsUser,
DataTableUser,
SkeletonTableUser,
} from "@/components/users";
import { DataTable } from "@/components/ui/table";
import { ColumnsUser, SkeletonTableUser } from "@/components/users/table";
import { SearchParamsProps } from "@/types";
export default async function Users({
@@ -22,17 +18,16 @@ export default async function Users({
return (
<>
<Header title="User Management" icon="ci:users" />
<Header title="Users" icon="ci:users" />
<Spacer y={4} />
<div className="flex w-full flex-col items-end">
<div className="flex space-x-6">
<AddUserModal />
</div>
<Spacer y={6} />
<Suspense key={searchParamsKey} fallback={<SkeletonTableUser />}>
<SSRDataTable searchParams={searchParams} />
</Suspense>
</div>
<FilterControls search />
<Spacer y={4} />
{/* <AddUser /> */}
<Spacer y={4} />
<Suspense key={searchParamsKey} fallback={<SkeletonTableUser />}>
<SSRDataTable searchParams={searchParams} />
</Suspense>
</>
);
}
@@ -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 (
<DataTableUser
<DataTable
columns={ColumnsUser}
data={users?.users?.data ?? []}
metadata={users?.meta}
data={usersData?.data || []}
metadata={usersData?.meta}
customFilters={filterUsers}
/>
);
};
-10
View File
@@ -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 });
}
-2
View File
@@ -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);
@@ -80,7 +80,7 @@ export function DataTableRowActions<ProviderProps>({
description="Allows you to edit the user"
textValue="Edit User"
startContent={<EditDocumentBulkIcon className={iconClasses} />}
// onClick={() => setIsEditOpen(true)}
onClick={() => setIsEditOpen(true)}
>
Edit User
</DropdownItem>
+22
View File
@@ -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";
+34
View File
@@ -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(),
});