feat: add inquiry page

This commit is contained in:
ARJUN S THAMPI
2026-03-25 11:26:45 +05:30
parent b9f372145b
commit e546519e7a
4 changed files with 184 additions and 0 deletions
+2
View File
@@ -17,6 +17,7 @@ import Appointment from "./pages/Appointment";
import EmailPage from "./pages/email"; import EmailPage from "./pages/email";
import CareerPage from "./pages/Career"; import CareerPage from "./pages/Career";
import CandidatePage from "./pages/candidates"; import CandidatePage from "./pages/candidates";
import InquiryPage from "./pages/inquiry";
export default function App() { export default function App() {
return ( return (
@@ -38,6 +39,7 @@ export default function App() {
<Route path="/email" element={<EmailPage />} /> <Route path="/email" element={<EmailPage />} />
<Route path="/career" element={<CareerPage />} /> <Route path="/career" element={<CareerPage />} />
<Route path="/candidate" element={<CandidatePage />} /> <Route path="/candidate" element={<CandidatePage />} />
<Route path="/inquiry" element={<InquiryPage />} />
</Route> </Route>
</Route> </Route>
+11
View File
@@ -0,0 +1,11 @@
import apiClient from "@/api/client";
export const getInquiriesApi = async () => {
const res = await apiClient.get("/inquiry/getAll");
return res.data;
};
export const deleteInquiryApi = async (id: number) => {
const res = await apiClient.delete(`/inquiry/${id}`);
return res.data;
};
@@ -27,6 +27,10 @@ export default function Sidebar() {
name: "Candidates", name: "Candidates",
path: "/candidate", path: "/candidate",
}, },
{
name: "Inquiry",
path: "/inquiry",
},
{ {
name: "Email", name: "Email",
path: "/email", path: "/email",
+167
View File
@@ -0,0 +1,167 @@
import { useState, useEffect, useCallback } from "react";
import { getInquiriesApi, deleteInquiryApi } from "@/api/inquiry";
import { exportToExcel } from "@/utils/exportToExcel";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Loader2, Trash, RefreshCw, Download } from "lucide-react";
export default function InquiryPage() {
const [inquiries, setInquiries] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const [searchText, setSearchText] = useState("");
const fetchAll = useCallback(async () => {
setLoading(true);
try {
const res = await getInquiriesApi();
setInquiries(res?.data || []);
} catch (err) {
console.error(err);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetchAll();
}, [fetchAll]);
const filteredInquiries = inquiries.filter((item) => {
return (
item.fullName?.toLowerCase().includes(searchText.toLowerCase()) ||
item.number?.includes(searchText) ||
item.emailId?.toLowerCase().includes(searchText.toLowerCase()) ||
item.subject?.toLowerCase().includes(searchText.toLowerCase())
);
});
async function handleDelete(id: number) {
if (!confirm("Delete inquiry?")) return;
await deleteInquiryApi(id);
fetchAll();
}
const handleExport = () => {
const exportData = filteredInquiries.map((item) => ({
ID: item.id,
Name: item.fullName,
Phone: item.number,
Email: item.emailId,
Subject: item.subject,
Message: item.message,
Date: new Date(item.createdAt).toLocaleDateString(),
}));
exportToExcel(exportData, "inquiries");
};
return (
<div className="p-6 space-y-6">
<div className="flex justify-between items-center gap-3 flex-wrap">
<h1 className="text-2xl font-bold">Inquiries</h1>
<div className="flex flex-wrap gap-2">
<Input
placeholder="Search name / phone / email / subject..."
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
className="w-[260px]"
/>
<Button variant="outline" onClick={fetchAll} disabled={loading}>
<RefreshCw className="mr-2 h-4 w-4" />
Refresh
</Button>
<Button variant="outline" onClick={handleExport}>
<Download className="mr-2 h-4 w-4" />
Export
</Button>
</div>
</div>
<Card>
<CardHeader>
<CardTitle>Inquiry List</CardTitle>
</CardHeader>
<CardContent>
<div className="overflow-x-auto">
<Table className="min-w-[900px]">
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead>Name</TableHead>
<TableHead>Phone</TableHead>
<TableHead>Email</TableHead>
<TableHead>Subject</TableHead>
<TableHead>Message</TableHead>
<TableHead>Date</TableHead>
<TableHead>Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{loading ? (
<TableRow>
<TableCell colSpan={8} className="text-center">
<Loader2 className="h-6 w-6 animate-spin mx-auto" />
</TableCell>
</TableRow>
) : filteredInquiries.length === 0 ? (
<TableRow>
<TableCell colSpan={8} className="text-center">
No inquiries found
</TableCell>
</TableRow>
) : (
filteredInquiries.map((item) => (
<TableRow key={item.id}>
<TableCell>{item.id}</TableCell>
<TableCell>{item.fullName}</TableCell>
<TableCell>{item.number}</TableCell>
<TableCell>{item.emailId}</TableCell>
<TableCell>{item.subject}</TableCell>
<TableCell className="max-w-[250px] whitespace-normal">
{item.message}
</TableCell>
<TableCell>
{new Date(item.createdAt).toLocaleDateString()}
</TableCell>
<TableCell>
<Button
size="sm"
variant="destructive"
onClick={() => handleDelete(item.id)}>
<Trash className="h-4 w-4" />
</Button>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</div>
</CardContent>
</Card>
</div>
);
}