import React, { useState, useEffect, useRef } from "react"; import { useNavigate } from "react-router-dom"; import axios from "axios"; import { LayoutDashboard, Package, Wrench, ShoppingCart, Users, BarChart3, Settings, AlertTriangle, TrendingUp, DollarSign, Calendar, Download, Plus, Edit, Trash2, Eye, ChevronRight, ChevronLeft, Search, Filter, RefreshCw, FileText, FileSpreadsheet, Info, Image, Printer, CheckCircle, Clock, } from "lucide-react"; import { Button } from "../components/ui/button"; import { Input } from "../components/ui/input"; import { Badge } from "../components/ui/badge"; import { Separator } from "../components/ui/separator"; import { Tabs, TabsContent, TabsList, TabsTrigger, } from "../components/ui/tabs"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../components/ui/select"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter, } from "../components/ui/dialog"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "../components/ui/alert-dialog"; import { Label } from "../components/ui/label"; import { Textarea } from "../components/ui/textarea"; import { useAuth } from "../context/AuthContext"; import { toast } from "sonner"; import RichTextEditor from "../components/RichTextEditor"; import ImageUploadManager from "../components/ImageUploadManager"; import MediaManager from "../components/MediaManager"; import { clearCache } from "../utils/apiCache"; const API = `${process.env.REACT_APP_BACKEND_URL}/api`; const statusColors = { pending: "bg-yellow-500", processing: "bg-blue-500", layaway: "bg-purple-500", shipped: "bg-cyan-500", delivered: "bg-green-500", cancelled: "bg-red-500", refunded: "bg-orange-500", on_hold: "bg-gray-500", }; const AdminDashboard = () => { const { user, token, isAuthenticated } = useAuth(); const navigate = useNavigate(); const [activeTab, setActiveTab] = useState("dashboard"); const [loading, setLoading] = useState(true); // Dashboard data const [dashboardData, setDashboardData] = useState(null); // Products state const [products, setProducts] = useState([]); const [productDialog, setProductDialog] = useState(false); const [editingProduct, setEditingProduct] = useState(null); const [productForm, setProductForm] = useState({ name: "", description: "", price: "", category: "", image_url: "", stock: "", brand: "", images: [], }); // Services state const [services, setServices] = useState([]); const [serviceDialog, setServiceDialog] = useState(false); const [editingService, setEditingService] = useState(null); const [serviceForm, setServiceForm] = useState({ name: "", description: "", price: "", duration: "", image_url: "", category: "", images: [], }); // Orders state const [orders, setOrders] = useState([]); const [orderStatusFilter, setOrderStatusFilter] = useState("all"); const [selectedOrder, setSelectedOrder] = useState(null); const [newStatus, setNewStatus] = useState(""); const [statusNotes, setStatusNotes] = useState(""); const [trackingNumber, setTrackingNumber] = useState(""); // Inventory state const [inventory, setInventory] = useState([]); const [adjustDialog, setAdjustDialog] = useState(false); const [adjustProduct, setAdjustProduct] = useState(null); const [adjustQty, setAdjustQty] = useState(0); const [adjustNotes, setAdjustNotes] = useState(""); const [inventorySearch, setInventorySearch] = useState(""); const [inventoryCategory, setInventoryCategory] = useState("all"); const [inventoryItemsPerPage, setInventoryItemsPerPage] = useState(20); const [inventoryCurrentPage, setInventoryCurrentPage] = useState(1); // Reports state const [reportPeriod, setReportPeriod] = useState("monthly"); const [salesReport, setSalesReport] = useState(null); // Bookings state const [bookings, setBookings] = useState([]); const [bookingCompleteDialog, setBookingCompleteDialog] = useState(false); const [selectedBooking, setSelectedBooking] = useState(null); const [bookingCompleteForm, setBookingCompleteForm] = useState({ diagnosis: "", work_performed: "", technician_notes: "", service_cost: "", paid: true, device_model: "", serial_number: "", product_number: "", screen_size: "", }); const [receiptData, setReceiptData] = useState(null); const [showReceiptDialog, setShowReceiptDialog] = useState(false); const [isEditMode, setIsEditMode] = useState(false); const receiptRef = useRef(null); // Categories state const [categories, setCategories] = useState([]); const [categoryDialog, setCategoryDialog] = useState(false); const [editingCategory, setEditingCategory] = useState(null); const [categoryForm, setCategoryForm] = useState({ name: "", description: "", }); // Users state const [users, setUsers] = useState([]); const [usersTotal, setUsersTotal] = useState(0); const [userDialog, setUserDialog] = useState(false); const [editingUser, setEditingUser] = useState(null); const [userForm, setUserForm] = useState({ name: "", email: "", password: "", role: "user", is_active: true, }); const [userSearch, setUserSearch] = useState(""); const [userRoleFilter, setUserRoleFilter] = useState(""); const [userStatusFilter, setUserStatusFilter] = useState(""); const [usersPerPage, setUsersPerPage] = useState(20); const [currentUsersPage, setCurrentUsersPage] = useState(1); // Delete confirmation state (generic for all delete operations) const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); const [productToDelete, setProductToDelete] = useState(null); const [confirmDialog, setConfirmDialog] = useState({ open: false, title: "Confirm Action", message: "Are you sure you want to proceed?", onConfirm: null, }); // About Page state const [aboutContent, setAboutContent] = useState([]); const [teamMembers, setTeamMembers] = useState([]); const [companyValues, setCompanyValues] = useState([]); const [aboutDialog, setAboutDialog] = useState(false); const [aboutEditType, setAboutEditType] = useState(""); // "content", "team", "value" const [editingAboutItem, setEditingAboutItem] = useState(null); const [aboutForm, setAboutForm] = useState({ section: "", title: "", subtitle: "", content: "", image_url: "", data: null, display_order: 0, is_active: true, name: "", role: "", bio: "", email: "", linkedin: "", description: "", icon: "", }); // Helper function to show confirmation dialog const showConfirmDialog = (title, message, onConfirm) => { setConfirmDialog({ open: true, title, message, onConfirm, }); }; const handleConfirmAction = () => { if (confirmDialog.onConfirm) { confirmDialog.onConfirm(); } setConfirmDialog({ ...confirmDialog, open: false, onConfirm: null }); }; const handleCancelConfirm = () => { setConfirmDialog({ ...confirmDialog, open: false, onConfirm: null }); }; useEffect(() => { // Only fetch if properly authenticated as admin if (isAuthenticated && user?.role === "admin" && token) { fetchDashboardData(); fetchCategories(); // Fetch categories on mount for product form dropdown } else if (isAuthenticated && user && user.role !== "admin") { // User is authenticated but not admin toast.error("Admin access required"); navigate("/"); } }, [isAuthenticated, user, token, navigate]); useEffect(() => { if (activeTab === "products") fetchProducts(); if (activeTab === "services") fetchServices(); if (activeTab === "orders") fetchOrders(); if (activeTab === "inventory") fetchInventory(); if (activeTab === "reports") fetchReports(); if (activeTab === "bookings") fetchBookings(); if (activeTab === "categories") fetchCategories(); if (activeTab === "users") fetchUsers(); if (activeTab === "about") fetchAboutData(); }, [activeTab]); // Fetch users when filters or pagination change useEffect(() => { if (activeTab === "users") { fetchUsers(); } }, [ currentUsersPage, usersPerPage, userSearch, userRoleFilter, userStatusFilter, ]); const fetchDashboardData = async () => { try { // Validate token exists if (!token) { console.warn("No authentication token available"); toast.error("Authentication required"); navigate("/login"); return; } const response = await axios.get(`${API}/admin/dashboard`, { headers: { Authorization: `Bearer ${token}` }, timeout: 10000, // 10 second timeout }); // Validate response structure if (response.data && response.data.stats) { setDashboardData(response.data); // Show warning if partial data if (response.data.error) { toast.warning(response.data.error); } } else { console.error("Invalid dashboard response structure:", response.data); toast.error("Received invalid dashboard data"); } } catch (error) { console.error("Failed to fetch dashboard:", error); // Detailed error handling if (error.response) { // Server responded with error if (error.response.status === 401) { toast.error("Session expired. Please login again."); navigate("/login"); } else if (error.response.status === 403) { toast.error("Admin access required"); navigate("/"); } else if (error.response.status === 500) { toast.error("Server error. Please try again later."); } else { toast.error( `Failed to load dashboard data (Error ${error.response.status})`, ); } } else if (error.request) { // Request made but no response toast.error("Cannot connect to server. Please check your connection."); } else { // Other errors toast.error("Failed to load dashboard data"); } } finally { setLoading(false); } }; const fetchProducts = async () => { try { const response = await axios.get( `${API}/admin/products?include_inactive=true`, { headers: { Authorization: `Bearer ${token}` }, }, ); setProducts(response.data); } catch (error) { toast.error("Failed to load products"); } }; const fetchServices = async () => { try { const response = await axios.get( `${API}/admin/services?include_inactive=true`, { headers: { Authorization: `Bearer ${token}` }, }, ); setServices(response.data); } catch (error) { toast.error("Failed to load services"); } }; const fetchOrders = async () => { try { const params = orderStatusFilter !== "all" ? `?status=${orderStatusFilter}` : ""; const response = await axios.get(`${API}/admin/orders${params}`, { headers: { Authorization: `Bearer ${token}` }, }); setOrders(response.data); } catch (error) { toast.error("Failed to load orders"); } }; const fetchInventory = async () => { try { const response = await axios.get(`${API}/admin/inventory`, { headers: { Authorization: `Bearer ${token}` }, }); setInventory(response.data); } catch (error) { toast.error("Failed to load inventory"); } }; const fetchReports = async () => { try { const response = await axios.get( `${API}/admin/reports/sales?period=${reportPeriod}`, { headers: { Authorization: `Bearer ${token}` }, }, ); setSalesReport(response.data); } catch (error) { toast.error("Failed to load reports"); } }; const fetchBookings = async () => { try { const response = await axios.get(`${API}/admin/bookings`, { headers: { Authorization: `Bearer ${token}` }, }); setBookings(response.data); } catch (error) { toast.error("Failed to load bookings"); } }; const fetchCategories = async () => { try { const response = await axios.get(`${API}/admin/categories`, { headers: { Authorization: `Bearer ${token}` }, }); setCategories(response.data); } catch (error) { toast.error("Failed to load categories"); } }; const fetchUsers = async () => { try { const skip = (currentUsersPage - 1) * usersPerPage; const response = await axios.get(`${API}/admin/users`, { headers: { Authorization: `Bearer ${token}` }, params: { skip, limit: usersPerPage, search: userSearch, role: userRoleFilter, status: userStatusFilter, }, }); setUsers(response.data.users); setUsersTotal(response.data.total); } catch (error) { toast.error("Failed to load users"); } }; const handleUserSubmit = async () => { try { const data = { ...userForm, role: userForm.role.toLowerCase(), }; if (editingUser) { // Update user - only include password if it's not empty if (!data.password || data.password.trim() === "") { delete data.password; // Don't send empty password } await axios.put(`${API}/admin/users/${editingUser.id}`, data, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("User updated successfully"); } else { // Create new user if (!data.password) { toast.error("Password is required for new users"); return; } await axios.post(`${API}/admin/users`, data, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("User created"); } setUserDialog(false); setEditingUser(null); setUserForm({ name: "", email: "", password: "", role: "user", is_active: true, }); fetchUsers(); } catch (error) { toast.error(error.response?.data?.detail || "Failed to save user"); } }; const handleToggleUserActive = async (userId) => { try { await axios.put( `${API}/admin/users/${userId}/toggle-active`, {}, { headers: { Authorization: `Bearer ${token}` }, }, ); toast.success("User status updated"); fetchUsers(); } catch (error) { toast.error( error.response?.data?.detail || "Failed to update user status", ); } }; const handleDeleteUser = async (userId) => { try { await axios.delete(`${API}/admin/users/${userId}`, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("User deleted"); fetchUsers(); } catch (error) { toast.error(error.response?.data?.detail || "Failed to delete user"); } }; // About Page Functions const fetchAboutData = async () => { try { const [contentRes, teamRes, valuesRes] = await Promise.all([ axios.get(`${API}/admin/about/content`, { headers: { Authorization: `Bearer ${token}` }, }), axios.get(`${API}/admin/about/team`, { headers: { Authorization: `Bearer ${token}` }, }), axios.get(`${API}/admin/about/values`, { headers: { Authorization: `Bearer ${token}` }, }), ]); setAboutContent(contentRes.data); setTeamMembers(teamRes.data); setCompanyValues(valuesRes.data); } catch (error) { toast.error("Failed to fetch about page data"); } }; const handleSaveAboutItem = async () => { try { if (aboutEditType === "content") { const data = { section: aboutForm.section, title: aboutForm.title || null, subtitle: aboutForm.subtitle || null, content: aboutForm.content || null, image_url: aboutForm.image_url || null, data: aboutForm.data, display_order: aboutForm.display_order, is_active: aboutForm.is_active, }; if (editingAboutItem) { await axios.put( `${API}/admin/about/content/${editingAboutItem.id}`, data, { headers: { Authorization: `Bearer ${token}` } }, ); toast.success("Content updated"); } else { await axios.post(`${API}/admin/about/content`, data, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Content created"); } } else if (aboutEditType === "team") { const data = { name: aboutForm.name, role: aboutForm.role, bio: aboutForm.bio || null, image_url: aboutForm.image_url || null, email: aboutForm.email || null, linkedin: aboutForm.linkedin || null, display_order: aboutForm.display_order, is_active: aboutForm.is_active, }; if (editingAboutItem) { await axios.put( `${API}/admin/about/team/${editingAboutItem.id}`, data, { headers: { Authorization: `Bearer ${token}` } }, ); toast.success("Team member updated"); } else { await axios.post(`${API}/admin/about/team`, data, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Team member created"); } } else if (aboutEditType === "value") { const data = { title: aboutForm.title, description: aboutForm.description, icon: aboutForm.icon, display_order: aboutForm.display_order, is_active: aboutForm.is_active, }; if (editingAboutItem) { await axios.put( `${API}/admin/about/values/${editingAboutItem.id}`, data, { headers: { Authorization: `Bearer ${token}` } }, ); toast.success("Company value updated"); } else { await axios.post(`${API}/admin/about/values`, data, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Company value created"); } } setAboutDialog(false); setEditingAboutItem(null); fetchAboutData(); } catch (error) { toast.error( error.response?.data?.detail || "Failed to save about page item", ); } }; const handleDeleteAboutItem = (id, type) => { const itemName = type === "content" ? "content section" : type === "team" ? "team member" : "company value"; showConfirmDialog( "Delete Item", `Are you sure you want to delete this ${itemName}? This action cannot be undone.`, async () => { try { if (type === "content") { await axios.delete(`${API}/admin/about/content/${id}`, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Content deleted"); } else if (type === "team") { await axios.delete(`${API}/admin/about/team/${id}`, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Team member deleted"); } else if (type === "value") { await axios.delete(`${API}/admin/about/values/${id}`, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Company value deleted"); } fetchAboutData(); } catch (error) { toast.error( error.response?.data?.detail || "Failed to delete about page item", ); } }, ); }; // Product CRUD const handleProductSubmit = async () => { try { const price = parseFloat(productForm.price); if (isNaN(price) || price <= 0) { toast.error("Price must be greater than 0"); return; } const data = { ...productForm, price: price, stock: parseInt(productForm.stock) || 10, }; if (editingProduct) { await axios.put(`${API}/admin/products/${editingProduct.id}`, data, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Product updated"); } else { await axios.post(`${API}/admin/products`, data, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Product created"); } clearCache("products-"); setProductDialog(false); setEditingProduct(null); setProductForm({ name: "", description: "", price: "", category: "", image_url: "", stock: "", brand: "", images: [], }); fetchProducts(); } catch (error) { toast.error("Failed to save product"); } }; const handleDeleteProduct = async () => { if (!productToDelete) return; try { await axios.delete(`${API}/admin/products/${productToDelete.id}`, { headers: { Authorization: `Bearer ${token}` }, }); toast.success(`"${productToDelete.name}" has been deleted`); clearCache("products-"); setDeleteConfirmOpen(false); setProductToDelete(null); fetchProducts(); } catch (error) { toast.error("Failed to delete product"); setDeleteConfirmOpen(false); setProductToDelete(null); } }; // Service CRUD const handleServiceSubmit = async () => { try { const price = parseFloat(serviceForm.price) || 0; if (isNaN(price) || price < 0) { toast.error("Price cannot be negative"); return; } const data = { ...serviceForm, price: price, }; if (editingService) { await axios.put(`${API}/admin/services/${editingService.id}`, data, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Service updated"); } else { await axios.post(`${API}/admin/services`, data, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Service created"); } clearCache("services-"); setServiceDialog(false); setEditingService(null); setServiceForm({ name: "", description: "", price: "", duration: "", image_url: "", category: "", images: [], }); fetchServices(); } catch (error) { toast.error("Failed to save service"); } }; const handleDeleteService = (id) => { showConfirmDialog( "Delete Service", "Are you sure you want to delete this service? This action cannot be undone.", async () => { try { await axios.delete(`${API}/admin/services/${id}`, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Service deleted"); clearCache("services-"); fetchServices(); } catch (error) { toast.error("Failed to delete service"); } }, ); }; // Booking completion const handleOpenCompleteDialog = (booking) => { setSelectedBooking(booking); setBookingCompleteForm({ diagnosis: booking.diagnosis || "", work_performed: booking.work_performed || "", technician_notes: booking.technician_notes || "", service_cost: booking.service_cost || "", paid: booking.paid !== undefined ? booking.paid : true, device_model: booking.device_model || "", serial_number: booking.serial_number || "", product_number: booking.product_number || "", screen_size: booking.screen_size || "", }); setIsEditMode(false); setBookingCompleteDialog(true); }; const handleDeleteBooking = (booking) => { showConfirmDialog( "Delete Booking", `Are you sure you want to delete the booking for "${booking.name}"? This action cannot be undone.`, async () => { try { await axios.delete(`${API}/admin/bookings/${booking.id}`, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Booking deleted successfully"); fetchBookings(); } catch (error) { toast.error("Failed to delete booking"); } }, ); }; const handleCompleteBooking = async () => { if (!selectedBooking) return; try { const data = { ...bookingCompleteForm, service_cost: bookingCompleteForm.service_cost ? parseFloat(bookingCompleteForm.service_cost) : null, }; await axios.put( `${API}/admin/bookings/${selectedBooking.id}/complete`, data, { headers: { Authorization: `Bearer ${token}` } }, ); toast.success("Service marked as completed!"); setBookingCompleteDialog(false); setSelectedBooking(null); fetchBookings(); } catch (error) { toast.error("Failed to complete booking"); } }; const handleUpdateBookingStatus = async (bookingId, status) => { try { await axios.put( `${API}/admin/bookings/${bookingId}/status?status=${status}`, {}, { headers: { Authorization: `Bearer ${token}` } }, ); toast.success(`Booking status updated to ${status}`); fetchBookings(); } catch (error) { toast.error("Failed to update status"); } }; const handlePrintReceipt = async (booking) => { try { const response = await axios.get( `${API}/admin/bookings/${booking.id}/receipt`, { headers: { Authorization: `Bearer ${token}` } }, ); setReceiptData(response.data); setShowReceiptDialog(true); } catch (error) { toast.error("Failed to load receipt data"); } }; const printReceipt = () => { if (!receiptData) return; const printWindow = window.open("", "_blank"); printWindow.document.write(` Service Receipt - PromptTech Solutions

${receiptData.company?.address || "Belmopan City, Cayo District, Belize"}

Phone: ${receiptData.company?.phone || "+501 638-6318"} | Email: ${receiptData.company?.email || "prompttechbz@gmail.com"}

SERVICE RECEIPT

Customer Information

Name: ${receiptData.name || ""}
Phone: ${receiptData.phone || ""}
Email: ${receiptData.email || ""}
Date Booked: ${receiptData.created_at ? new Date(receiptData.created_at).toLocaleDateString() : ""}

Service Details

Service: ${receiptData.service_name || ""}
Preferred Date: ${receiptData.preferred_date || ""}
Status: ${receiptData.status || ""}
${receiptData.completed_at ? `
Completed: ${new Date(receiptData.completed_at).toLocaleDateString()}
` : ""}
${ receiptData.device_model || receiptData.serial_number || receiptData.product_number || receiptData.screen_size ? `

Device Information

${receiptData.device_model ? `
Model: ${receiptData.device_model}
` : ""} ${receiptData.screen_size ? `
Screen Size: ${receiptData.screen_size}
` : ""} ${receiptData.serial_number ? `
Serial #: ${receiptData.serial_number}
` : ""} ${receiptData.product_number ? `
Product #: ${receiptData.product_number}
` : ""}
` : "" } ${ receiptData.diagnosis || receiptData.work_performed ? `

Service Report

${ receiptData.diagnosis ? `

Diagnosis:

${receiptData.diagnosis}

` : "" } ${ receiptData.work_performed ? `

Work Performed:

${receiptData.work_performed}

` : "" }
` : "" } ${ receiptData.notes ? `

Customer Notes

${receiptData.notes}

` : "" }
Total: $${receiptData.final_cost?.toFixed(2) || "0.00"}
`); printWindow.document.close(); // Small delay to ensure styles are loaded before printing setTimeout(() => { printWindow.print(); }, 250); }; // Order status update const handleUpdateOrderStatus = async () => { if (!selectedOrder || !newStatus) return; try { await axios.put( `${API}/admin/orders/${selectedOrder}/status`, { status: newStatus, notes: statusNotes, tracking_number: trackingNumber || null, }, { headers: { Authorization: `Bearer ${token}` }, }, ); toast.success("Order status updated"); setSelectedOrder(null); setNewStatus(""); setStatusNotes(""); setTrackingNumber(""); fetchOrders(); } catch (error) { toast.error("Failed to update order"); } }; // Inventory adjustment const handleAdjustInventory = async () => { if (!adjustProduct) return; try { await axios.post( `${API}/admin/inventory/${adjustProduct.id}/adjust`, { quantity_change: adjustQty, notes: adjustNotes, }, { headers: { Authorization: `Bearer ${token}` }, }, ); toast.success("Inventory adjusted"); setAdjustDialog(false); setAdjustProduct(null); setAdjustQty(0); setAdjustNotes(""); fetchInventory(); } catch (error) { toast.error("Failed to adjust inventory"); } }; // Toggle product active status const handleToggleActive = async (product) => { try { await axios.put( `${API}/admin/products/${product.id}`, { is_active: !product.is_active }, { headers: { Authorization: `Bearer ${token}` } }, ); toast.success( `Product ${!product.is_active ? "activated" : "deactivated"}`, ); fetchInventory(); } catch (error) { toast.error("Failed to update product status"); } }; // Export reports const handleExportCSV = async (type) => { try { const response = await axios.get( `${API}/admin/reports/export/csv?report_type=${type}&period=${reportPeriod}`, { headers: { Authorization: `Bearer ${token}` }, responseType: "blob", }, ); const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement("a"); link.href = url; link.setAttribute("download", `${type}_report.csv`); document.body.appendChild(link); link.click(); link.remove(); toast.success("CSV exported"); } catch (error) { toast.error("Failed to export CSV"); } }; const handleExportPDF = async (type) => { try { const response = await axios.get( `${API}/admin/reports/export/pdf?report_type=${type}&period=${reportPeriod}`, { headers: { Authorization: `Bearer ${token}` }, responseType: "blob", }, ); const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement("a"); link.href = url; link.setAttribute("download", `${type}_report.pdf`); document.body.appendChild(link); link.click(); link.remove(); toast.success("PDF exported"); } catch (error) { toast.error("Failed to export PDF"); } }; if (!isAuthenticated || user?.role !== "admin") { return (

Access Denied

You need admin privileges to access this page.

); } // Category CRUD const handleCategorySubmit = async () => { try { if (editingCategory) { await axios.put( `${API}/admin/categories/${editingCategory.id}`, categoryForm, { headers: { Authorization: `Bearer ${token}` }, }, ); toast.success("Category updated"); } else { await axios.post(`${API}/admin/categories`, categoryForm, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Category created"); } setCategoryDialog(false); setEditingCategory(null); setCategoryForm({ name: "", description: "" }); fetchCategories(); } catch (error) { toast.error("Failed to save category"); } }; const handleDeleteCategory = (id) => { showConfirmDialog( "Delete Category", "Are you sure you want to delete this category? This action cannot be undone.", async () => { try { await axios.delete(`${API}/admin/categories/${id}`, { headers: { Authorization: `Bearer ${token}` }, }); toast.success("Category deleted"); fetchCategories(); } catch (error) { toast.error("Failed to delete category"); } }, ); }; if (loading) { return (
); } return (

Admin Dashboard

Manage your store

Dashboard Products Services Orders Inventory Bookings Categories Users Reports Media About Page {/* Dashboard Tab */} {dashboardData && (
{/* Stats Grid */}
Total Revenue

${dashboardData.stats.total_revenue.toFixed(2)}

Total Orders

{dashboardData.stats.total_orders}

Products

{dashboardData.stats.total_products}

Users

{dashboardData.stats.total_users}

{/* Today's Stats */}

Today's Performance

Orders

{dashboardData.stats.today_orders}

Revenue

${(dashboardData.stats.today_revenue || 0).toFixed(2)}

Monthly Stats

Revenue

$ {(dashboardData.stats.monthly_revenue || 0).toFixed( 2, )}

Pending Bookings

{dashboardData.stats.pending_bookings}

{/* Low Stock Alert */} {dashboardData.low_stock_products.length > 0 && (

Low Stock Alert

{dashboardData.low_stock_products.map((product) => (
{product.name} {product.stock} left
))}
)} {/* Recent Orders */}

Recent Orders

{dashboardData.recent_orders.slice(0, 5).map((order) => (

{order.id.slice(0, 8)}...

{new Date(order.created_at).toLocaleString()}

${order.total.toFixed(2)} {order.status}
))}
)}
{/* Products Tab */}

Products Management

{editingProduct ? "Edit Product" : "Add Product"}
setProductForm({ ...productForm, name: e.target.value, }) } />
setProductForm({ ...productForm, description: html, }) } placeholder="Enter product description with rich formatting..." />
setProductForm({ ...productForm, price: e.target.value, }) } />
setProductForm({ ...productForm, stock: e.target.value, }) } />
setProductForm({ ...productForm, brand: e.target.value, }) } />
setProductForm({ ...productForm, images }) } token={token} />
setProductForm({ ...productForm, image_url: e.target.value, }) } />

Use the image uploader above for best results. This field is for backwards compatibility.

{products.map((product) => ( ))}
Product Category Price Stock Status Actions
0 ? ( product.images[0].url || product.images[0].image_url ).startsWith("/uploads") ? `${process.env.REACT_APP_BACKEND_URL}${ product.images[0].url || product.images[0].image_url }` : product.images[0].url || product.images[0].image_url : product.image_url } alt={product.name} className="w-10 h-10 rounded object-cover bg-muted" onError={(e) => { e.target.src = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Crect x="3" y="3" width="18" height="18" rx="2" /%3E%3Ccircle cx="8.5" cy="8.5" r="1.5" /%3E%3Cpath d="M21 15l-5-5L5 21" /%3E%3C/svg%3E'; }} /> {product.name}
{product.category} ${product.price.toFixed(2)} {product.stock} {product.is_active ? "Active" : "Inactive"}
{/* Services Tab */}

Services Management

{editingService ? "Edit Service" : "Add Service"}
setServiceForm({ ...serviceForm, name: e.target.value, }) } />
setServiceForm({ ...serviceForm, price: e.target.value, }) } />
setServiceForm({ ...serviceForm, duration: e.target.value, }) } placeholder="e.g. 1-2 hours" />
setServiceForm({ ...serviceForm, description: html, }) } />
setServiceForm({ ...serviceForm, images }) } token={token} />
setServiceForm({ ...serviceForm, image_url: e.target.value, }) } />

Use the image uploader above for best results. This field is for backwards compatibility.

{services.map((service) => ( ))}
Service Category Duration Price Status Actions
0 ? ( service.images[0].url || service.images[0].image_url ).startsWith("/uploads") ? `${process.env.REACT_APP_BACKEND_URL}${ service.images[0].url || service.images[0].image_url }` : service.images[0].url || service.images[0].image_url : service.image_url } alt={service.name} className="w-10 h-10 rounded object-cover bg-muted" onError={(e) => { e.target.src = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Crect x="3" y="3" width="18" height="18" rx="2" /%3E%3Ccircle cx="8.5" cy="8.5" r="1.5" /%3E%3Cpath d="M21 15l-5-5L5 21" /%3E%3C/svg%3E'; }} /> {service.name}
{service.category} {service.duration} ${service.price.toFixed(2)} {service.is_active ? "Active" : "Inactive"}
{/* Orders Tab */}

Orders Management

{orders.map((order) => (

Order ID

{order.id.slice(0, 8)}...

Customer

{order.user_name}

Date

{new Date(order.created_at).toLocaleDateString()}

Total

${order.total.toFixed(2)}

{order.status} Update Order Status
setTrackingNumber(e.target.value) } />