Redo header and remove column selector
This commit is contained in:
@@ -58,14 +58,6 @@ const ProductGrid = ({
|
||||
direction: "desc",
|
||||
});
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [columnVisibility, setColumnVisibility] = useState({
|
||||
image: true,
|
||||
name: true,
|
||||
totalQuantity: true,
|
||||
totalRevenue: true,
|
||||
orderCount: true,
|
||||
price: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
fetchProducts();
|
||||
@@ -210,11 +202,20 @@ const ProductGrid = ({
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="relative hidden sm:block">
|
||||
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Search products..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-8 h-9 w-[200px]"
|
||||
/>
|
||||
</div>
|
||||
<Select
|
||||
value={selectedTimeRange}
|
||||
onValueChange={handleTimeRangeChange}
|
||||
>
|
||||
<SelectTrigger className="w-[180px] h-9">
|
||||
<SelectTrigger className="w-[120px] h-9">
|
||||
<SelectValue placeholder="Select time range" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -270,53 +271,7 @@ const ProductGrid = ({
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="p-6 pt-0 flex-1 overflow-hidden -mt-1">
|
||||
<div className="flex items-center justify-between mb-4 mt-1">
|
||||
<div className="relative max-w-sm">
|
||||
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Search products..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-8 h-9"
|
||||
/>
|
||||
</div>
|
||||
<DropdownMenu modal={false}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="sm" className="ml-auto h-9 hover:bg-muted/50 focus:ring-1 focus:ring-ring/30">
|
||||
<Settings2 className="mr-2 h-4 w-4" />
|
||||
View
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
className="w-[150px] mt-2 border-border/40"
|
||||
sideOffset={5}
|
||||
>
|
||||
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator className="bg-border/40" />
|
||||
{Object.entries(columnVisibility).map(([column, isVisible]) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={column}
|
||||
className="capitalize"
|
||||
checked={isVisible}
|
||||
onCheckedChange={(value) =>
|
||||
setColumnVisibility(prev => ({
|
||||
...prev,
|
||||
[column]: value
|
||||
}))
|
||||
}
|
||||
>
|
||||
{column === 'totalQuantity' ? 'Sold' :
|
||||
column === 'totalRevenue' ? 'Revenue' :
|
||||
column === 'orderCount' ? 'Orders' :
|
||||
column}
|
||||
</DropdownMenuCheckboxItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
<div className="h-[calc(100%-3.5rem)]">
|
||||
<div className="h-full">
|
||||
{loading && !products.length ? (
|
||||
<LoadingState />
|
||||
) : error ? (
|
||||
@@ -337,84 +292,72 @@ const ProductGrid = ({
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="hover:bg-transparent">
|
||||
{columnVisibility.image && (
|
||||
<th className="p-1.5 text-left font-medium sticky top-0 bg-white dark:bg-background z-10 w-[50px] min-w-[50px] border-b" />
|
||||
)}
|
||||
{columnVisibility.name && (
|
||||
<th className="p-1.5 text-left font-medium sticky top-0 bg-white dark:bg-background z-10 min-w-[200px] border-b">
|
||||
<button
|
||||
onClick={() => handleSort("name")}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-start w-full px-2 py-1 text-sm font-medium transition-colors rounded-md",
|
||||
sorting.column === "name"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
Product
|
||||
</button>
|
||||
</th>
|
||||
)}
|
||||
{columnVisibility.totalQuantity && (
|
||||
<th className="p-1.5 font-medium text-center sticky top-0 bg-white dark:bg-background z-10 border-b">
|
||||
<button
|
||||
onClick={() => handleSort("totalQuantity")}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center w-full px-2 py-1 text-sm font-medium transition-colors rounded-md",
|
||||
sorting.column === "totalQuantity"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
Sold
|
||||
</button>
|
||||
</th>
|
||||
)}
|
||||
{columnVisibility.totalRevenue && (
|
||||
<th className="p-1.5 font-medium text-center sticky top-0 bg-white dark:bg-background z-10 border-b">
|
||||
<button
|
||||
onClick={() => handleSort("totalRevenue")}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center w-full px-2 py-1 text-sm font-medium transition-colors rounded-md",
|
||||
sorting.column === "totalRevenue"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
Rev
|
||||
</button>
|
||||
</th>
|
||||
)}
|
||||
{columnVisibility.orderCount && (
|
||||
<th className="p-1.5 font-medium text-center sticky top-0 bg-white dark:bg-background z-10 border-b">
|
||||
<button
|
||||
onClick={() => handleSort("orderCount")}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center w-full px-2 py-1 text-sm font-medium transition-colors rounded-md",
|
||||
sorting.column === "orderCount"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
Orders
|
||||
</button>
|
||||
</th>
|
||||
)}
|
||||
{columnVisibility.price && (
|
||||
<th className="p-1.5 font-medium text-center sticky top-0 bg-white dark:bg-background z-10 hidden md:table-cell border-b">
|
||||
<button
|
||||
onClick={() => handleSort("price")}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center w-full px-2 py-1 text-sm font-medium transition-colors rounded-md",
|
||||
sorting.column === "price"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
Price
|
||||
</button>
|
||||
</th>
|
||||
)}
|
||||
<th className="p-1.5 text-left font-medium sticky top-0 bg-white dark:bg-background z-10 w-[50px] min-w-[50px] border-b" />
|
||||
<th className="p-1.5 text-left font-medium sticky top-0 bg-white dark:bg-background z-10 min-w-[200px] border-b">
|
||||
<button
|
||||
onClick={() => handleSort("name")}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-start w-full px-2 py-1 text-sm font-medium transition-colors rounded-md",
|
||||
sorting.column === "name"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
Product
|
||||
</button>
|
||||
</th>
|
||||
<th className="p-1.5 font-medium text-center sticky top-0 bg-white dark:bg-background z-10 border-b">
|
||||
<button
|
||||
onClick={() => handleSort("totalQuantity")}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center w-full px-2 py-1 text-sm font-medium transition-colors rounded-md",
|
||||
sorting.column === "totalQuantity"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
Sold
|
||||
</button>
|
||||
</th>
|
||||
<th className="p-1.5 font-medium text-center sticky top-0 bg-white dark:bg-background z-10 border-b">
|
||||
<button
|
||||
onClick={() => handleSort("totalRevenue")}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center w-full px-2 py-1 text-sm font-medium transition-colors rounded-md",
|
||||
sorting.column === "totalRevenue"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
Rev
|
||||
</button>
|
||||
</th>
|
||||
<th className="p-1.5 font-medium text-center sticky top-0 bg-white dark:bg-background z-10 border-b">
|
||||
<button
|
||||
onClick={() => handleSort("orderCount")}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center w-full px-2 py-1 text-sm font-medium transition-colors rounded-md",
|
||||
sorting.column === "orderCount"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
Orders
|
||||
</button>
|
||||
</th>
|
||||
<th className="p-1.5 font-medium text-center sticky top-0 bg-white dark:bg-background z-10 hidden md:table-cell border-b">
|
||||
<button
|
||||
onClick={() => handleSort("price")}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center w-full px-2 py-1 text-sm font-medium transition-colors rounded-md",
|
||||
sorting.column === "price"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-accent hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
Price
|
||||
</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 dark:divide-gray-800">
|
||||
@@ -423,54 +366,42 @@ const ProductGrid = ({
|
||||
key={product.id}
|
||||
className="hover:bg-muted/50 transition-colors"
|
||||
>
|
||||
{columnVisibility.image && (
|
||||
<td className="p-1 align-middle w-[50px]">
|
||||
{product.ImgThumb && (
|
||||
<img
|
||||
src={product.ImgThumb}
|
||||
alt=""
|
||||
width={50}
|
||||
height={50}
|
||||
className="rounded bg-muted"
|
||||
onError={(e) => (e.target.style.display = "none")}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
)}
|
||||
{columnVisibility.name && (
|
||||
<td className="p-1 align-middle min-w-[200px]">
|
||||
<div className="flex flex-col min-w-0">
|
||||
<a
|
||||
href={`https://backend.acherryontop.com/product/${product.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm hover:underline line-clamp-2 text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
{product.name}
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
{columnVisibility.totalQuantity && (
|
||||
<td className="p-1 align-middle text-center text-sm font-medium">
|
||||
{product.totalQuantity}
|
||||
</td>
|
||||
)}
|
||||
{columnVisibility.totalRevenue && (
|
||||
<td className="p-1 align-middle text-center text-emerald-600 dark:text-emerald-400 text-sm font-medium">
|
||||
${product.totalRevenue.toFixed(2)}
|
||||
</td>
|
||||
)}
|
||||
{columnVisibility.orderCount && (
|
||||
<td className="p-1 align-middle text-center text-muted-foreground text-sm">
|
||||
{product.orderCount}
|
||||
</td>
|
||||
)}
|
||||
{columnVisibility.price && (
|
||||
<td className="p-1 align-middle text-center hidden md:table-cell text-sm">
|
||||
${product.price.toFixed(2)}
|
||||
</td>
|
||||
)}
|
||||
<td className="p-1 align-middle w-[50px]">
|
||||
{product.ImgThumb && (
|
||||
<img
|
||||
src={product.ImgThumb}
|
||||
alt=""
|
||||
width={50}
|
||||
height={50}
|
||||
className="rounded bg-muted"
|
||||
onError={(e) => (e.target.style.display = "none")}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td className="p-1 align-middle min-w-[200px]">
|
||||
<div className="flex flex-col min-w-0">
|
||||
<a
|
||||
href={`https://backend.acherryontop.com/product/${product.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm hover:underline line-clamp-2 text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
{product.name}
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-1 align-middle text-center text-sm font-medium">
|
||||
{product.totalQuantity}
|
||||
</td>
|
||||
<td className="p-1 align-middle text-center text-emerald-600 dark:text-emerald-400 text-sm font-medium">
|
||||
${product.totalRevenue.toFixed(2)}
|
||||
</td>
|
||||
<td className="p-1 align-middle text-center text-muted-foreground text-sm">
|
||||
{product.orderCount}
|
||||
</td>
|
||||
<td className="p-1 align-middle text-center hidden md:table-cell text-sm">
|
||||
${product.price.toFixed(2)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user