More polishing
This commit is contained in:
parent
59d4085194
commit
2ce882f36e
@ -224,37 +224,17 @@ export default function Shop() {
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-neutral-100">
|
||||
{/* Main Content */}
|
||||
<div className="flex flex-1 overflow-y-auto mr-[19rem]">
|
||||
<div className="flex flex-1 overflow-y-auto">
|
||||
{/* Artifact Grid */}
|
||||
<div className="flex-grow grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 p-6">
|
||||
<div className="flex-grow grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 p-6">
|
||||
{currentArtifacts.map((artifact) => (
|
||||
<ArtifactCard key={artifact.id} artifact={artifact} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sidebar */}
|
||||
<div className="bg-white shadow-lg h-screen fixed right-0 overflow-y-auto">
|
||||
<Sidebar
|
||||
logTitle="Artifact Collection"
|
||||
logSubtitle="Record new artifacts - name, description, image, location and price"
|
||||
recentsTitle="Recent Updates"
|
||||
events={
|
||||
[
|
||||
/* example events if needed */
|
||||
]
|
||||
}
|
||||
selectedEventId=""
|
||||
setSelectedEventId={() => {}}
|
||||
hoveredEventId=""
|
||||
setHoveredEventId={() => {}}
|
||||
button1Name="Add New Artifact"
|
||||
button2Name="Search Artifacts"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Pagination Footer */}
|
||||
<footer className="mt-10 bg-white border-t border-neutral-300 py-3 text-center flex justify-center items-center mr-[19rem]">
|
||||
<footer className="mt-10 bg-white border-t border-neutral-300 py-3 text-center flex justify-center items-center">
|
||||
<button
|
||||
onClick={handlePreviousPage}
|
||||
disabled={currentPage === 1}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
"use client";
|
||||
import { useState, useMemo } from "react";
|
||||
import { FaInbox, FaTimes, FaBox, FaCalendarPlus, FaShoppingCart } from "react-icons/fa";
|
||||
import { IoFilter, IoFilterCircleOutline, IoFilterOutline } from "react-icons/io5";
|
||||
import { FaCalendarPlus, FaWarehouse, FaCartShopping } from "react-icons/fa6";
|
||||
import { IoFilter, IoFilterCircleOutline, IoFilterOutline, IoToday } from "react-icons/io5";
|
||||
import { FaTimes } from "react-icons/fa";
|
||||
import { SetStateAction, Dispatch } from "react";
|
||||
|
||||
// Artifact type
|
||||
@ -88,18 +89,31 @@ function FilterInput({
|
||||
type?: string;
|
||||
options?: string[];
|
||||
}) {
|
||||
const showSelectedFilter = type === "text" && !["true", "false"].includes(options?.at(-1)!);
|
||||
return (
|
||||
<div className="flex items-center gap-2 group">
|
||||
<div className="flex h-full pl-0.5 pr-1 items-center group">
|
||||
<div className="relative">
|
||||
<div className="p-1 group-hover:bg-blue-100 rounded transition-colors duration-200">
|
||||
<IoFilter className="cursor-pointer text-neutral-500 font-bold group-hover:text-blue-600" />
|
||||
<div
|
||||
className={`p-1 group-hover:bg-blue-100 rounded transition-colors duration-200 ${
|
||||
!showSelectedFilter && value && "bg-blue-100"
|
||||
}`}
|
||||
>
|
||||
<IoFilter
|
||||
className={`cursor-pointer text-neutral-500 font-bold group-hover:text-blue-600
|
||||
${!showSelectedFilter && value && "text-blue-600"}
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute z-50 mt-0 w-48 bg-white border border-neutral-300 rounded-md shadow-lg p-2 opacity-0 group-hover:opacity-100 group-hover:visible transition-opacity duration-200 left-0 pointer-events-none group-hover:pointer-events-auto">
|
||||
<div
|
||||
className={`absolute z-50 mt-2 w-48 bg-white border border-neutral-300 rounded-md shadow-lg p-2 opacity-0 group-hover:opacity-100 group-hover:visible transition-opacity duration-200 pointer-events-none group-hover:pointer-events-auto
|
||||
${type === "date" ? "-right-1/2" : "-left-1/2"}
|
||||
`}
|
||||
>
|
||||
{options ? (
|
||||
<div className="max-h-32 overflow-y-auto">
|
||||
{options.map((opt) => (
|
||||
<div key={opt} className="p-1 hover:bg-blue-100 cursor-pointer text-sm" onClick={() => onChange(opt)}>
|
||||
{opt || "All"}
|
||||
{opt ? (opt === "true" ? "Yes" : "No") : "All"}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@ -115,7 +129,7 @@ function FilterInput({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{value && (
|
||||
{value && showSelectedFilter && (
|
||||
<div className="inline-flex items-center bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-md">
|
||||
{value === "true" ? "Yes" : value === "false" ? "No" : value}
|
||||
<FaTimes className="ml-1 cursor-pointer text-blue-600 hover:text-blue-800" onClick={() => onChange("")} />
|
||||
@ -160,11 +174,10 @@ function ArtifactTable({
|
||||
setSortConfig((prev) => {
|
||||
if (!prev || prev.key !== key) {
|
||||
return { key, direction: "asc" };
|
||||
} else if (prev.direction === "asc") {
|
||||
return { key, direction: "desc" };
|
||||
}
|
||||
return {
|
||||
key,
|
||||
direction: prev.direction === "asc" ? "desc" : "asc",
|
||||
};
|
||||
return null;
|
||||
});
|
||||
};
|
||||
|
||||
@ -185,48 +198,52 @@ function ArtifactTable({
|
||||
return sorted;
|
||||
}, [artifacts, sortConfig]);
|
||||
|
||||
const columns: { label: string; key: keyof Artifact; width: string }[] = [
|
||||
{ label: "ID", key: "id", width: "5%" },
|
||||
{ label: "Name", key: "name", width: "12%" },
|
||||
{ label: "Earthquake ID", key: "earthquakeId", width: "10%" },
|
||||
{ label: "Location", key: "location", width: "12%" },
|
||||
{ label: "Description", key: "description", width: "25%" },
|
||||
{ label: "Required", key: "isRequired", width: "6%" },
|
||||
{ label: "Sold", key: "isSold", width: "5%" },
|
||||
{ label: "Collected", key: "isCollected", width: "7%" },
|
||||
{ label: "Date Added", key: "dateAdded", width: "8%" },
|
||||
];
|
||||
|
||||
return (
|
||||
<table className="w-full text-left">
|
||||
<table className="w-full table-fixed text-left">
|
||||
<thead className="sticky top-0 bg-neutral-100 border-b border-neutral-200 z-10">
|
||||
<tr>
|
||||
{[
|
||||
{ label: "ID", key: "id" },
|
||||
{ label: "Name", key: "name" },
|
||||
{ label: "Earthquake ID", key: "earthquakeId" },
|
||||
{ label: "Location", key: "location" },
|
||||
{ label: "Description", key: "description" },
|
||||
{ label: "Required", key: "isRequired" },
|
||||
{ label: "Sold", key: "isSold" },
|
||||
{ label: "Collected", key: "isCollected" },
|
||||
{ label: "Date Added", key: "dateAdded" },
|
||||
].map(({ label, key }) => (
|
||||
<th
|
||||
key={key}
|
||||
className="p-3 text-sm font-semibold text-neutral-800 cursor-pointer"
|
||||
onClick={() => handleSort(key as keyof Artifact)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{label}
|
||||
<FilterInput
|
||||
value={filters[key]}
|
||||
onChange={(value) => {
|
||||
setFilters({ ...filters, [key]: value } as {
|
||||
id: string;
|
||||
name: string;
|
||||
earthquakeId: string;
|
||||
location: string;
|
||||
description: string;
|
||||
isRequired: string;
|
||||
isSold: string;
|
||||
isCollected: string;
|
||||
dateAdded: string;
|
||||
});
|
||||
if (value === "") clearSortConfig();
|
||||
}}
|
||||
type={key === "dateAdded" ? "date" : "text"}
|
||||
options={["isRequired", "isSold", "isCollected"].includes(key) ? ["", "true", "false"] : undefined}
|
||||
/>
|
||||
{sortConfig?.key === key && <span>{sortConfig.direction === "asc" ? "↑" : "↓"}</span>}
|
||||
{columns.map(({ label, key, width }) => (
|
||||
<th key={key} className="text-sm px-5 font-semibold text-neutral-800 cursor-pointer" style={{ width }}>
|
||||
<div className="flex h-11 items-center">
|
||||
<div className="flex h-full items-center" onClick={() => handleSort(key as keyof Artifact)}>
|
||||
<div className="select-none">{label}</div>
|
||||
</div>
|
||||
<div className="h-full relative">
|
||||
<FilterInput
|
||||
value={filters[key]}
|
||||
onChange={(value) => {
|
||||
setFilters({ ...filters, [key]: value } as {
|
||||
id: string;
|
||||
name: string;
|
||||
earthquakeId: string;
|
||||
location: string;
|
||||
description: string;
|
||||
isRequired: string;
|
||||
isSold: string;
|
||||
isCollected: string;
|
||||
dateAdded: string;
|
||||
});
|
||||
if (value === "") clearSortConfig();
|
||||
}}
|
||||
type={key === "dateAdded" ? "date" : "text"}
|
||||
options={["isRequired", "isSold", "isCollected"].includes(key) ? ["", "true", "false"] : undefined}
|
||||
/>
|
||||
{sortConfig?.key === key && (
|
||||
<div className="absolute -right-2 top-3">{sortConfig.direction === "asc" ? "↑" : "↓"}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
@ -239,15 +256,27 @@ function ArtifactTable({
|
||||
className="border-b border-neutral-200 hover:bg-neutral-100 cursor-pointer"
|
||||
onClick={() => setEditArtifact(artifact)}
|
||||
>
|
||||
<td className="p-3 pl-4 text-sm text-neutral-600">{artifact.id}</td>
|
||||
<td className="p-3 text-sm text-neutral-800 font-medium">{artifact.name}</td>
|
||||
<td className="p-3 text-sm text-neutral-600">{artifact.earthquakeId}</td>
|
||||
<td className="p-3 text-sm text-neutral-600">{artifact.location}</td>
|
||||
<td className="p-3 text-sm text-neutral-600">{artifact.description}</td>
|
||||
<td className="p-3 text-sm text-neutral-600">{artifact.isRequired ? "Yes" : "No"}</td>
|
||||
<td className="p-3 text-sm text-neutral-600">{artifact.isSold ? "Yes" : "No"}</td>
|
||||
<td className="p-3 text-sm text-neutral-600">{artifact.isCollected ? "Yes" : "No"}</td>
|
||||
<td className="p-3 text-sm text-neutral-600">{artifact.dateAdded}</td>
|
||||
{columns.map(({ key, width }) => (
|
||||
<td
|
||||
key={key}
|
||||
className={`py-3 px-5 text-sm text-neutral-600 truncate ${key === "name" && "font-medium text-neutral-800"}`}
|
||||
style={{ width }}
|
||||
>
|
||||
{key === "isRequired"
|
||||
? artifact.isRequired
|
||||
? "Yes"
|
||||
: "No"
|
||||
: key === "isSold"
|
||||
? artifact.isSold
|
||||
? "Yes"
|
||||
: "No"
|
||||
: key === "isCollected"
|
||||
? artifact.isCollected
|
||||
? "Yes"
|
||||
: "No"
|
||||
: artifact[key]}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
@ -715,18 +744,6 @@ export default function Warehouse() {
|
||||
const artifactsAddedToday = warehouseArtifacts.filter((a) => a.dateAdded === today).length;
|
||||
const artifactsSoldToday = warehouseArtifacts.filter((a) => a.isSold && a.dateAdded === today).length;
|
||||
|
||||
const handleNextPage = () => {
|
||||
if (indexOfLastArtifact < filteredArtifacts.length) {
|
||||
setCurrentPage((prev) => prev + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePreviousPage = () => {
|
||||
if (currentPage > 1) {
|
||||
setCurrentPage((prev) => prev - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const clearFilters = () => {
|
||||
setFilters({
|
||||
id: "",
|
||||
@ -748,17 +765,17 @@ export default function Warehouse() {
|
||||
<div className="flex flex-1 p-5">
|
||||
<div className="flex-grow flex flex-col">
|
||||
{/* Overview Stats */}
|
||||
<div className="flex gap-8 mb-4">
|
||||
<div className="flex gap-8 ml-5 mt-1">
|
||||
<div className="flex items-center text-md text-neutral-600">
|
||||
<FaBox className="mr-2 text-blue-600" />
|
||||
<FaWarehouse className="mr-2 h-8 w-8 text-blue-600 opacity-90" />
|
||||
Total Artifacts: <span className="font-semibold ml-1">{totalArtifacts}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-md text-neutral-600">
|
||||
<FaCalendarPlus className="mr-2 text-blue-600" />
|
||||
<IoToday className="mr-2 h-8 w-8 text-blue-600 opacity-90" />
|
||||
Added Today: <span className="font-semibold ml-1">{artifactsAddedToday}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-md text-neutral-600">
|
||||
<FaShoppingCart className="mr-2 text-blue-600" />
|
||||
<FaCartShopping className="mr-2 h-8 w-8 text-blue-600 opacity-90" />
|
||||
Sold Today: <span className="font-semibold ml-1">{artifactsSoldToday}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user