diff --git a/src/app/dashboard/(user-dashboard)/products/_components/product-content-view.tsx b/src/app/dashboard/(user-dashboard)/products/_components/product-content-view.tsx new file mode 100644 index 000000000..e14e89a96 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/products/_components/product-content-view.tsx @@ -0,0 +1,161 @@ +import { AnimatePresence } from "framer-motion"; +import { useRouter } from "next-nprogress-bar"; + +import { + Table, + TableBody, + TableHead, + TableHeader, + TableRow, +} from "~/components/ui/table"; +import { useProductModal } from "~/hooks/admin-product/use-product.modal"; +import { useProducts } from "~/hooks/admin-product/use-products.persistence"; +import { cn } from "~/lib/utils"; +import { ProductTableProperties } from "~/types/admin-product.types"; +import { ProductGridCard } from "./product-grid-card"; +import { ProductListRow } from "./product-list-row"; +import { ProductNotFound } from "./product-not-found"; + +type Properties = { + subset: ProductTableProperties[]; + filteredProducts: ProductTableProperties[]; + searchTerm: string; + view: "grid" | "list"; +}; + +export const ProductContentView = ({ + subset, + filteredProducts, + searchTerm, + view = "grid", +}: Properties) => { + const { products } = useProducts(); + const { + updateOpen, + updateProductId, + product_id, + isActionModal, + setIsActionModal, + setIsDelete, + } = useProductModal(); + const router = useRouter(); + + if (!products) return; + + const handleOpenActionModal = (product_id: string) => { + updateProductId(product_id); + setIsActionModal(!isActionModal); + }; + + const handleOpenDetail = (product_id: string) => { + setIsActionModal(false); + updateProductId(product_id); + updateOpen(true); + }; + const handleDeleteAction = (id: string) => { + setIsActionModal(false); + updateProductId(id); + setIsDelete(true); + }; + const handleEditAction = (id: string) => { + setIsActionModal(false); + router.push(`/dashboard/products/${id}`); + }; + + return ( + + {view === "list" && ( +
+ + + + + Product Name + + + Product ID + + + Category + + + Price + + + Status + + + Actions + + + + + {filteredProducts.length > 0 && + subset.length > 0 && + subset.map((product, index) => ( + handleOpenDetail(product.product_id)} + onEdit={() => handleEditAction(product.product_id)} + onDelete={() => handleDeleteAction(product.product_id)} + onOpenActionModal={() => + handleOpenActionModal(product.product_id) + } + onCloseActionModal={() => setIsActionModal(false)} + /> + ))} + + {filteredProducts.length === 0 && searchTerm.length > 1 && ( + + )} +
+
+ )} + {view === "grid" && ( +
+
+ {filteredProducts.length > 0 && + subset.length > 0 && + subset.map((product) => ( + ({})} + onEdit={() => handleEditAction(product.product_id)} + onDelete={() => handleDeleteAction(product.product_id)} + /> + ))} +
+ {filteredProducts.length === 0 && searchTerm.length > 1 && ( + + )} +
+ )} +
+ ); +}; diff --git a/src/app/dashboard/(user-dashboard)/products/_components/product-content.tsx b/src/app/dashboard/(user-dashboard)/products/_components/product-content.tsx index 9b190c91b..d3f450d1a 100644 --- a/src/app/dashboard/(user-dashboard)/products/_components/product-content.tsx +++ b/src/app/dashboard/(user-dashboard)/products/_components/product-content.tsx @@ -5,22 +5,12 @@ import { useEffect, useMemo, useState } from "react"; import LoadingSpinner from "~/components/miscellaneous/loading-spinner"; import ProductCardSkeleton from "~/components/skeleton/product.skeleton"; - -import "~/components/ui/table"; - -import { - Table, - TableBody, - TableHead, - TableHeader, - TableRow, -} from "~/components/ui/table"; import { useProductModal } from "~/hooks/admin-product/use-product.modal"; import { useProductsFilters } from "~/hooks/admin-product/use-products.-filters.persistence"; import { useProducts } from "~/hooks/admin-product/use-products.persistence"; import { cn } from "~/lib/utils"; import { ProductTableProperties } from "~/types/admin-product.types"; -import ProductBodyShadcn from "./product-body-shadcn"; +import { ProductContentView } from "./product-content-view"; const Pagination = dynamic(() => import("react-paginate"), { ssr: false, @@ -108,60 +98,17 @@ const ProductContent = ({
- - {view === "list" && ( - - - - - Product Name - - - Product ID - - - Category - - - Price - - - Status - - - Actions - - - - - - - {filteredProducts.length === 0 && searchTerm.length > 1 && ( -
-

- No product found for " - {searchTerm} - " -

-
- )} -
- )} -
+ {!products && }
diff --git a/src/app/dashboard/(user-dashboard)/products/_components/product-filter.tsx b/src/app/dashboard/(user-dashboard)/products/_components/product-filter.tsx index d81f8864b..7fff6eb7c 100644 --- a/src/app/dashboard/(user-dashboard)/products/_components/product-filter.tsx +++ b/src/app/dashboard/(user-dashboard)/products/_components/product-filter.tsx @@ -87,7 +87,7 @@ const ProductFilter = ({
+ + {isActionModal && selectedId === id && ( + + + Actions + +
+ + +
+
+ )} +
+ + + ); +} diff --git a/src/app/dashboard/(user-dashboard)/products/_components/product-not-found.tsx b/src/app/dashboard/(user-dashboard)/products/_components/product-not-found.tsx new file mode 100644 index 000000000..8df78e0e4 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/products/_components/product-not-found.tsx @@ -0,0 +1,11 @@ +export function ProductNotFound({ term }: { term: string }) { + return ( +
+

+ No product found for " + {term} + " +

+
+ ); +} diff --git a/src/app/dashboard/(user-dashboard)/products/page.tsx b/src/app/dashboard/(user-dashboard)/products/page.tsx index 1bb5df0fc..fa241ff49 100644 --- a/src/app/dashboard/(user-dashboard)/products/page.tsx +++ b/src/app/dashboard/(user-dashboard)/products/page.tsx @@ -21,7 +21,7 @@ const ProductFilter = dynamic(() => import("./_components/product-filter"), { const ProductPage = () => { const [searchTerm, setSearchTerm] = useState(""); - const [view, setView] = useState<"list" | "grid">("list"); + const [view, setView] = useState<"list" | "grid">("grid"); const { isOpen, diff --git a/src/app/globals.css b/src/app/globals.css index a5043fd7e..a9943ece4 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -48,8 +48,13 @@ --link: 222 47% 11%; --error: 0 72% 51%; + --error-50: 0 85% 95%; + --error-700: 0 90% 34%; --success: 102 51% 52%; + --success-50: 128 50% 94%; + --success-700: 126 84% 26%; --warning: 45 93% 47%; + --warning-50: 25% 100% 98%; --border: 214 32% 91%; --input: 214 32% 91%; diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 000000000..2ec8c6011 --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,45 @@ +import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react"; + +import { cn } from "~/lib/utils"; + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + "success-dot": + "bg-success-50 text-success-700 border-transparent before:mr-1 before:content-['•_'] before:text-success", + "error-dot": + "bg-error-50 text-error-700 border-transparent before:mr-1 before:content-['•_'] before:text-error", + "warning-dot": + "bg-warning-50 text-warning border-transparent before:mr-1 before:content-['•_'] before:text-warning", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +export interface BadgeProperties + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...properties }: BadgeProperties) { + return ( +
+ ); +} + +export { Badge, badgeVariants }; diff --git a/tailwind.config.ts b/tailwind.config.ts index c318a20ed..53f29dd84 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -82,9 +82,20 @@ const config = { DEFAULT: "hsl(var(--card))", foreground: "hsl(var(--card-foreground))", }, - error: "hsl(var(--error))", - success: "hsl(var(--success))", - warning: "hsl(var(--warning))", + error: { + DEFAULT: "hsl(var(--error))", + "50": "hsl(var(--error-50))", + "700": "hsl(var(--error-700))", + }, + success: { + DEFAULT: "hsl(var(--success))", + "50": "hsl(var(--success-50))", + "700": "hsl(var(--success-700))", + }, + warning: { + DEFAULT: "hsl(var(--warning))", + "50": "hsl(var(--warning-50))", + }, neutral: { dark: { 1: "hsl(var(--neutralColor-dark-1))",