Skip to content

Commit

Permalink
modify footer
Browse files Browse the repository at this point in the history
  • Loading branch information
devhasibulislam committed Jan 20, 2024
1 parent 4117d12 commit 5a69819
Show file tree
Hide file tree
Showing 19 changed files with 589 additions and 2,030 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"use client";

import Inform from "@/components/icons/Inform";
import View from "@/components/icons/View";
import Modal from "@/components/shared/Modal";
import Dashboard from "@/components/shared/layouts/Dashboard";
import { setPurchases } from "@/features/purchase/purchaseSlice";
import {
Expand All @@ -24,15 +26,26 @@ import {
} from "@/services/purchase/purchaseApi";
import Image from "next/image";
import Link from "next/link";
import React, { useEffect, useMemo } from "react";
import React, { useEffect, useMemo, useState } from "react";
import { toast } from "react-hot-toast";
import { useDispatch } from "react-redux";

const Page = () => {
const { isLoading, data, error } = useGetAllPurchasesQuery();
const purchases = useMemo(() => data?.data || [], [data]);
const [filter, setFilter] = useState("all");
const dispatch = useDispatch();

const filteredPurchases = useMemo(() => {
if (filter === "all") {
return purchases;
} else if (filter === "pending") {
return purchases.filter((purchase) => purchase?.status === "pending");
} else if (filter === "delivered") {
return purchases.filter((purchase) => purchase?.status === "delivered");
}
}, [purchases, filter]);

useEffect(() => {
if (isLoading) {
toast.loading("Fetching Purchases...", { id: "purchases" });
Expand All @@ -51,14 +64,43 @@ const Page = () => {

return (
<Dashboard>
{purchases?.length === 0 ? (
{filteredPurchases?.length === 0 ? (
<p className="text-sm flex flex-row gap-x-1 items-center justify-center">
<Inform /> No Purchases Found!
</p>
) : (
<section className="w-full h-full">
<section className="w-full h-full flex flex-col gap-y-6">
<div className="flex flex-row gap-x-2">
<button
type="button"
className={`text-sm bg-purple-50 border border-purple-900 rounded-secondary text-purple-900 px-4 py-1 ${
filter === "all" && "bg-purple-900 !text-white"
}`}
onClick={() => setFilter("all")}
>
All
</button>
<button
type="button"
className={`text-sm bg-red-50 border border-red-900 rounded-secondary text-red-900 px-4 py-1 ${
filter === "pending" && "bg-red-900 !text-white"
}`}
onClick={() => setFilter("pending")}
>
Pending
</button>
<button
type="button"
className={`text-sm bg-green-50 border border-green-900 rounded-secondary text-green-900 px-4 py-1 ${
filter === "delivered" && "bg-green-900 !text-white"
}`}
onClick={() => setFilter("delivered")}
>
Delivered
</button>
</div>
<div className="overflow-x-auto w-full">
<table className="min-w-full divide-y divide-gray-200">
<table className="w-full divide-y divide-gray-200">
<thead className="bg-slate-100">
<tr>
<th
Expand Down Expand Up @@ -103,10 +145,16 @@ const Page = () => {
>
Action
</th>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-bold text-gray-500 uppercase whitespace-nowrap"
>
<span className="sr-only">Action</span>
</th>
</tr>
</thead>
<tbody>
{purchases.map((purchase) => (
{filteredPurchases.map((purchase) => (
<tr
key={purchase?._id}
className="odd:bg-white even:bg-gray-100 hover:odd:bg-gray-100"
Expand Down Expand Up @@ -158,6 +206,9 @@ const Page = () => {
status={purchase?.status}
/>
</td>
<td className="px-6 py-4">
<ViewProducts products={purchase?.products} />
</td>
</tr>
))}
</tbody>
Expand Down Expand Up @@ -201,4 +252,52 @@ function ModifyStatus({ id, status }) {
);
}

function ViewProducts({ products }) {
const [isOpen, setIsOpen] = useState(false);

return (
<>
<button
type="button"
className="text-sm bg-cyan-50 border border-cyan-900 rounded-secondary text-cyan-900 p-1"
onClick={() => setIsOpen(!isOpen)}
>
<View />
</button>

{isOpen && (
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
className="p-6 lg:w-1/3 md:w-3/4 w-full max-h-96 overflow-y-auto scrollbar-hide"
>
<div className="flex flex-col gap-y-4">
{products?.map(({ product, quantity, _id }) => (
<div key={_id} className="flex flex-row gap-x-2 items-start">
<Image
src={product?.thumbnail?.url}
alt={product?.thumbnail?.public_id}
height={40}
width={40}
className="w-[40px] h-[40px] object-cover rounded"
/>
<div className="flex flex-col">
<p className="text-base line-clamp-1 font-semibold">
{product?.title}
</p>
<p className="text-sm line-clamp-2">{product?.summary}</p>
<p className="text-xs mt-2">
QTY: {quantity} • Price: ${product?.price} • Total Price: $
{product?.price * quantity}
</p>
</div>
</div>
))}
</div>
</Modal>
)}
</>
);
}

export default Page;
202 changes: 202 additions & 0 deletions client/src/app/dashboard/admin/(account-manager)/list-users/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/**
* Title: Write a program using JavaScript on Page
* Author: Hasibul Islam
* Portfolio: https://devhasibulislam.vercel.app
* Linkedin: https://linkedin.com/in/devhasibulislam
* GitHub: https://github.com/devhasibulislam
* Facebook: https://facebook.com/devhasibulislam
* Instagram: https://instagram.com/devhasibulislam
* Twitter: https://twitter.com/devhasibulislam
* Pinterest: https://pinterest.com/devhasibulislam
* WhatsApp: https://wa.me/8801906315901
* Telegram: devhasibulislam
* Date: 20, January 2024
*/

"use client";

import Cross from "@/components/icons/Cross";
import Trash from "@/components/icons/Trash";
import Dashboard from "@/components/shared/layouts/Dashboard";
import {
useDeleteUserMutation,
useGetUsersQuery,
useReviewSellerMutation,
} from "@/services/user/userApi";
import Image from "next/image";
import React, { useEffect, useMemo, useState } from "react";
import { toast } from "react-hot-toast";

const Page = () => {
const { isLoading, data, error } = useGetUsersQuery();
const users = useMemo(() => data?.data || [], [data]);
const [filter, setFilter] = useState("all");

const filteredUsers = useMemo(
() => users.filter((user) => user?.role === filter || filter === "all"),
[users, filter]
);

useEffect(() => {
if (isLoading) {
toast.loading("Fetching Users...", { id: "allUsers" });
}
if (data) {
toast.success(data?.description, { id: "allUsers" });
}
if (error?.data) {
toast.error(error?.data?.description, { id: "allUsers" });
}
}, [isLoading, data, error]);

return (
<Dashboard>
<section className="flex flex-col gap-y-6">
<div className="flex flex-row gap-x-2">
<button
type="button"
className={`bg-purple-50 border border-purple-900 rounded-secondary text-purple-900 px-4 py-1 text-xs ${
filter === "all" && "bg-purple-900 !text-white"
}`}
onClick={() => setFilter("all")}
>
All
</button>
<button
type="button"
className={`bg-teal-50 border border-teal-900 rounded-secondary text-teal-900 px-4 py-1 text-xs ${
filter === "admin" && "bg-teal-900 !text-white"
}`}
onClick={() => setFilter("admin")}
>
Admin
</button>
<button
type="button"
className={`bg-indigo-50 border border-indigo-900 rounded-secondary text-indigo-900 px-4 py-1 text-xs ${
filter === "buyer" && "bg-indigo-900 !text-white"
}`}
onClick={() => setFilter("buyer")}
>
Buyer
</button>
<button
type="button"
className={`bg-cyan-50 border border-cyan-900 rounded-secondary text-cyan-900 px-4 py-1 text-xs ${
filter === "seller" && "bg-cyan-900 !text-white"
}`}
onClick={() => setFilter("seller")}
>
Seller
</button>
</div>
<div className="grid grid-cols-3 gap-4">
{filteredUsers?.map((user) => (
<div
key={user?._id}
className="flex flex-col gap-y-2 border rounded p-4 relative group cursor-default"
>
<Image
src={user?.avatar?.url}
alt={user?.avatar?.public_id}
width={50}
height={50}
className="h-[50px] w-[50px] rounded-secondary object-cover"
/>
<div className="flex flex-col gap-y-0.5">
<h2 className="truncate">{user?.name}</h2>
<p className="truncate text-sm">{user?.email}</p>
<p className="truncate text-xs">{user?.phone}</p>
</div>
{user?.role === "buyer" && (
<span className="bg-indigo-50 border border-indigo-900 px-2 rounded-secondary text-indigo-900 text-xs uppercase w-fit">
{user?.role}
</span>
)}
{user?.role === "seller" && (
<span className="bg-cyan-50 border border-cyan-900 px-2 rounded-secondary text-cyan-900 text-xs uppercase w-fit">
{user?.role}
</span>
)}
{user?.role === "admin" && (
<span className="bg-purple-50 border border-purple-900 px-2 rounded-secondary text-purple-900 text-xs uppercase w-fit">
{user?.role}
</span>
)}

<Disapprove id={user?._id} role={user?.role} />
<DeleteUser id={user?._id} role={user?.role} />
</div>
))}
</div>
</section>
</Dashboard>
);
};

function DeleteUser({ id, role }) {
const [deleteUser, { isLoading, data, error }] = useDeleteUserMutation();

useEffect(() => {
if (isLoading) {
toast.loading("Deleting User...", { id: "deleteUser" });
}
if (data) {
toast.success(data?.description, { id: "deleteUser" });
}
if (error?.data) {
toast.error(error?.data?.description, { id: "deleteUser" });
}
}, [isLoading, data, error]);

return (
<>
{!(role === "admin" || role === "seller") && (
<button
type="button"
className="bg-red-50 border border-red-900 p-0.5 rounded-secondary text-red-900 absolute top-2 right-2 group-hover:opacity-100 opacity-0 transition-opacity"
onClick={() => deleteUser(id)}
title="Delete User from DB"
>
<Trash />
</button>
)}
</>
);
}

function Disapprove({ id, role }) {
const [disapproveSeller, { isLoading, data, error }] =
useReviewSellerMutation();

useEffect(() => {
if (isLoading) {
toast.loading("Deleting User...", { id: "disapproveSeller" });
}
if (data) {
toast.success(data?.description, { id: "disapproveSeller" });
}
if (error?.data) {
toast.error(error?.data?.description, { id: "disapproveSeller" });
}
}, [isLoading, data, error]);

return (
<>
{!(role === "admin" || role === "buyer") && (
<button
type="button"
className="bg-red-50 border border-red-900 p-0.5 rounded-secondary text-red-900 absolute top-2 right-2 group-hover:opacity-100 opacity-0 transition-opacity"
onClick={() =>
disapproveSeller({ id, body: { status: "active", role: "buyer" } })
}
title="Demote User to Buyer"
>
<Cross />
</button>
)}
</>
);
}

export default Page;
Loading

0 comments on commit 5a69819

Please sign in to comment.