import { useState, useEffect } from "react"; import { Bell, Menu, X, User, LogOut, Home, Briefcase, FileText, Wrench, BarChart3, Settings, ChevronDown, Search, Plus, AlertTriangle, Check, Trash, Edit, Eye, Download, Send, Clock, DollarSign, CreditCard, Package, Users, ShoppingCart, Filter, Calendar, ArrowUpRight, ArrowDownRight, Printer, Save, Lock, Unlock, Upload, RefreshCw, CheckCircle, XCircle, HelpCircle } from "lucide-react"; // Mock data for demonstration const mockCompanies = [ { id: 1, name: "TechSolutions Inc", regNumber: "2020/123456/07", taxNumber: "9876543210", status: "active", contactPerson: "John Smith", email: "john@techsolutions.com", phone: "011 123 4567", address: "123 Tech Street, Johannesburg", createdAt: "2022-01-15" }, { id: 2, name: "ABC Enterprises", regNumber: "2018/654321/07", taxNumber: "1234567890", status: "active", contactPerson: "Sarah Johnson", email: "sarah@abcent.com", phone: "012 345 6789", address: "456 Main Road, Pretoria", createdAt: "2021-03-22" }, { id: 3, name: "XYZ Corporation", regNumber: "2019/987654/07", taxNumber: "5678901234", status: "inactive", contactPerson: "Michael Brown", email: "michael@xyzcorp.com", phone: "021 876 5432", address: "789 Corporate Avenue, Cape Town", createdAt: "2020-11-05" }, { id: 4, name: "Global Services Ltd", regNumber: "2017/456789/07", taxNumber: "3456789012", status: "active", contactPerson: "Lisa Williams", email: "lisa@globalservices.com", phone: "031 234 5678", address: "321 Service Road, Durban", createdAt: "2019-07-18" }, { id: 5, name: "Innovative Solutions", regNumber: "2021/234567/07", taxNumber: "7890123456", status: "pending", contactPerson: "David Miller", email: "david@innovative.com", phone: "041 567 8901", address: "567 Innovation Street, Port Elizabeth", createdAt: "2022-05-30" }, ]; const mockDocuments = [ { id: 1, companyId: 1, name: "Company Registration", type: "pdf", size: "1.2 MB", uploadDate: "2022-01-20", status: "verified" }, { id: 2, companyId: 1, name: "Tax Clearance Certificate", type: "pdf", size: "0.8 MB", uploadDate: "2022-02-15", status: "verified" }, { id: 3, companyId: 1, name: "Banking Confirmation", type: "pdf", size: "0.5 MB", uploadDate: "2022-01-25", status: "pending" }, { id: 4, companyId: 2, name: "Company Registration", type: "pdf", size: "1.5 MB", uploadDate: "2021-04-10", status: "verified" }, { id: 5, companyId: 2, name: "Tax Clearance Certificate", type: "pdf", size: "0.9 MB", uploadDate: "2021-05-05", status: "expired" }, ]; const mockCredentials = [ { id: 1, companyId: 1, type: "CIPC", username: "tech_admin", password: "encrypted_password", lastUpdated: "2022-06-10" }, { id: 2, companyId: 1, type: "SARS", username: "tech_tax", password: "encrypted_password", lastUpdated: "2022-05-20" }, { id: 3, companyId: 1, type: "CSD", username: "tech_procurement", password: "encrypted_password", lastUpdated: "2022-04-15" }, { id: 4, companyId: 2, type: "CIPC", username: "abc_admin", password: "encrypted_password", lastUpdated: "2021-10-12" }, { id: 5, companyId: 2, type: "SARS", username: "abc_tax", password: "encrypted_password", lastUpdated: "2021-11-05" }, ]; const mockClients = [ { id: 1, name: "Department of Education", contactPerson: "James Wilson", email: "james@education.gov.za", phone: "012 345 6789", address: "Government Complex, Pretoria" }, { id: 2, name: "City of Johannesburg", contactPerson: "Patricia Brown", email: "patricia@joburg.gov.za", phone: "011 789 0123", address: "Metro Centre, Braamfontein" }, { id: 3, name: "National Health Services", contactPerson: "Robert Green", email: "robert@health.gov.za", phone: "012 567 8901", address: "Health Park, Pretoria" }, { id: 4, name: "Private Client Corp", contactPerson: "Susan Taylor", email: "susan@privatecorp.co.za", phone: "021 234 5678", address: "100 Corporate Drive, Cape Town" }, { id: 5, name: "SME Solutions", contactPerson: "Thomas Johnson", email: "thomas@smesolutions.co.za", phone: "031 901 2345", address: "25 Small Business Hub, Durban" }, ]; const mockQuotations = [ { id: "QT-2023-001", companyId: 1, clientId: 1, date: "2023-06-15", expiryDate: "2023-07-15", status: "pending", total: 15750.00, items: [ { id: 1, description: "Website Development", quantity: 1, unitPrice: 12000.00 }, { id: 2, description: "SEO Setup", quantity: 1, unitPrice: 3000.00 }, { id: 3, description: "Content Creation", quantity: 5, unitPrice: 150.00 } ]}, { id: "QT-2023-002", companyId: 1, clientId: 2, date: "2023-06-20", expiryDate: "2023-07-20", status: "accepted", total: 28500.00, items: [ { id: 1, description: "Network Infrastructure Upgrade", quantity: 1, unitPrice: 25000.00 }, { id: 2, description: "Technical Support (hours)", quantity: 10, unitPrice: 350.00 } ]}, { id: "QT-2023-003", companyId: 2, clientId: 3, date: "2023-06-25", expiryDate: "2023-07-25", status: "rejected", total: 8750.00, items: [ { id: 1, description: "IT Consultation", quantity: 5, unitPrice: 1200.00 }, { id: 2, description: "Security Assessment", quantity: 1, unitPrice: 2750.00 } ]}, { id: "QT-2023-004", companyId: 2, clientId: 4, date: "2023-07-01", expiryDate: "2023-08-01", status: "pending", total: 42000.00, items: [ { id: 1, description: "Custom Software Development", quantity: 1, unitPrice: 35000.00 }, { id: 2, description: "User Training", quantity: 10, unitPrice: 700.00 } ]}, { id: "QT-2023-005", companyId: 1, clientId: 5, date: "2023-07-05", expiryDate: "2023-08-05", status: "accepted", total: 5600.00, items: [ { id: 1, description: "Cloud Migration", quantity: 1, unitPrice: 4500.00 }, { id: 2, description: "Data Backup Setup", quantity: 1, unitPrice: 1100.00 } ]}, ]; const mockInvoices = [ { id: "INV-2023-001", quotationId: "QT-2023-002", companyId: 1, clientId: 2, date: "2023-07-01", dueDate: "2023-07-31", status: "paid", paymentDate: "2023-07-25", total: 28500.00 }, { id: "INV-2023-002", quotationId: "QT-2023-005", companyId: 1, clientId: 5, date: "2023-07-10", dueDate: "2023-08-09", status: "unpaid", paymentDate: null, total: 5600.00 }, { id: "INV-2023-003", quotationId: null, companyId: 2, clientId: 1, date: "2023-07-15", dueDate: "2023-08-14", status: "partial", paymentDate: "2023-07-30", amountPaid: 5000.00, total: 12500.00 }, { id: "INV-2023-004", quotationId: null, companyId: 2, clientId: 3, date: "2023-07-20", dueDate: "2023-08-19", status: "overdue", paymentDate: null, total: 9800.00 }, { id: "INV-2023-005", quotationId: null, companyId: 1, clientId: 4, date: "2023-07-25", dueDate: "2023-08-24", status: "unpaid", paymentDate: null, total: 15300.00 }, ]; const mockCustomers = [ { id: 1, name: "John Doe", email: "john.doe@example.com", phone: "071 234 5678", address: "123 Main Street, Johannesburg" }, { id: 2, name: "Jane Smith", email: "jane.smith@example.com", phone: "082 345 6789", address: "456 Oak Avenue, Pretoria" }, { id: 3, name: "Michael Johnson", email: "michael.johnson@example.com", phone: "083 456 7890", address: "789 Pine Road, Cape Town" }, { id: 4, name: "Sarah Williams", email: "sarah.williams@example.com", phone: "084 567 8901", address: "321 Cedar Lane, Durban" }, { id: 5, name: "David Brown", email: "david.brown@example.com", phone: "085 678 9012", address: "654 Maple Drive, Bloemfontein" }, ]; const mockRepairTickets = [ { id: "RT-2023-001", customerId: 1, deviceType: "Laptop", brand: "Dell", model: "XPS 15", serialNumber: "DL12345", problem: "Won't boot up, black screen", estimatedCost: 1200.00, createdAt: "2023-07-01", status: "completed", technicianId: 1, completedAt: "2023-07-05", notes: "Replaced faulty RAM module and reset BIOS." }, { id: "RT-2023-002", customerId: 2, deviceType: "Smartphone", brand: "Samsung", model: "Galaxy S21", serialNumber: "SG54321", problem: "Battery drains quickly, overheating", estimatedCost: 800.00, createdAt: "2023-07-03", status: "in-progress", technicianId: 2, completedAt: null, notes: "Diagnosed as faulty battery, replacement ordered." }, { id: "RT-2023-003", customerId: 3, deviceType: "Desktop PC", brand: "HP", model: "Pavilion", serialNumber: "HP67890", problem: "Random crashes and blue screens", estimatedCost: 1500.00, createdAt: "2023-07-05", status: "pending", technicianId: null, completedAt: null, notes: "Initial assessment needed." }, { id: "RT-2023-004", customerId: 4, deviceType: "Printer", brand: "Epson", model: "EcoTank L3150", serialNumber: "EP09876", problem: "Paper jam issues, poor print quality", estimatedCost: 600.00, createdAt: "2023-07-08", status: "in-progress", technicianId: 1, completedAt: null, notes: "Cleaning print heads, checking paper feed mechanism." }, { id: "RT-2023-005", customerId: 5, deviceType: "Tablet", brand: "Apple", model: "iPad Pro", serialNumber: "AP13579", problem: "Cracked screen, touch not responding", estimatedCost: 2200.00, createdAt: "2023-07-10", status: "completed", technicianId: 2, completedAt: "2023-07-15", notes: "Replaced screen assembly, tested all functions." }, ]; const mockInventory = [ { id: 1, name: "Laptop RAM 8GB DDR4", category: "Components", quantity: 15, threshold: 5, unitPrice: 450.00, location: "Shelf A1" }, { id: 2, name: "Laptop SSD 500GB", category: "Components", quantity: 8, threshold: 3, unitPrice: 850.00, location: "Shelf A2" }, { id: 3, name: "Smartphone Battery - Samsung", category: "Components", quantity: 12, threshold: 4, unitPrice: 350.00, location: "Shelf B1" }, { id: 4, name: "Printer Ink Cartridge - Black", category: "Supplies", quantity: 20, threshold: 5, unitPrice: 180.00, location: "Shelf C1" }, { id: 5, name: "Laptop Power Adapter - Universal", category: "Accessories", quantity: 6, threshold: 2, unitPrice: 280.00, location: "Shelf D1" }, { id: 6, name: "Thermal Paste", category: "Supplies", quantity: 25, threshold: 5, unitPrice: 45.00, location: "Shelf C2" }, { id: 7, name: "Screen Protector - Universal", category: "Accessories", quantity: 30, threshold: 10, unitPrice: 50.00, location: "Shelf D2" }, { id: 8, name: "HDMI Cable 2m", category: "Cables", quantity: 18, threshold: 5, unitPrice: 75.00, location: "Shelf E1" }, ]; const mockTechnicians = [ { id: 1, name: "John Doe", email: "john.tech@madonoro.com", phone: "071 234 5678", specialization: "Computers & Laptops", status: "active" }, { id: 2, name: "Jane Smith", email: "jane.tech@madonoro.com", phone: "082 345 6789", specialization: "Mobile Devices", status: "active" }, { id: 3, name: "Mike Johnson", email: "mike.tech@madonoro.com", phone: "083 456 7890", specialization: "Networking & Printers", status: "inactive" }, ]; const mockRepairParts = [ { id: 1, repairId: "RT-2023-001", inventoryId: 1, quantity: 1, unitPrice: 450.00, total: 450.00 }, { id: 2, repairId: "RT-2023-001", inventoryId: 6, quantity: 1, unitPrice: 45.00, total: 45.00 }, { id: 3, repairId: "RT-2023-002", inventoryId: 3, quantity: 1, unitPrice: 350.00, total: 350.00 }, { id: 4, repairId: "RT-2023-005", inventoryId: 7, quantity: 1, unitPrice: 50.00, total: 50.00 }, ]; const mockStats = { activeCompanies: mockCompanies.filter(c => c.status === "active").length, pendingRepairs: mockRepairTickets.filter(t => t.status === "pending" || t.status === "in-progress").length, unpaidInvoices: mockInvoices.filter(i => i.status === "unpaid" || i.status === "overdue").length, quotesThisMonth: mockQuotations.filter(q => { const date = new Date(q.date); const now = new Date(); return date.getMonth() === now.getMonth() && date.getFullYear() === now.getFullYear(); }).length, revenueData: [ { month: "Jan", amount: 12500 }, { month: "Feb", amount: 15000 }, { month: "Mar", amount: 18000 }, { month: "Apr", amount: 16500 }, { month: "May", amount: 21000 }, { month: "Jun", amount: 19500 }, { month: "Jul", amount: 22500 }, ], repairsByTechnician: [ { name: "John Doe", completed: 24, inProgress: 5 }, { name: "Jane Smith", completed: 18, inProgress: 3 }, { name: "Mike Johnson", completed: 15, inProgress: 0 }, ], recentTickets: mockRepairTickets.slice(0, 4).map(ticket => { const customer = mockCustomers.find(c => c.id === ticket.customerId); const technician = ticket.technicianId ? mockTechnicians.find(t => t.id === ticket.technicianId)?.name : "Unassigned"; return { id: ticket.id, customer: customer?.name || "Unknown", device: `${ticket.brand} ${ticket.model}`, status: ticket.status === "in-progress" ? "In Progress" : ticket.status.charAt(0).toUpperCase() + ticket.status.slice(1), technician }; }), alerts: [ { type: "inventory", message: "Laptop Power Adapter - Universal stock below threshold (2 remaining)" }, { type: "delete", message: "Company deletion request: XYZ Corporation" }, { type: "invoice", message: "Invoice #INV-2023-004 is overdue by 10 days" }, ] }; // Main App Component export default function App() { const [sidebarOpen, setSidebarOpen] = useState(true); const [currentUser, setCurrentUser] = useState(null); const [currentModule, setCurrentModule] = useState("dashboard"); const [currentSubModule, setCurrentSubModule] = useState(null); const [notifications, setNotifications] = useState([]); const [showNotifications, setShowNotifications] = useState(false); const [isMobile, setIsMobile] = useState(false); // State for company management const [companies, setCompanies] = useState(mockCompanies); const [documents, setDocuments] = useState(mockDocuments); const [credentials, setCredentials] = useState(mockCredentials); const [selectedCompany, setSelectedCompany] = useState(null); // State for quotation & invoice const [clients, setClients] = useState(mockClients); const [quotations, setQuotations] = useState(mockQuotations); const [invoices, setInvoices] = useState(mockInvoices); const [selectedQuotation, setSelectedQuotation] = useState(null); const [selectedInvoice, setSelectedInvoice] = useState(null); // State for repair management const [customers, setCustomers] = useState(mockCustomers); const [repairTickets, setRepairTickets] = useState(mockRepairTickets); const [inventory, setInventory] = useState(mockInventory); const [technicians, setTechnicians] = useState(mockTechnicians); const [repairParts, setRepairParts] = useState(mockRepairParts); const [selectedTicket, setSelectedTicket] = useState(null); // Check if user is logged in useEffect(() => { // In a real app, this would check JWT token validity const user = { id: 1, name: "Admin User", email: "admin@madonoro.com", role: "admin", avatar: "https://ui-avatars.com/api/?name=Admin+User&background=2563eb&color=fff" }; setCurrentUser(user); // Mock notifications setNotifications([ { id: 1, message: "New repair ticket created", time: "10 minutes ago", read: false }, { id: 2, message: "Invoice #INV-2023-004 is overdue", time: "2 hours ago", read: false }, { id: 3, message: "Company update request approved", time: "Yesterday", read: true }, ]); // Check if mobile const handleResize = () => { setIsMobile(window.innerWidth < 768); if (window.innerWidth < 768) { setSidebarOpen(false); } else { setSidebarOpen(true); } }; handleResize(); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); // Handle logout const handleLogout = () => { // In a real app, this would clear JWT token setCurrentUser(null); }; // Handle module change const handleModuleChange = (module, subModule = null) => { setCurrentModule(module); setCurrentSubModule(subModule); // Reset selections when changing modules setSelectedCompany(null); setSelectedQuotation(null); setSelectedInvoice(null); setSelectedTicket(null); if (isMobile) { setSidebarOpen(false); } }; // If not logged in, show login screen if (!currentUser) { return setCurrentUser(user)} />; } return (
{/* Sidebar */} {/* Main Content */}
{/* Top Navigation */}
{isMobile && ( )}
{showNotifications && (

Notifications

{notifications.length > 0 ? ( notifications.map(notification => (

{notification.message}

{notification.time}

)) ) : (
No notifications
)}
)}
{currentUser.name} {currentUser.name}
{/* Page Content */}
{currentModule === "dashboard" && ( )} {currentModule === "company" && ( <> {currentSubModule === "list" && ( )} {currentSubModule === "documents" && ( )} {currentSubModule === "credentials" && ( )} )} {currentModule === "invoice" && ( <> {currentSubModule === "quotations" && ( )} {currentSubModule === "invoices" && ( )} {currentSubModule === "clients" && ( )} )} {currentModule === "repair" && ( <> {currentSubModule === "tickets" && ( )} {currentSubModule === "customers" && ( )} {currentSubModule === "inventory" && ( )} {currentSubModule === "technicians" && ( )} )} {currentModule === "reports" && ( <> {currentSubModule === "financial" && ( )} {currentSubModule === "repair" && ( )} {currentSubModule === "inventory" && ( )} )} {currentModule === "settings" && ( )}
); } // Login Screen Component function LoginScreen({ onLogin }) { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(""); const handleSubmit = (e) => { e.preventDefault(); // Basic validation if (!email || !password) { setError("Please enter both email and password"); return; } // In a real app, this would make an API call to verify credentials if (email === "admin@madonoro.com" && password === "password") { onLogin({ id: 1, name: "Admin User", email: "admin@madonoro.com", role: "admin", avatar: "https://ui-avatars.com/api/?name=Admin+User&background=2563eb&color=fff" }); } else if (email === "staff@madonoro.com" && password === "password") { onLogin({ id: 2, name: "Staff User", email: "staff@madonoro.com", role: "staff", avatar: "https://ui-avatars.com/api/?name=Staff+User&background=10b981&color=fff" }); } else if (email === "tech@madonoro.com" && password === "password") { onLogin({ id: 3, name: "Tech User", email: "tech@madonoro.com", role: "technician", avatar: "https://ui-avatars.com/api/?name=Tech+User&background=f59e0b&color=fff" }); } else if (email === "accounting@madonoro.com" && password === "password") { onLogin({ id: 4, name: "Accounting User", email: "accounting@madonoro.com", role: "accounting", avatar: "https://ui-avatars.com/api/?name=Accounting+User&background=ef4444&color=fff" }); } else { setError("Invalid email or password"); } }; return (
M

Madonoro Express

Unified Business Management System

{error && (

{error}

)}
setEmail(e.target.value)} className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-[#2563eb] focus:z-10 sm:text-sm" placeholder="Email address" />
setPassword(e.target.value)} className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-[#2563eb] focus:z-10 sm:text-sm" placeholder="Password" />

Demo credentials:

Admin: admin@madonoro.com / password

Staff: staff@madonoro.com / password

Technician: tech@madonoro.com / password

Accounting: accounting@madonoro.com / password

); } // Navigation Item Component function NavItem({ icon, label, active, onClick, hasSubmenu = false, expanded = false, subItems = [] }) { return (
  • {hasSubmenu && expanded && ( )}
  • ); } // Dashboard Module Component function DashboardModule({ stats }) { return (

    Dashboard

    {/* Stats Cards */}
    } change="+2 this month" positive={true} /> } change="3 urgent" positive={false} /> } change="5 overdue" positive={false} /> } change="+15% vs last month" positive={true} />
    {/* Charts and Tables */}
    {/* Revenue Chart */}

    Monthly Revenue

    {/* Technician Performance */}

    Technician Performance

    {stats.repairsByTechnician.map((tech, index) => (
    {tech.name} {tech.completed + tech.inProgress} total tickets
    {tech.completed} completed {tech.inProgress} in progress
    ))}
    {/* Recent Tickets and Alerts */}
    {/* Recent Repair Tickets */}

    Recent Repair Tickets

    {stats.recentTickets.map((ticket, index) => ( ))}
    Ticket ID Customer Device Status Technician
    {ticket.id} {ticket.customer} {ticket.device} {ticket.status} {ticket.technician}
    View all tickets →
    {/* System Alerts */}

    System Alerts

    {stats.alerts.map((alert, index) => (

    {alert.message}

    ))}
    View all alerts →
    ); } // Stat Card Component function StatCard({ title, value, icon, change, positive }) { return (

    {title}

    {icon}

    {value}

    {change}

    ); } // Revenue Chart Component function RevenueChart({ data }) { return (
    {data.map((item, index) => (
    d.amount))) * 100}%` }} >
    {item.month}
    ))}
    ); } // Company Management Module Components function CompanyListModule({ companies, setCompanies, selectedCompany, setSelectedCompany }) { const [showAddModal, setShowAddModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); const [companyToDelete, setCompanyToDelete] = useState(null); const [formData, setFormData] = useState({ name: "", regNumber: "", taxNumber: "", contactPerson: "", email: "", phone: "", address: "" }); const [searchTerm, setSearchTerm] = useState(""); const [filterStatus, setFilterStatus] = useState("all"); // Filter companies based on search and status const filteredCompanies = companies.filter(company => { const matchesSearch = company.name.toLowerCase().includes(searchTerm.toLowerCase()) || company.regNumber.includes(searchTerm) || company.contactPerson.toLowerCase().includes(searchTerm.toLowerCase()); const matchesStatus = filterStatus === "all" || company.status === filterStatus; return matchesSearch && matchesStatus; }); const handleAddCompany = () => { const newCompany = { id: companies.length + 1, ...formData, status: "active", createdAt: new Date().toISOString().split('T')[0] }; setCompanies([...companies, newCompany]); setFormData({ name: "", regNumber: "", taxNumber: "", contactPerson: "", email: "", phone: "", address: "" }); setShowAddModal(false); }; const handleUpdateCompany = () => { const updatedCompanies = companies.map(company => company.id === selectedCompany.id ? { ...company, ...formData } : company ); setCompanies(updatedCompanies); setSelectedCompany(null); setFormData({ name: "", regNumber: "", taxNumber: "", contactPerson: "", email: "", phone: "", address: "" }); }; const handleDeleteRequest = (company) => { setCompanyToDelete(company); setShowDeleteModal(true); }; const confirmDelete = () => { const updatedCompanies = companies.map(company => company.id === companyToDelete.id ? { ...company, status: "pending deletion" } : company ); setCompanies(updatedCompanies); setShowDeleteModal(false); setCompanyToDelete(null); }; const handleEditClick = (company) => { setSelectedCompany(company); setFormData({ name: company.name, regNumber: company.regNumber, taxNumber: company.taxNumber, contactPerson: company.contactPerson, email: company.email, phone: company.phone, address: company.address }); }; return (

    Company Management

    {/* Search and Filter */}
    setSearchTerm(e.target.value)} className="pl-10 pr-4 py-2 w-full border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    {/* Companies Table */}
    {filteredCompanies.map((company) => ( ))} {filteredCompanies.length === 0 && ( )}
    Company Name Registration No. Contact Person Status Created Actions
    {company.name}
    {company.email}
    {company.regNumber} {company.contactPerson} {company.status.charAt(0).toUpperCase() + company.status.slice(1)} {company.createdAt}
    No companies found
    {/* Company Details/Edit Form */} {selectedCompany && (

    Edit Company

    setFormData({...formData, name: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    setFormData({...formData, regNumber: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    setFormData({...formData, taxNumber: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    setFormData({...formData, contactPerson: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    setFormData({...formData, email: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    setFormData({...formData, phone: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    )} {/* Add Company Modal */} {showAddModal && (

    Add New Company

    setFormData({...formData, name: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    setFormData({...formData, regNumber: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    setFormData({...formData, taxNumber: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    setFormData({...formData, contactPerson: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    setFormData({...formData, email: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    setFormData({...formData, phone: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    )} {/* Delete Confirmation Modal */} {showDeleteModal && (

    Request Company Deletion

    Are you sure you want to request deletion of {companyToDelete?.name}? This action requires admin approval.

    )}
    ); } function DocumentsModule({ documents, setDocuments, companies }) { const [showUploadModal, setShowUploadModal] = useState(false); const [selectedCompanyId, setSelectedCompanyId] = useState(""); const [documentName, setDocumentName] = useState(""); const [documentType, setDocumentType] = useState("pdf"); const [searchTerm, setSearchTerm] = useState(""); const [filterCompany, setFilterCompany] = useState("all"); const [filterStatus, setFilterStatus] = useState("all"); // Filter documents based on search, company, and status const filteredDocuments = documents.filter(doc => { const matchesSearch = doc.name.toLowerCase().includes(searchTerm.toLowerCase()); const matchesCompany = filterCompany === "all" || doc.companyId === parseInt(filterCompany); const matchesStatus = filterStatus === "all" || doc.status === filterStatus; return matchesSearch && matchesCompany && matchesStatus; }); const handleUploadDocument = () => { if (!selectedCompanyId || !documentName) return; const newDocument = { id: documents.length + 1, companyId: parseInt(selectedCompanyId), name: documentName, type: documentType, size: `${(Math.random() * 2 + 0.1).toFixed(1)} MB`, uploadDate: new Date().toISOString().split('T')[0], status: "pending" }; setDocuments([...documents, newDocument]); setShowUploadModal(false); setSelectedCompanyId(""); setDocumentName(""); setDocumentType("pdf"); }; const handleVerifyDocument = (docId) => { const updatedDocuments = documents.map(doc => doc.id === docId ? { ...doc, status: "verified" } : doc ); setDocuments(updatedDocuments); }; const handleDeleteDocument = (docId) => { const updatedDocuments = documents.filter(doc => doc.id !== docId); setDocuments(updatedDocuments); }; return (

    Company Documents

    {/* Search and Filter */}
    setSearchTerm(e.target.value)} className="pl-10 pr-4 py-2 w-full border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    {/* Documents Table */}
    {filteredDocuments.map((doc) => { const company = companies.find(c => c.id === doc.companyId); return ( ); })} {filteredDocuments.length === 0 && ( )}
    Document Name Company Type Size Upload Date Status Actions
    {doc.name} {company?.name || "Unknown"} {doc.type} {doc.size} {doc.uploadDate} {doc.status.charAt(0).toUpperCase() + doc.status.slice(1)}
    {doc.status === 'pending' && ( )}
    No documents found
    {/* Upload Document Modal */} {showUploadModal && (

    Upload Document

    setDocumentName(e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" placeholder="e.g. Company Registration Certificate" />

    or drag and drop

    PDF, DOC, JPG, PNG up to 10MB

    )}
    ); } function CredentialsModule({ credentials, setCredentials, companies }) { const [showAddModal, setShowAddModal] = useState(false); const [showViewModal, setShowViewModal] = useState(false); const [selectedCredential, setSelectedCredential] = useState(null); const [formData, setFormData] = useState({ companyId: "", type: "CIPC", username: "", password: "" }); const [filterCompany, setFilterCompany] = useState("all"); const [filterType, setFilterType] = useState("all"); // Filter credentials based on company and type const filteredCredentials = credentials.filter(cred => { const matchesCompany = filterCompany === "all" || cred.companyId === parseInt(filterCompany); const matchesType = filterType === "all" || cred.type === filterType; return matchesCompany && matchesType; }); const handleAddCredential = () => { if (!formData.companyId || !formData.username || !formData.password) return; const newCredential = { id: credentials.length + 1, companyId: parseInt(formData.companyId), type: formData.type, username: formData.username, password: formData.password, lastUpdated: new Date().toISOString().split('T')[0] }; setCredentials([...credentials, newCredential]); setShowAddModal(false); setFormData({ companyId: "", type: "CIPC", username: "", password: "" }); }; const handleViewCredential = (credential) => { setSelectedCredential(credential); setShowViewModal(true); }; const handleDeleteCredential = (credId) => { const updatedCredentials = credentials.filter(cred => cred.id !== credId); setCredentials(updatedCredentials); }; return (

    Secure Credentials

    {/* Filter */}
    {/* Credentials Table */}
    {filteredCredentials.map((cred) => { const company = companies.find(c => c.id === cred.companyId); return ( ); })} {filteredCredentials.length === 0 && ( )}
    Company Type Username Password Last Updated Actions
    {company?.name || "Unknown"} {cred.type} {cred.username}
    ••••••••
    {cred.lastUpdated}
    No credentials found
    {/* Add Credential Modal */} {showAddModal && (

    Add New Credential

    setFormData({...formData, username: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    setFormData({...formData, password: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    )} {/* View Credential Modal */} {showViewModal && selectedCredential && (

    Credential Details

    Company

    {companies.find(c => c.id === selectedCredential.companyId)?.name || "Unknown"}

    Type

    {selectedCredential.type}

    Username

    {selectedCredential.username}

    Password

    {selectedCredential.password}

    Last Updated

    {selectedCredential.lastUpdated}

    )}
    ); } // Quotation & Invoice Module Components function QuotationsModule({ quotations, setQuotations, companies, clients, selectedQuotation, setSelectedQuotation, invoices, setInvoices }) { const [showCreateModal, setShowCreateModal] = useState(false); const [showViewModal, setShowViewModal] = useState(false); const [searchTerm, setSearchTerm] = useState(""); const [filterCompany, setFilterCompany] = useState("all"); const [filterStatus, setFilterStatus] = useState("all"); const [formData, setFormData] = useState({ companyId: "", clientId: "", items: [{ id: 1, description: "", quantity: 1, unitPrice: 0 }] }); // Calculate total for a quotation const calculateTotal = (items) => { return items.reduce((total, item) => total + (item.quantity * item.unitPrice), 0); }; // Filter quotations based on search, company, and status const filteredQuotations = quotations.filter(quote => { const matchesSearch = quote.id.toLowerCase().includes(searchTerm.toLowerCase()) || clients.find(c => c.id === quote.clientId)?.name.toLowerCase().includes(searchTerm.toLowerCase()); const matchesCompany = filterCompany === "all" || quote.companyId === parseInt(filterCompany); const matchesStatus = filterStatus === "all" || quote.status === filterStatus; return matchesSearch && matchesCompany && matchesStatus; }); const handleCreateQuotation = () => { if (!formData.companyId || !formData.clientId || formData.items.some(item => !item.description || item.quantity <= 0)) { return; // Validation failed } const newQuotation = { id: `QT-${new Date().getFullYear()}-${String(quotations.length + 1).padStart(3, '0')}`, companyId: parseInt(formData.companyId), clientId: parseInt(formData.clientId), date: new Date().toISOString().split('T')[0], expiryDate: new Date(new Date().setDate(new Date().getDate() + 30)).toISOString().split('T')[0], status: "pending", total: calculateTotal(formData.items), items: formData.items }; setQuotations([...quotations, newQuotation]); setShowCreateModal(false); setFormData({ companyId: "", clientId: "", items: [{ id: 1, description: "", quantity: 1, unitPrice: 0 }] }); }; const handleAddItem = () => { setFormData({ ...formData, items: [...formData.items, { id: formData.items.length + 1, description: "", quantity: 1, unitPrice: 0 }] }); }; const handleRemoveItem = (index) => { const updatedItems = [...formData.items]; updatedItems.splice(index, 1); setFormData({ ...formData, items: updatedItems }); }; const handleItemChange = (index, field, value) => { const updatedItems = [...formData.items]; updatedItems[index][field] = field === 'quantity' || field === 'unitPrice' ? parseFloat(value) : value; setFormData({ ...formData, items: updatedItems }); }; const handleViewQuotation = (quotation) => { setSelectedQuotation(quotation); setShowViewModal(true); }; const handleUpdateStatus = (quotationId, newStatus) => { const updatedQuotations = quotations.map(quote => quote.id === quotationId ? { ...quote, status: newStatus } : quote ); setQuotations(updatedQuotations); setShowViewModal(false); setSelectedQuotation(null); }; const handleConvertToInvoice = (quotation) => { // Check if invoice already exists for this quotation const existingInvoice = invoices.find(inv => inv.quotationId === quotation.id); if (existingInvoice) { // In a real app, you would show a notification here return; } const newInvoice = { id: `INV-${new Date().getFullYear()}-${String(invoices.length + 1).padStart(3, '0')}`, quotationId: quotation.id, companyId: quotation.companyId, clientId: quotation.clientId, date: new Date().toISOString().split('T')[0], dueDate: new Date(new Date().setDate(new Date().getDate() + 30)).toISOString().split('T')[0], status: "unpaid", paymentDate: null, total: quotation.total }; setInvoices([...invoices, newInvoice]); // Update quotation status to "accepted" const updatedQuotations = quotations.map(quote => quote.id === quotation.id ? { ...quote, status: "accepted" } : quote ); setQuotations(updatedQuotations); setShowViewModal(false); setSelectedQuotation(null); }; return (

    Quotations

    {/* Search and Filter */}
    setSearchTerm(e.target.value)} className="pl-10 pr-4 py-2 w-full border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#2563eb] focus:border-transparent" />
    {/* Quotations Table */}
    {filteredQuotations.map((quote) => { const company = companies.find(c => c.id === quote.companyId); const client = clients.find(c => c.id === quote.clientId); return ( ); })} {filteredQuotations.length === 0 && ( )} import { useState, useEffect } from "react"; import { Bell, Menu, X, User, LogOut, Home, Briefcase, FileText, Wrench, BarChart3, Settings, ChevronDown, Search, Plus, AlertTriangle } from "lucide-react"; // Mock data for demonstration const mockStats = { activeCompanies: 12, pendingRepairs: 8, unpaidInvoices: 15, quotesThisMonth: 23, revenueData: [ { month: "Jan", amount: 12500 }, { month: "Feb", amount: 15000 }, { month: "Mar", amount: 18000 }, { month: "Apr", amount: 16500 }, { month: "May", amount: 21000 }, { month: "Jun", amount: 19500 }, ], repairsByTechnician: [ { name: "John Doe", completed: 24, inProgress: 5 }, { name: "Jane Smith", completed: 18, inProgress: 3 }, { name: "Mike Johnson", completed: 15, inProgress: 0 }, ], recentTickets: [ { id: "RT-2023-001", customer: "ABC Corp", device: "Dell Laptop", status: "In Progress", technician: "John Doe" }, { id: "RT-2023-002", customer: "XYZ Ltd", device: "iPhone 13", status: "Pending", technician: "Unassigned" }, { id: "RT-2023-003", customer: "123 Industries", device: "HP Printer", status: "Completed", technician: "Jane Smith" }, { id: "RT-2023-004", customer: "Smith & Co", device: "Custom PC", status: "In Progress", technician: "Mike Johnson" }, ], alerts: [ { type: "inventory", message: "Laptop RAM stock below threshold (2 remaining)" }, { type: "delete", message: "Company deletion request: TechSolutions Inc" }, { type: "invoice", message: "5 invoices overdue by more than 30 days" }, ] }; // Main App Component export default function App() { const [sidebarOpen, setSidebarOpen] = useState(true); const [currentUser, setCurrentUser] = useState(null); const [currentModule, setCurrentModule] = useState("dashboard"); const [notifications, setNotifications] = useState([]); const [showNotifications, setShowNotifications] = useState(false); const [isMobile, setIsMobile] = useState(false); // Check if user is logged in useEffect(() => { // In a real app, this would check JWT token validity const user = { id: 1, name: "Admin User", email: "admin@madonoro.com", role: "admin", avatar: "https://ui-avatars.com/api/?name=Admin+User&background=2563eb&color=fff" }; setCurrentUser(user); // Mock notifications setNotifications([ { id: 1, message: "New repair ticket created", time: "10 minutes ago", read: false }, { id: 2, message: "Invoice #INV-2023-042 is overdue", time: "2 hours ago", read: false }, { id: 3, message: "Company update request approved", time: "Yesterday", read: true }, ]); // Check if mobile const handleResize = () => { setIsMobile(window.innerWidth < 768); if (window.innerWidth < 768) { setSidebarOpen(false); } else { setSidebarOpen(true); } }; handleResize(); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); // Handle logout const handleLogout = () => { // In a real app, this would clear JWT token setCurrentUser(null); }; // If not logged in, show login screen if (!currentUser) { return setCurrentUser(user)} />; } return (
    {/* Sidebar */} {/* Main Content */}
    {/* Top Navigation */}
    {isMobile && ( )}
    {showNotifications && (

    Notifications

    {notifications.length > 0 ? ( notifications.map(notification => (

    {notification.message}

    {notification.time}

    )) ) : (
    No notifications
    )}
    )}
    {currentUser.name} {currentUser.name}
    {/* Page Content */}
    {currentModule === "dashboard" && } {currentModule === "company" && } {currentModule === "invoice" && } {currentModule === "repair" && } {currentModule === "reports" && } {currentModule === "settings" && }
    ); } // Login Screen Component function LoginScreen({ onLogin }) { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(""); const handleSubmit = (e) => { e.preventDefault(); // Basic validation if (!email || !password) { setError("Please enter both email and password"); return; } // In a real app, this would make an API call to verify credentials if (email === "admin@madonoro.com" && password === "password") { onLogin({ id: 1, name: "Admin User", email: "admin@madonoro.com", role: "admin", avatar: "https://ui-avatars.com/api/?name=Admin+User&background=2563eb&color=fff" }); } else { setError("Invalid email or password"); } }; return (
    M

    Madonoro Express

    Unified Business Management System

    {error && (

    {error}

    )}
    setEmail(e.target.value)} className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-[#2563eb] focus:border-[#2563eb] focus:z-10 sm:text-sm" placeholder="Email address" />
    setPassword(e.target.value)} className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-[#2563eb] focus:border-[#2563eb] focus:z-10 sm:text-sm" placeholder="Password" />

    Demo credentials: admin@madonoro.com / password

    ); } // Navigation Item Component function NavItem({ icon, label, active, onClick, hasSubmenu = false }) { return (
  • ); } // Dashboard Module Component function DashboardModule({ stats }) { return (

    Dashboard

    {/* Stats Cards */}
    } change="+2 this month" positive={true} /> } change="3 urgent" positive={false} /> } change="5 overdue" positive={false} /> } change="+15% vs last month" positive={true} />
    {/* Charts and Tables */}
    {/* Revenue Chart */}

    Monthly Revenue

    {/* Technician Performance */}

    Technician Performance

    {stats.repairsByTechnician.map((tech, index) => (
    {tech.name} {tech.completed + tech.inProgress} total tickets
    {tech.completed} completed {tech.inProgress} in progress
    ))}
    {/* Recent Tickets and Alerts */}
    {/* Recent Repair Tickets */}

    Recent Repair Tickets

    Quotation ID Company Client Date Expiry Total Status Actions
    {quote.id} {company?.name || "Unknown"} {client?.name || "Unknown"} {quote.date} {quote.expiryDate} R {quote.total.toFixed(2)} {quote.status.charAt(0).toUpperCase() + quote.status.slice(1)}
    {quote.status === 'pending' && ( )}
    No quotations found
    {stats.recentTickets.map((ticket, index) => ( ))}
    Ticket ID Customer Device Status Technician
    {ticket.id} {ticket.customer} {ticket.device} {ticket.status} {ticket.technician}
    View all tickets →
    {/* System Alerts */}

    System Alerts

    {stats.alerts.map((alert, index) => (

    {alert.message}

    ))}
    View all alerts →
    ); } // Stat Card Component function StatCard({ title, value, icon, change, positive }) { return (

    {title}

    {icon}

    {value}

    {change}

    ); } // Revenue Chart Component function RevenueChart({ data }) { return (
    {data.map((item, index) => (
    d.amount))) * 100}%` }} >
    {item.month}
    ))}
    ); } // Placeholder Module Components function CompanyModule() { return (

    Company Management

    This module allows you to manage company information, store sensitive credentials, and handle document uploads.

    Company management features will be implemented here.

    ); } function InvoiceModule() { return (

    Quotation & Invoice Management

    Create and manage quotations and invoices, with options to convert quotes to invoices and track payment status.

    Quotation and invoice features will be implemented here.

    ); } function RepairModule() { return (

    Tech Repair Management

    Manage repair tickets, track customer devices, assign technicians, and handle inventory for repairs.

    Repair management features will be implemented here.

    ); } function ReportsModule() { return (

    Reports & Analytics

    Generate and view reports on business performance, revenue, repair statistics, and more.

    Reporting features will be implemented here.

    ); } function SettingsModule() { return (

    System Settings

    Configure system settings, manage user accounts, and customize notification preferences.

    Settings features will be implemented here.

    ); }