import { useState, useEffect, useRef } from "react"; const COLORS = { bg: "#0D0F14", surface: "#13161D", card: "#191C25", cardHover: "#1E2230", border: "#252A38", accent: "#F5A623", accentDim: "#C4841B", accentGlow: "rgba(245,166,35,0.15)", green: "#22C55E", greenDim: "rgba(34,197,94,0.12)", red: "#EF4444", redDim: "rgba(239,68,68,0.12)", blue: "#3B82F6", blueDim: "rgba(59,130,246,0.12)", purple: "#A855F7", purpleDim: "rgba(168,85,247,0.12)", text: "#F1F3F9", textSub: "#8B92A5", textMuted: "#525869", }; const styles = ` @import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700;800&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;1,9..40,300&display=swap'); * { margin:0; padding:0; box-sizing:border-box; } :root { --bg: ${COLORS.bg}; --surface: ${COLORS.surface}; --card: ${COLORS.card}; --border: ${COLORS.border}; --accent: ${COLORS.accent}; --text: ${COLORS.text}; --text-sub: ${COLORS.textSub}; --text-muted: ${COLORS.textMuted}; --green: ${COLORS.green}; --red: ${COLORS.red}; --blue: ${COLORS.blue}; --radius: 12px; } body { background: var(--bg); color: var(--text); font-family: 'DM Sans', sans-serif; overflow-x: hidden; } ::-webkit-scrollbar { width: 4px; height: 4px; } ::-webkit-scrollbar-track { background: var(--surface); } ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; } .app { display:flex; min-height:100vh; } /* SIDEBAR */ .sidebar { width: 240px; min-height: 100vh; background: var(--surface); border-right: 1px solid var(--border); display: flex; flex-direction: column; position: fixed; left:0; top:0; bottom:0; z-index:100; transition: transform 0.3s ease; } .sidebar-logo { padding: 24px 20px 20px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 10px; } .logo-icon { width: 34px; height: 34px; background: var(--accent); border-radius: 8px; display: flex; align-items: center; justify-content: center; font-family: 'Syne', sans-serif; font-weight: 800; font-size: 16px; color: #000; } .logo-text { font-family: 'Syne', sans-serif; font-weight: 700; font-size: 17px; color: var(--text); } .logo-sub { font-size: 10px; color: var(--text-muted); letter-spacing: 1.5px; text-transform: uppercase; margin-top: 1px; } .sidebar-nav { flex:1; padding: 16px 12px; overflow-y: auto; } .nav-section-label { font-size: 10px; font-weight: 600; color: var(--text-muted); letter-spacing: 1.2px; text-transform: uppercase; padding: 8px 8px 6px; margin-top: 8px; } .nav-item { display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 8px; cursor: pointer; font-size: 13.5px; font-weight: 400; color: var(--text-sub); transition: all 0.15s ease; margin-bottom: 2px; position: relative; border: 1px solid transparent; } .nav-item:hover { background: rgba(255,255,255,0.04); color: var(--text); } .nav-item.active { background: ${COLORS.accentGlow}; color: var(--accent); border-color: rgba(245,166,35,0.2); font-weight: 500; } .nav-item.active .nav-icon { color: var(--accent); } .nav-badge { margin-left: auto; background: var(--accent); color: #000; font-size: 10px; font-weight: 700; padding: 1px 6px; border-radius: 20px; } .nav-icon { width: 16px; text-align: center; flex-shrink: 0; } .sidebar-footer { padding: 16px 12px; border-top: 1px solid var(--border); } .user-card { display: flex; align-items: center; gap: 10px; padding: 10px; border-radius: 8px; cursor: pointer; transition: background 0.15s; } .user-card:hover { background: rgba(255,255,255,0.04); } .user-avatar { width: 32px; height: 32px; border-radius: 50%; background: linear-gradient(135deg, ${COLORS.accent} 0%, ${COLORS.accentDim} 100%); display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700; color: #000; flex-shrink: 0; } .user-name { font-size: 13px; font-weight: 500; color: var(--text); } .user-role { font-size: 11px; color: var(--text-muted); } /* MAIN */ .main { margin-left: 240px; flex:1; min-height: 100vh; } .topbar { height: 60px; background: var(--surface); border-bottom: 1px solid var(--border); display: flex; align-items: center; padding: 0 28px; gap: 16px; position: sticky; top:0; z-index:50; } .topbar-title { font-family: 'Syne', sans-serif; font-weight: 700; font-size: 17px; color: var(--text); flex:1; } .topbar-sub { font-size: 12px; color: var(--text-muted); margin-top: 2px; font-family: 'DM Sans'; font-weight: 300; } .btn { display: inline-flex; align-items: center; gap: 6px; padding: 8px 16px; border-radius: 8px; font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.15s ease; border: none; font-family: 'DM Sans', sans-serif; } .btn-primary { background: var(--accent); color: #000; } .btn-primary:hover { background: #FFB930; transform: translateY(-1px); } .btn-ghost { background: rgba(255,255,255,0.05); color: var(--text-sub); border: 1px solid var(--border); } .btn-ghost:hover { background: rgba(255,255,255,0.08); color: var(--text); } .btn-sm { padding: 6px 12px; font-size: 12px; } .btn-icon { padding: 8px; } .content { padding: 28px; } /* STAT CARDS */ .stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px; } .stat-card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; position: relative; overflow: hidden; cursor: pointer; transition: all 0.2s ease; } .stat-card:hover { border-color: rgba(245,166,35,0.3); transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,0.3); } .stat-card::before { content:''; position:absolute; top:0; right:0; width:80px; height:80px; border-radius: 50%; opacity: 0.06; transform: translate(20px,-20px); } .stat-card.yellow::before { background: var(--accent); } .stat-card.green::before { background: var(--green); } .stat-card.blue::before { background: var(--blue); } .stat-card.red::before { background: var(--red); } .stat-label { font-size: 11px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 1px; font-weight: 600; margin-bottom: 10px; } .stat-value { font-family: 'Syne', sans-serif; font-size: 28px; font-weight: 700; color: var(--text); line-height: 1; margin-bottom: 8px; } .stat-delta { font-size: 12px; display: flex; align-items: center; gap: 4px; } .delta-up { color: var(--green); } .delta-down { color: var(--red); } .stat-icon { position: absolute; top: 18px; right: 18px; font-size: 20px; opacity: 0.5; } /* GRID LAYOUTS */ .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 24px; } .grid-3 { display: grid; grid-template-columns: 2fr 1fr; gap: 20px; margin-bottom: 24px; } /* CARDS */ .card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; } .card-header { padding: 18px 20px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; } .card-title { font-family: 'Syne', sans-serif; font-weight: 600; font-size: 15px; color: var(--text); } .card-sub { font-size: 12px; color: var(--text-muted); margin-top: 2px; } .card-body { padding: 20px; } /* PROJECT LIST */ .project-item { display: flex; align-items: center; gap: 14px; padding: 14px 20px; border-bottom: 1px solid var(--border); cursor: pointer; transition: background 0.15s; } .project-item:last-child { border-bottom: none; } .project-item:hover { background: rgba(255,255,255,0.02); } .project-color { width: 4px; height: 42px; border-radius: 2px; flex-shrink: 0; } .project-name { font-size: 13.5px; font-weight: 500; color: var(--text); margin-bottom: 3px; } .project-meta { font-size: 11.5px; color: var(--text-muted); } .project-right { margin-left: auto; text-align: right; } .project-budget { font-size: 13px; font-weight: 600; color: var(--text); font-family: 'Syne', sans-serif; } .project-date { font-size: 11px; color: var(--text-muted); margin-top: 3px; } /* STATUS BADGE */ .badge { display: inline-flex; align-items: center; gap: 4px; padding: 3px 9px; border-radius: 20px; font-size: 11px; font-weight: 500; } .badge-green { background: ${COLORS.greenDim}; color: var(--green); } .badge-yellow { background: ${COLORS.accentGlow}; color: var(--accent); } .badge-red { background: ${COLORS.redDim}; color: var(--red); } .badge-blue { background: ${COLORS.blueDim}; color: var(--blue); } .badge-purple { background: ${COLORS.purpleDim}; color: ${COLORS.purple}; } .badge::before { content:''; width: 5px; height: 5px; border-radius: 50%; background: currentColor; } /* PROGRESS BAR */ .progress-wrap { background: rgba(255,255,255,0.06); border-radius: 99px; height: 5px; overflow: hidden; } .progress-fill { height: 100%; border-radius: 99px; transition: width 0.6s ease; } /* CASH FLOW CHART */ .chart-bars { display: flex; align-items: flex-end; gap: 8px; height: 120px; padding: 0 4px; } .chart-bar-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; } .chart-bar { width: 100%; border-radius: 4px 4px 0 0; transition: opacity 0.2s; cursor: pointer; } .chart-bar:hover { opacity: 0.8; } .chart-label { font-size: 10px; color: var(--text-muted); } /* ACTIVITY FEED */ .activity-item { display: flex; gap: 12px; padding: 12px 0; } .activity-item:not(:last-child) { border-bottom: 1px solid rgba(255,255,255,0.04); } .activity-dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 5px; flex-shrink: 0; } .activity-text { font-size: 13px; color: var(--text-sub); line-height: 1.5; } .activity-text strong { color: var(--text); font-weight: 500; } .activity-time { font-size: 11px; color: var(--text-muted); margin-top: 3px; } /* TASK LIST */ .task-item { display: flex; align-items: flex-start; gap: 10px; padding: 11px 0; border-bottom: 1px solid rgba(255,255,255,0.04); cursor: pointer; } .task-item:last-child { border-bottom: none; } .task-checkbox { width: 16px; height: 16px; border: 1.5px solid var(--border); border-radius: 4px; flex-shrink: 0; margin-top: 1px; transition: all 0.2s; display: flex; align-items: center; justify-content: center; } .task-checkbox.checked { background: var(--green); border-color: var(--green); } .task-name { font-size: 13px; color: var(--text-sub); flex: 1; line-height: 1.4; } .task-name.done { text-decoration: line-through; color: var(--text-muted); } .task-meta { font-size: 11px; color: var(--text-muted); margin-top: 2px; } .task-priority { flex-shrink: 0; font-size: 10px; padding: 2px 7px; border-radius: 4px; font-weight: 600; } .priority-high { background: ${COLORS.redDim}; color: var(--red); } .priority-med { background: ${COLORS.accentGlow}; color: var(--accent); } .priority-low { background: ${COLORS.blueDim}; color: var(--blue); } /* INVOICE TABLE */ .table { width: 100%; border-collapse: collapse; } .table th { text-align: left; font-size: 11px; color: var(--text-muted); font-weight: 600; letter-spacing: 0.8px; text-transform: uppercase; padding: 10px 16px; border-bottom: 1px solid var(--border); } .table td { padding: 13px 16px; font-size: 13px; border-bottom: 1px solid rgba(255,255,255,0.04); } .table tr:last-child td { border-bottom: none; } .table tr:hover td { background: rgba(255,255,255,0.02); } .table-amount { font-family: 'Syne', sans-serif; font-weight: 600; } /* TEAM */ .team-member { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid rgba(255,255,255,0.04); } .team-member:last-child { border-bottom: none; } .member-avatar { width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700; flex-shrink: 0; } .member-name { font-size: 13px; font-weight: 500; color: var(--text); } .member-role { font-size: 11.5px; color: var(--text-muted); } .member-status { margin-left: auto; } /* AI PANEL */ .ai-panel { background: linear-gradient(135deg, rgba(245,166,35,0.08) 0%, rgba(168,85,247,0.06) 100%); border: 1px solid rgba(245,166,35,0.2); border-radius: var(--radius); padding: 20px; margin-bottom: 24px; } .ai-header { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; } .ai-badge { background: var(--accent); color: #000; font-size: 10px; font-weight: 800; padding: 2px 8px; border-radius: 4px; letter-spacing: 1px; } .ai-title { font-family: 'Syne', sans-serif; font-weight: 600; font-size: 14px; color: var(--text); } .ai-alerts { display: flex; flex-direction: column; gap: 8px; } .ai-alert { background: rgba(0,0,0,0.3); border-radius: 8px; padding: 10px 14px; display: flex; align-items: flex-start; gap: 10px; font-size: 13px; color: var(--text-sub); } .ai-alert-icon { flex-shrink: 0; font-size: 15px; margin-top: 1px; } /* GANTT */ .gantt-row { display: flex; align-items: center; gap: 12px; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.04); } .gantt-label { width: 140px; font-size: 12.5px; color: var(--text-sub); flex-shrink: 0; } .gantt-track { flex: 1; background: rgba(255,255,255,0.05); border-radius: 3px; height: 20px; position: relative; overflow: hidden; } .gantt-bar { height: 100%; border-radius: 3px; display: flex; align-items: center; padding: 0 8px; font-size: 10px; font-weight: 600; color: rgba(255,255,255,0.8); position: absolute; } .gantt-pct { width: 36px; font-size: 11px; color: var(--text-muted); text-align: right; flex-shrink: 0; } /* MODAL OVERLAY */ .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); z-index: 200; display: flex; align-items: center; justify-content: center; padding: 20px; backdrop-filter: blur(4px); } .modal { background: var(--card); border: 1px solid var(--border); border-radius: 16px; width: 100%; max-width: 520px; max-height: 85vh; overflow-y: auto; animation: modalIn 0.2s ease; } @keyframes modalIn { from { opacity:0; transform:scale(0.96) translateY(8px); } to { opacity:1; transform:scale(1) translateY(0); } } .modal-header { padding: 22px 24px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; } .modal-title { font-family: 'Syne', sans-serif; font-weight: 700; font-size: 17px; } .modal-body { padding: 22px 24px; } .modal-footer { padding: 16px 24px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 10px; } /* FORM */ .form-group { margin-bottom: 16px; } .form-label { font-size: 12px; font-weight: 600; color: var(--text-sub); letter-spacing: 0.5px; margin-bottom: 6px; display: block; } .form-input { width: 100%; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 10px 13px; font-size: 13.5px; color: var(--text); font-family: 'DM Sans', sans-serif; transition: border-color 0.2s; outline: none; } .form-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px ${COLORS.accentGlow}; } .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; } select.form-input { cursor: pointer; } /* TABS */ .tabs { display: flex; gap: 2px; background: var(--surface); border-radius: 10px; padding: 3px; margin-bottom: 22px; } .tab { flex: 1; padding: 8px 14px; border-radius: 8px; font-size: 13px; font-weight: 500; cursor: pointer; text-align: center; color: var(--text-muted); transition: all 0.2s; } .tab.active { background: var(--card); color: var(--text); box-shadow: 0 1px 4px rgba(0,0,0,0.3); } /* NOTIFICATION DOT */ .notif-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); position: absolute; top: 6px; right: 6px; } /* SEARCH */ .search-box { display: flex; align-items: center; gap: 8px; background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 8px 12px; min-width: 200px; } .search-box input { background: none; border: none; outline: none; color: var(--text); font-size: 13px; font-family: 'DM Sans'; width: 100%; } .search-box input::placeholder { color: var(--text-muted); } /* ANIMATIONS */ @keyframes fadeIn { from { opacity:0; transform: translateY(10px); } to { opacity:1; transform: translateY(0); } } .page-enter { animation: fadeIn 0.25s ease; } /* METRIC RING */ .ring-wrap { display: flex; align-items: center; gap: 14px; } .ring-text { } .ring-val { font-family: 'Syne', sans-serif; font-size: 22px; font-weight: 700; color: var(--text); } .ring-label { font-size: 11px; color: var(--text-muted); margin-top: 2px; } /* DIVIDER */ .divider { height: 1px; background: var(--border); margin: 16px 0; } /* QUICK ACTIONS */ .quick-actions { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 24px; } .quick-action { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; text-align: center; cursor: pointer; transition: all 0.2s; } .quick-action:hover { border-color: rgba(245,166,35,0.3); background: var(--cardHover); transform: translateY(-2px); } .qa-icon { font-size: 24px; margin-bottom: 8px; } .qa-label { font-size: 12px; font-weight: 500; color: var(--text-sub); } /* EMPTY STATE */ .empty { text-align: center; padding: 40px; color: var(--text-muted); } .empty-icon { font-size: 36px; margin-bottom: 12px; } .empty-text { font-size: 14px; } `; // ─── DATA ─────────────────────────────────────────────────────────────────── const PROJECTS = [ { id:1, name:"Lekki Phase 2 Residential", client:"Zenith Properties", status:"active", progress:67, budget:12500000, spent:8350000, dueDate:"Aug 2026", color:"#F5A623", type:"Residential" }, { id:2, name:"Victoria Island Office Complex", client:"First Bank HQ", status:"active", progress:34, budget:45000000, spent:15300000, dueDate:"Mar 2027", color:"#3B82F6", type:"Commercial" }, { id:3, name:"Abuja Road Expansion (Km 12–18)", client:"FCT Infrastructure", status:"at-risk", progress:81, budget:78000000, spent:65400000, dueDate:"Jul 2026", color:"#EF4444", type:"Infrastructure" }, { id:4, name:"Ikeja GRA Renovation", client:"Private Client", status:"completed", progress:100, budget:3200000, spent:3050000, dueDate:"Apr 2026", color:"#22C55E", type:"Renovation" }, { id:5, name:"Port Harcourt Warehouse A", client:"Dangote Logistics", status:"active", progress:18, budget:22000000, spent:3960000, dueDate:"Dec 2026", color:"#A855F7", type:"Industrial" }, ]; const INVOICES = [ { id:"INV-0041", client:"Zenith Properties", project:"Lekki Phase 2", amount:2500000, status:"paid", date:"Apr 28" }, { id:"INV-0042", client:"First Bank HQ", project:"VI Office Complex", amount:8750000, status:"pending", date:"May 2" }, { id:"INV-0043", client:"FCT Infrastructure", project:"Abuja Road", amount:15000000, status:"overdue", date:"Apr 15" }, { id:"INV-0044", client:"Dangote Logistics", project:"PH Warehouse A", amount:4000000, status:"pending", date:"May 5" }, { id:"INV-0045", client:"Private Client", project:"Ikeja Renovation", amount:3200000, status:"paid", date:"Apr 30" }, ]; const TASKS = [ { id:1, name:"Submit structural drawings — VI Complex floors 3–5", project:"VI Office Complex", priority:"high", due:"Today", done:false }, { id:2, name:"Procure reinforcement bars for Lekki Phase 2", project:"Lekki Phase 2", priority:"high", due:"Tomorrow", done:false }, { id:3, name:"Complete safety inspection report — Abuja Road", project:"Abuja Road", priority:"med", due:"May 7", done:false }, { id:4, name:"Review sub-contractor quote (electrical)", project:"PH Warehouse", priority:"med", due:"May 8", done:false }, { id:5, name:"Upload daily progress photos — Lekki", project:"Lekki Phase 2", priority:"low", due:"Today", done:true }, { id:6, name:"Send payment reminder — INV-0042", project:"VI Office Complex", priority:"high", due:"Today", done:true }, ]; const TEAM = [ { name:"Emeka Okafor", role:"Project Manager", status:"on-site", initials:"EO", color:"#F5A623" }, { name:"Ngozi Adeyemi", role:"Quantity Surveyor", status:"office", initials:"NA", color:"#3B82F6" }, { name:"Bello Usman", role:"Site Foreman", status:"on-site", initials:"BU", color:"#22C55E" }, { name:"Taiwo Fashola", role:"Civil Engineer", status:"remote", initials:"TF", color:"#A855F7" }, { name:"Chidinma Eze", role:"Admin & Finance", status:"office", initials:"CE", color:"#EF4444" }, ]; const ACTIVITY = [ { text: <>Bello Usman uploaded 12 photos from Lekki Phase 2 site>, time:"2 min ago", color:"#F5A623" }, { text: <>INV-0041 marked as paid — ₦2.5M received from Zenith Properties>, time:"1 hr ago", color:"#22C55E" }, { text: <>AI Risk Alert: Abuja Road Km 14–16 showing 11-day delay risk>, time:"3 hrs ago", color:"#EF4444" }, { text: <>Ngozi Adeyemi updated budget estimate for VI Office Complex (Rev 3)>, time:"5 hrs ago", color:"#3B82F6" }, { text: <>Change Order #7 approved by FCT Infrastructure — +₦1.2M>, time:"Yesterday", color:"#A855F7" }, ]; const GANTT = [ { label:"Foundation", start:0, width:25, color:"#F5A623", pct:"100%" }, { label:"Structural Frame", start:20, width:35, color:"#3B82F6", pct:"72%" }, { label:"MEP Rough-in", start:45, width:25, color:"#A855F7", pct:"10%" }, { label:"Cladding & Facade", start:55, width:20, color:"#22C55E", pct:"0%" }, { label:"Interior Fit-out", start:70, width:20, color:"#EF4444", pct:"0%" }, { label:"Commissioning", start:85, width:15, color:"#F5A623", pct:"0%" }, ]; const CASHFLOW = [ { month:"Nov", income:18.2, expense:12.1 }, { month:"Dec", income:22.5, expense:17.8 }, { month:"Jan", income:15.0, expense:13.5 }, { month:"Feb", income:28.3, expense:19.2 }, { month:"Mar", income:31.5, expense:22.0 }, { month:"Apr", income:26.8, expense:18.5 }, { month:"May", income:34.2, expense:21.3 }, ]; const NAV = [ { id:"dashboard", label:"Dashboard", icon:"⬛", section:"main" }, { id:"projects", label:"Projects", icon:"🏗", section:"main" }, { id:"field", label:"Field Reports", icon:"📱", section:"main", badge:3 }, { id:"financials", label:"Financials", icon:"💰", section:"main" }, { id:"documents", label:"Documents", icon:"📁", section:"main" }, { id:"team", label:"Team", icon:"👥", section:"main" }, { id:"ai", label:"AI Assistant", icon:"✦", section:"tools" }, { id:"reports", label:"Reports", icon:"📊", section:"tools" }, { id:"settings", label:"Settings", icon:"⚙", section:"tools" }, ]; const fmt = (n) => "₦" + (n >= 1000000 ? (n/1000000).toFixed(1)+"M" : (n/1000).toFixed(0)+"K"); // ─── COMPONENTS ────────────────────────────────────────────────────────────── function Ring({ pct, size=60, color, children }) { const r = (size/2) - 5; const circ = 2*Math.PI*r; const fill = circ - (circ * pct/100); return ( ); } function NewProjectModal({ onClose }) { return (
| Invoice | Client | Project | Date | Amount | Status | Actions |
|---|---|---|---|---|---|---|
| {inv.id} | {inv.client} | {inv.project} | {inv.date} | {fmt(inv.amount)} | {inv.status.charAt(0).toUpperCase()+inv.status.slice(1)} |
{inv.status!=="paid" && }
|
| Name | Project | Type | Size | Date | Tag | |
|---|---|---|---|---|---|---|
| {d.name} | {d.project} | {d.type} | {d.size} | {d.date} | {d.tag} |