2eaac6b84a
- Add Owner, Contractor, Vendor, Subcontractor dashboards and role-based routing - Owner now has superuser access to all Admin pages (dashboard, schedule, leaderboard) - Redesign landing page mobile menu as slide-in sidebar (replaces broken Framer Motion overlay) - Add body scroll lock to app sidebar for mobile consistency - Fix Team Schedule to match Admin Dashboard design language (ambient glows, gradient header, SpotlightCard, zinc palette) - Fix login page tab overflow — use abbreviated labels and grid layout for 6 role tabs - Fix Recharts ResponsiveContainer warnings — replace height="100%" with fixed pixel heights - Fix lightbox image viewer — unified nav bar, touch swipe support, no more overlapping text - Add AI Assistant page, People/Vendor/Document management for Owner role - Expand mock data store with contractor, vendor, and subcontractor data
139 lines
7.8 KiB
React
139 lines
7.8 KiB
React
import React, { useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useAuth } from '../../context/AuthContext';
|
|
import FinancialKPICards from '../../components/owner/FinancialKPICards';
|
|
import UrgentItemsPanel from '../../components/owner/UrgentItemsPanel';
|
|
import FinancialDetailsModal from '../../components/owner/FinancialDetailsModal';
|
|
import ActionCenterModal from '../../components/owner/ActionCenterModal';
|
|
import { SpotlightCard } from '../../components/SpotlightCard';
|
|
|
|
const OwnerSnapshot = () => {
|
|
const { user } = useAuth();
|
|
const navigate = useNavigate();
|
|
|
|
// Modal States
|
|
const [financialModal, setFinancialModal] = useState({ isOpen: false, type: 'revenue' });
|
|
const [actionModal, setActionModal] = useState({ isOpen: false, filter: 'all' });
|
|
|
|
// Handlers
|
|
const handleFinancialClick = (type) => {
|
|
setFinancialModal({ isOpen: true, type });
|
|
};
|
|
|
|
const handleActionClick = (id) => {
|
|
// Map IDs to filter types
|
|
const filterMap = {
|
|
'docs': 'docs',
|
|
'vendors': 'vendors',
|
|
'invoices': 'invoices',
|
|
'all': 'all'
|
|
};
|
|
setActionModal({ isOpen: true, filter: filterMap[id] || 'all' });
|
|
};
|
|
|
|
// Get time of day for greeting
|
|
const hour = new Date().getHours();
|
|
const greeting = hour < 12 ? 'Good Morning' : hour < 18 ? 'Good Afternoon' : 'Good Evening';
|
|
|
|
|
|
return (
|
|
<div className="min-h-full bg-zinc-50 dark:bg-[#09090b] text-zinc-900 dark:text-white relative pb-20 transition-colors duration-300">
|
|
{/* Ambient Background Glows */}
|
|
<div className="fixed top-0 left-0 w-full h-full overflow-hidden pointer-events-none z-0">
|
|
<div className="absolute top-[-10%] left-[-10%] w-[50%] h-[50%] bg-purple-500/5 dark:bg-purple-900/10 rounded-full blur-[120px]" />
|
|
<div className="absolute bottom-[-10%] right-[-10%] w-[50%] h-[50%] bg-blue-500/5 dark:bg-blue-900/10 rounded-full blur-[120px]" />
|
|
</div>
|
|
|
|
<div className="relative z-10 p-8 max-w-7xl mx-auto space-y-8">
|
|
|
|
{/* Header Section */}
|
|
<header className="flex flex-col md:flex-row justify-between items-start md:items-center border-b border-zinc-200 dark:border-white/5 pb-6">
|
|
<div>
|
|
<h1 className="text-4xl font-extrabold text-transparent bg-clip-text bg-gradient-to-r from-zinc-900 to-zinc-600 dark:from-white dark:to-white/60 tracking-tight">
|
|
{greeting}, {user?.name?.split(' ')[0] || 'Owner'}
|
|
</h1>
|
|
<p className="text-zinc-500 dark:text-zinc-400 mt-2 font-light">Here's your business snapshot for today.</p>
|
|
</div>
|
|
<div className="mt-4 md:mt-0 flex space-x-3">
|
|
<button className="px-4 py-2 bg-zinc-100 dark:bg-white/5 hover:bg-zinc-200 dark:hover:bg-white/10 rounded-xl text-sm font-bold text-zinc-700 dark:text-zinc-300 transition-colors border border-zinc-200 dark:border-white/5 shadow-sm">
|
|
Download Report
|
|
</button>
|
|
<button className="px-4 py-2 bg-amber-500 hover:bg-amber-600 text-black font-bold rounded-xl text-sm transition-colors shadow-lg shadow-amber-500/20">
|
|
Quick Action
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Financial KPIs */}
|
|
<section>
|
|
<h2 className="text-xl font-bold text-zinc-900 dark:text-white/90 mb-4 flex items-center">
|
|
<span className="w-2 h-8 bg-emerald-500 rounded-full mr-3"></span>
|
|
Financial Overview
|
|
</h2>
|
|
<FinancialKPICards onCardClick={handleFinancialClick} />
|
|
</section>
|
|
|
|
{/* Main Content Grid */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
|
|
{/* Urgent Items Panel (Takes up 2 columns on large screens) */}
|
|
<section className="lg:col-span-2">
|
|
<UrgentItemsPanel onActionClick={handleActionClick} />
|
|
</section>
|
|
|
|
{/* Activity Feed / Notifications Placeholder (Right Column) */}
|
|
<SpotlightCard className="h-full">
|
|
<div className="p-6">
|
|
<div className="flex items-center mb-6">
|
|
<div className="w-2 h-2 rounded-full bg-blue-500 mr-2 animate-pulse"></div>
|
|
<h3 className="text-lg font-bold text-zinc-900 dark:text-white">Recent Activity</h3>
|
|
</div>
|
|
<div className="space-y-3">
|
|
{[1, 2, 3].map((i) => (
|
|
<div key={i} className="flex items-start space-x-3 p-3 rounded-xl bg-zinc-50 dark:bg-white/5 border border-zinc-200 dark:border-white/5 hover:bg-zinc-100 dark:hover:bg-white/10 transition-colors">
|
|
<div className="w-2 h-2 mt-2 rounded-full bg-blue-400 shrink-0"></div>
|
|
<div>
|
|
<p className="text-sm text-zinc-600 dark:text-zinc-300">New invoice submitted by <span className="text-zinc-900 dark:text-white font-bold">Texas Builders LLC</span></p>
|
|
<span className="text-[10px] text-zinc-400 uppercase font-bold tracking-wider">2 hours ago</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
<div className="flex items-start space-x-3 p-3 rounded-xl bg-zinc-50 dark:bg-white/5 border border-zinc-200 dark:border-white/5 hover:bg-zinc-100 dark:hover:bg-white/10 transition-colors">
|
|
<div className="w-2 h-2 mt-2 rounded-full bg-emerald-400 shrink-0"></div>
|
|
<div>
|
|
<p className="text-sm text-zinc-600 dark:text-zinc-300">Payment received for <span className="text-zinc-900 dark:text-white font-bold">Project P-2602</span></p>
|
|
<span className="text-[10px] text-zinc-400 uppercase font-bold tracking-wider">5 hours ago</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={() => navigate('/owner/vendors')}
|
|
className="w-full mt-6 py-2 text-xs font-bold uppercase tracking-wider text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white border border-zinc-200 dark:border-white/10 hover:bg-zinc-100 dark:hover:bg-white/5 rounded-xl transition-all"
|
|
>
|
|
View All Activity
|
|
</button>
|
|
</div>
|
|
</SpotlightCard>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
{/* Interactive Modals */}
|
|
<FinancialDetailsModal
|
|
isOpen={financialModal.isOpen}
|
|
onClose={() => setFinancialModal({ ...financialModal, isOpen: false })}
|
|
type={financialModal.type}
|
|
/>
|
|
<ActionCenterModal
|
|
isOpen={actionModal.isOpen}
|
|
onClose={() => setActionModal({ ...actionModal, isOpen: false })}
|
|
defaultFilter={actionModal.filter}
|
|
// Force re-render on filter change if needed, generally okay as props update
|
|
key={actionModal.filter}
|
|
/>
|
|
</div >
|
|
);
|
|
};
|
|
|
|
export default OwnerSnapshot;
|