import { useState, useEffect, useCallback, useMemo } from "react"; import { AxiosError } from "axios"; import { getHealthPackagesApi, getHealthCategoriesApi, createHealthPackageApi, updateHealthPackageApi, createCategoryApi, updateCategoryApi, deleteCategoryApi, HealthPackage, HealthCategory, } from "@/api/healthCheck"; import PackageInquiriesTab from "@/components/PackageInquiriesTab/PackageInquiriesTab"; 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 { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Badge } from "@/components/ui/badge"; import { Switch } from "@/components/ui/switch"; import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Loader2, RefreshCw, Plus, Pencil, ChevronLeft, ChevronRight, LayoutGrid, Eye, Trash2, } from "lucide-react"; export default function HealthPackagePage() { const [packages, setPackages] = useState([]); const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); // Modals const [packageModal, setPackageModal] = useState(false); const [categoryModal, setCategoryModal] = useState(false); const [viewModal, setViewModal] = useState(false); // States const [selectedPackage, setSelectedPackage] = useState( null, ); const [editingPackage, setEditingPackage] = useState( null, ); const [editingCategory, setEditingCategory] = useState( null, ); // Filters & Pagination const [searchText, setSearchText] = useState(""); const [filterCategory, setFilterCategory] = useState(""); const [currentPage, setCurrentPage] = useState(1); const itemsPerPage = 10; // Forms const [pkgForm, setPkgForm] = useState>({ name: "", slug: "", description: "", price: 0, discountedPrice: 0, categoryId: 0, isActive: true, sortOrder: 1000, }); const [inclusionsList, setInclusionsList] = useState([ { id: Date.now(), category: "", items: "" }, ]); const [catForm, setCatForm] = useState({ name: "", slug: "", sortOrder: 1000, isActive: true, }); const fetchData = useCallback(async () => { setLoading(true); setError(""); try { const [p, c] = await Promise.all([ getHealthPackagesApi(), getHealthCategoriesApi(), ]); setPackages(p.data || []); setCategories(c.data || []); } catch (err) { if (err instanceof AxiosError) { setError(err.response?.data?.message || "Failed to load data"); } else { setError("Something went wrong"); } } finally { setLoading(false); } }, []); useEffect(() => { fetchData(); }, [fetchData]); // --- Package Filtering & Pagination --- const filteredPackages = useMemo(() => { return packages.filter((pkg) => { const matchesSearch = pkg.name.toLowerCase().includes(searchText.toLowerCase()) || pkg.category?.name.toLowerCase().includes(searchText.toLowerCase()); const matchesCat = filterCategory ? pkg.categoryId === Number(filterCategory) : true; return matchesSearch && matchesCat; }); }, [packages, searchText, filterCategory]); useEffect(() => { setCurrentPage(1); }, [searchText, filterCategory]); const totalPages = Math.ceil(filteredPackages.length / itemsPerPage); const indexOfLastItem = currentPage * itemsPerPage; const indexOfFirstItem = indexOfLastItem - itemsPerPage; const currentItems = filteredPackages.slice( indexOfFirstItem, indexOfLastItem, ); // --- Actions --- const handleToggleStatus = async (pkg: HealthPackage) => { if (!pkg.id) return; try { await updateHealthPackageApi(pkg.id, { isActive: !pkg.isActive }); fetchData(); } catch (err) { console.error("Failed to update status", err); } }; const handleToggleCategoryStatus = async (cat: HealthCategory) => { if (!cat.id) return; try { if (cat.isActive) { const proceed = window.confirm( "Hiding this category will also hide all packages inside it. Proceed?", ); if (!proceed) return; } await updateCategoryApi(cat.id, { isActive: !cat.isActive }); fetchData(); } catch (err) { console.error("Failed to update category status", err); } }; const openAddPackage = () => { setEditingPackage(null); setPkgForm({ name: "", slug: "", description: "", price: 0, discountedPrice: 0, categoryId: categories[0]?.id || 0, isActive: true, sortOrder: 1000, }); setInclusionsList([{ id: Date.now(), category: "", items: "" }]); setPackageModal(true); }; const openEditPackage = (pkg: any) => { setEditingPackage(pkg); setPkgForm(pkg); if ( pkg.inclusions && typeof pkg.inclusions === "object" && !Array.isArray(pkg.inclusions) ) { const formattedList = Object.entries(pkg.inclusions).map( ([cat, items], idx) => ({ id: Date.now() + idx, category: cat, items: (items as string[]).join(", "), }), ); setInclusionsList( formattedList.length ? formattedList : [{ id: Date.now(), category: "", items: "" }], ); } else { setInclusionsList([{ id: Date.now(), category: "", items: "" }]); } setPackageModal(true); }; const handleAddInclusionField = () => { setInclusionsList([ ...inclusionsList, { id: Date.now(), category: "", items: "" }, ]); }; const handleRemoveInclusionField = (id: number) => { setInclusionsList(inclusionsList.filter((item) => item.id !== id)); }; const handleUpdateInclusionField = ( id: number, field: string, value: string, ) => { setInclusionsList( inclusionsList.map((item) => item.id === id ? { ...item, [field]: value } : item, ), ); }; const savePackage = async () => { try { // Convert the dynamic array back into the required JSON object format const parsedInclusions: Record = {}; inclusionsList.forEach((entry) => { const catName = entry.category.trim(); if (catName) { parsedInclusions[catName] = entry.items .split(",") .map((i) => i.trim()) .filter(Boolean); } }); const finalData = { ...pkgForm, inclusions: parsedInclusions }; if (editingPackage?.id) { const changedFields: Record = {}; Object.keys(finalData).forEach((key) => { const k = key as keyof HealthPackage; if ( JSON.stringify(finalData[k]) !== JSON.stringify(editingPackage[k]) ) { changedFields[k] = finalData[k]; } }); delete changedFields.id; delete changedFields.category; if (Object.keys(changedFields).length === 0) { setPackageModal(false); return; } await updateHealthPackageApi(editingPackage.id, changedFields); } else { await createHealthPackageApi(finalData); } setPackageModal(false); fetchData(); } catch (err) { console.error(err); } }; const saveCategory = async () => { try { if (editingCategory?.id) { const changedFields: Record = {}; Object.keys(catForm).forEach((key) => { const k = key as keyof HealthCategory; if (catForm[k] !== editingCategory[k]) { changedFields[k] = catForm[k]; } }); delete changedFields.id; delete changedFields._count; if (Object.keys(changedFields).length === 0) { setCategoryModal(false); return; } await updateCategoryApi( editingCategory.id, changedFields as Partial, ); } else { await createCategoryApi(catForm as any); } setCategoryModal(false); fetchData(); } catch (err) { console.error(err); } }; const deleteCategory = async (id: number) => { if (confirm("Delete this category? Ensure no packages are linked to it.")) { await deleteCategoryApi(id); fetchData(); } }; return (

Health Packages

setSearchText(e.target.value)} className="w-[250px] text-base" />
{error && (
{error}
)} Packages Categories Inquiries {/* PACKAGES TAB */} Package List
Priority Package Details Category Pricing Status Actions {loading ? ( ) : currentItems.length === 0 ? ( No packages found ) : ( currentItems.map((pkg) => ( {pkg.sortOrder}
{pkg.name}
/{pkg.slug}
{pkg.category?.name}
₹{pkg.discountedPrice || pkg.price}
{pkg.discountedPrice && pkg.discountedPrice < pkg.price && (
₹{pkg.price}
)}
handleToggleStatus(pkg)} /> {pkg.isActive ? "Active" : "Hidden"}
)) )}
{!loading && filteredPackages.length > 0 && (
Showing{" "} {indexOfFirstItem + 1} {" "} to{" "} {Math.min(indexOfLastItem, filteredPackages.length)} {" "} of{" "} {filteredPackages.length} {" "} packages
Page {currentPage} of {totalPages}
)}
{/* CATEGORIES TAB */} Category List
Priority Category Name Status Actions {categories.map((cat) => ( {cat.sortOrder} {cat.name}
handleToggleCategoryStatus(cat) } /> {cat.isActive ? "Active" : "Hidden"}
))}
{/* --- PACKAGE MODAL --- */} {editingPackage ? "Edit Package" : "Add Package"}

Profile & Pricing

setPkgForm({ ...pkgForm, isActive: val }) } />
setPkgForm({ ...pkgForm, sortOrder: Number(e.target.value), }) } className="text-base" />
setPkgForm({ ...pkgForm, name: e.target.value }) } className="text-base" />
setPkgForm({ ...pkgForm, slug: e.target.value }) } className="text-base" />
setPkgForm({ ...pkgForm, price: Number(e.target.value), }) } className="text-base" />
setPkgForm({ ...pkgForm, discountedPrice: Number(e.target.value), }) } className="text-base" />

Details & Inclusions