diff --git a/src/app/actions/wishlist.ts b/src/app/actions/wishlist.ts index f06e027..c7ea1a7 100644 --- a/src/app/actions/wishlist.ts +++ b/src/app/actions/wishlist.ts @@ -476,45 +476,32 @@ export async function getItemWithComments(itemId: string, userId: string) { } } -export async function getWishlistStats(userId: string) { +export interface WishlistStats { + totalItems: number + highPriority: number + yourSubmissions: number + totalBudget: number +} + +export async function getWishlistStats(userId: string): Promise { const { env } = await getCloudflareContext() const db = getDb(env.DB) const allItems = await db.select().from(wishlistItems) - const allVotes = await db.select().from(wishlistVotes) const totalItems = allItems.length - const yourItems = allItems.filter((i) => i.submittedBy === userId).length - - let mostWanted = null - if (allItems.length > 0) { - const itemScores = allItems.map((item) => { - const itemVotes = allVotes.filter((v) => v.itemId === item.id) - const upvotes = itemVotes.filter((v) => v.voteType === "up").length - const downvotes = itemVotes.filter((v) => v.voteType === "down").length - return { - item, - score: upvotes - downvotes, - } - }) - const topItem = itemScores.sort((a, b) => b.score - a.score)[0] - if (topItem.score > 0) { - mostWanted = { name: topItem.item.name, score: topItem.score } - } - } - - const recentItems = allItems - .sort( - (a, b) => - new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() - ) - .slice(0, 5).length + const highPriority = allItems.filter((i) => i.priority === "high").length + const yourSubmissions = allItems.filter((i) => i.submittedBy === userId).length + const totalBudget = allItems.reduce( + (sum, item) => sum + (item.estimatedCost ?? 0), + 0 + ) return { totalItems, - yourItems, - mostWanted, - recentItems, + highPriority, + yourSubmissions, + totalBudget, } } diff --git a/src/components/wishlist/wishlist-filters.tsx b/src/components/wishlist/wishlist-filters.tsx index 7e79b90..5ee4453 100644 --- a/src/components/wishlist/wishlist-filters.tsx +++ b/src/components/wishlist/wishlist-filters.tsx @@ -1,7 +1,6 @@ "use client" -import { IconPlus } from "@tabler/icons-react" -import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" import { Select, SelectContent, @@ -11,54 +10,73 @@ import { } from "@/components/ui/select" interface WishlistFiltersProps { + search: string category: string + priority: string sortBy: string + onSearchChange: (search: string) => void onCategoryChange: (category: string) => void + onPriorityChange: (priority: string) => void onSortChange: (sort: string) => void - onAddClick: () => void } export function WishlistFilters({ + search, category, + priority, sortBy, + onSearchChange, onCategoryChange, + onPriorityChange, onSortChange, - onAddClick, }: WishlistFiltersProps) { return ( -
-
- +
+ onSearchChange(e.target.value)} + className="h-9 w-56" + /> - -
+ - + + +
) } diff --git a/src/components/wishlist/wishlist-stats.tsx b/src/components/wishlist/wishlist-stats.tsx index e989ae0..cf61c1b 100644 --- a/src/components/wishlist/wishlist-stats.tsx +++ b/src/components/wishlist/wishlist-stats.tsx @@ -1,74 +1,42 @@ "use client" -import { IconList, IconStar, IconUser, IconClock } from "@tabler/icons-react" -import { - Card, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card" +import { Card } from "@/components/ui/card" +import type { WishlistStats as WishlistStatsType } from "@/app/actions/wishlist" interface WishlistStatsProps { - stats: { - totalItems: number - yourItems: number - mostWanted: { name: string; score: number } | null - recentItems: number - } + stats: WishlistStatsType } export function WishlistStats({ stats }: WishlistStatsProps) { return ( -
- - - Total Items - - - - {stats.totalItems} - - - - - - Most Wanted - - - - {stats.mostWanted ? ( - - {stats.mostWanted.name} - - (score: {stats.mostWanted.score}) - +
+ +
+ + Total Items:{" "} + {stats.totalItems} + + | + + High Priority:{" "} + {stats.highPriority} + + | + + Your Submissions:{" "} + {stats.yourSubmissions} + + | + + Est. Total Budget:{" "} + + ${stats.totalBudget.toLocaleString(undefined, { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + })} - ) : ( - - No votes yet - - )} - - - - - - Your Submissions - - - - {stats.yourItems} - - - - - - Recent (Last 5) - - - - {stats.recentItems} - + +
) diff --git a/src/components/wishlist/wishlist-table.tsx b/src/components/wishlist/wishlist-table.tsx index 0c4dda3..3bc68b1 100644 --- a/src/components/wishlist/wishlist-table.tsx +++ b/src/components/wishlist/wishlist-table.tsx @@ -1,6 +1,6 @@ "use client" -import { useState, useEffect, useTransition, useCallback } from "react" +import { useState, useEffect, useTransition, useCallback, useMemo } from "react" import { IconArrowBigDown, IconArrowBigDownFilled, @@ -13,6 +13,7 @@ import { IconFileSpreadsheet, IconFileTypePdf, IconLoader2, + IconPlus, IconRefresh, IconTrash, } from "@tabler/icons-react" @@ -47,6 +48,7 @@ import { toggleItemVote, deleteWishlistItem, type WishlistItemWithMeta, + type WishlistStats as WishlistStatsType, type SortOption, type VoteType, } from "@/app/actions/wishlist" @@ -66,18 +68,31 @@ interface WishlistTableProps { export function WishlistTable({ userId, userName }: WishlistTableProps) { const [isPending, startTransition] = useTransition() const [items, setItems] = useState([]) - const [stats, setStats] = useState({ + const [stats, setStats] = useState({ totalItems: 0, - yourItems: 0, - mostWanted: null as { name: string; score: number } | null, - recentItems: 0, + highPriority: 0, + yourSubmissions: 0, + totalBudget: 0, }) + const [search, setSearch] = useState("") const [category, setCategory] = useState("all") + const [priority, setPriority] = useState("all") const [sortBy, setSortBy] = useState("score") const [addDialogOpen, setAddDialogOpen] = useState(false) const [selectedItem, setSelectedItem] = useState(null) const [detailOpen, setDetailOpen] = useState(false) + const filteredItems = useMemo(() => { + return items.filter((item) => { + const matchesSearch = + search === "" || + item.name.toLowerCase().includes(search.toLowerCase()) || + item.description.toLowerCase().includes(search.toLowerCase()) + const matchesPriority = priority === "all" || item.priority === priority + return matchesSearch && matchesPriority + }) + }, [items, search, priority]) + const fetchData = useCallback(() => { startTransition(async () => { const [itemsData, statsData] = await Promise.all([ @@ -100,7 +115,7 @@ export function WishlistTable({ userId, userName }: WishlistTableProps) { const getExportData = () => { const headers = ["Name", "Description", "Category", "Priority", "Est. Cost", "Score", "Submitted By", "Created"] - const rows = items.map((item) => [ + const rows = filteredItems.map((item) => [ item.name, item.description, item.category, @@ -178,7 +193,7 @@ export function WishlistTable({ userId, userName }: WishlistTableProps) { +
setSortBy(sort as SortOption)} - onAddClick={() => setAddDialogOpen(true)} />
@@ -233,6 +255,12 @@ export function WishlistTable({ userId, userName }: WishlistTableProps) { Be the first to add something!

+ ) : filteredItems.length === 0 ? ( +
+

+ No items match your filters. +

+
) : (
@@ -248,7 +276,7 @@ export function WishlistTable({ userId, userName }: WishlistTableProps) { - {items.map((item) => ( + {filteredItems.map((item) => (