underrate's picture
Initial commit
34ed5b1 verified
Raw
History Blame Contribute Delete
7.06 kB
"use client"
import { useState, useEffect } from "react"
import { useSession } from "next-auth/react"
import { formatDistanceToNow } from "date-fns"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Loader2, Search, Filter } from "lucide-react"
import Link from "next/link"
interface Inquiry {
id: string
name: string
email: string
projectType: string
budget: string | null
status: string
createdAt: string
}
const statusColors: Record<string, string> = {
NEW: "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300",
IN_PROGRESS: "bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-300",
QUOTED: "bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300",
WON: "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300",
LOST: "bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300",
}
export default function InquiriesPage() {
const { data: session } = useSession()
const [inquiries, setInquiries] = useState<Inquiry[]>([])
const [loading, setLoading] = useState(true)
const [searchTerm, setSearchTerm] = useState("")
useEffect(() => {
if (session) {
fetchInquiries()
}
}, [session])
const fetchInquiries = async () => {
try {
const res = await fetch("http://localhost:5000/api/admin/inquiries", {
headers: {
Authorization: `Bearer ${session?.user?.id}`, // using user id as mock token for now
},
})
if (res.ok) {
const data = await res.json()
setInquiries(data)
}
} catch (error) {
console.error("Failed to fetch inquiries:", error)
} finally {
setLoading(false)
}
}
const filteredInquiries = inquiries.filter(
(inq) =>
inq.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
inq.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
(inq.projectType && inq.projectType.toLowerCase().includes(searchTerm.toLowerCase()))
)
if (loading) {
return (
<div className="flex h-[50vh] items-center justify-center">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
</div>
)
}
return (
<div className="space-y-6">
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<h1 className="text-3xl font-bold tracking-tight">Inquiry Management</h1>
<p className="text-muted-foreground">Manage and track client quote requests.</p>
</div>
</div>
<Card>
<CardHeader>
<div className="flex items-center space-x-2">
<Search className="h-4 w-4 text-muted-foreground" />
<Input
placeholder="Search by name, email, or project type..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="max-w-sm"
/>
</div>
</CardHeader>
<CardContent>
<div className="rounded-md border">
<div className="grid grid-cols-12 gap-4 border-b bg-muted/50 p-4 font-medium text-sm text-muted-foreground">
<div className="col-span-3">Client</div>
<div className="col-span-2 hidden sm:block">Project</div>
<div className="col-span-2 hidden md:block">Budget</div>
<div className="col-span-2">Date</div>
<div className="col-span-2">Status</div>
<div className="col-span-1 text-right">Actions</div>
</div>
{filteredInquiries.length === 0 ? (
<div className="p-8 text-center text-muted-foreground">
No inquiries found.
</div>
) : (
<div className="divide-y">
{filteredInquiries.map((inquiry) => (
<div key={inquiry.id} className="grid grid-cols-12 items-center gap-4 p-4 text-sm hover:bg-muted/50 transition-colors">
<div className="col-span-3">
<div className="font-medium truncate">{inquiry.name}</div>
<div className="text-xs text-muted-foreground truncate">{inquiry.email}</div>
</div>
<div className="col-span-2 hidden sm:block truncate capitalize">
{inquiry.projectType || '—'}
</div>
<div className="col-span-2 hidden md:block text-muted-foreground">
{inquiry.budget || '—'}
</div>
<div className="col-span-2 text-muted-foreground">
{formatDistanceToNow(new Date(inquiry.createdAt), { addSuffix: true })}
</div>
<div className="col-span-2">
<Badge variant="secondary" className={`${statusColors[inquiry.status] || ''} hover:bg-opacity-80`}>
{inquiry.status.replace('_', ' ')}
</Badge>
</div>
<div className="col-span-1 text-right">
<Button variant="ghost" size="sm" asChild>
<Link href={`/admin/inquiries/${inquiry.id}`}>
View
</Link>
</Button>
</div>
</div>
))}
</div>
)}
</div>
</CardContent>
</Card>
</div>
)
}