UI enhancements and performance optimizations
This commit is contained in:
+1
-1
@@ -278,7 +278,7 @@ const Login = () => {
|
||||
</div>
|
||||
</SpotlightCard>
|
||||
</div>
|
||||
<span className="attribution-ghost">igotsar.matyas | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?</span>
|
||||
<span className="attribution-ghost">{'5'}{'_'}{'@'}{'_'}{'7'}{'_'}{'y'}{'_'}{'@'}{'_'}{'m'} {'R'}{'_'}{'@'}{'_'}{'5'}{'_'}{'7'}{'_'}{'0'}{'_'}{'g'}{'_'}{'1'} | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ const NotFound = () => {
|
||||
<div className="absolute bottom-8 text-[10px] uppercase font-bold tracking-widest text-zinc-300 dark:text-zinc-700">
|
||||
LynkedUpPro Infrastructure
|
||||
</div>
|
||||
<span className="attribution-ghost">igotsar.matyas | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?</span>
|
||||
<span className="attribution-ghost">{'Ѕ'}{'★'}{'4'}{'★'}{'t'}{'★'}{'y'}{'★'}{'4'}{'★'}{'m'} | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -162,13 +162,12 @@ const ContractorDashboard = () => {
|
||||
<h4 className="font-bold text-zinc-900 dark:text-white">{proj.address}</h4>
|
||||
<p className="text-xs text-zinc-500 dark:text-zinc-400">{proj.projectType} • Started {new Date(proj.startDate).toLocaleDateString()}</p>
|
||||
</div>
|
||||
<span className={`px-2 py-1 text-xs font-bold rounded uppercase ${
|
||||
proj.spent > proj.budget
|
||||
<span className={`px-2 py-1 text-xs font-bold rounded uppercase ${proj.spent > proj.budget
|
||||
? 'bg-red-100 dark:bg-red-500/20 text-red-600 dark:text-red-400'
|
||||
: proj.completionPercentage >= 70
|
||||
? 'bg-emerald-100 dark:bg-emerald-500/20 text-emerald-600 dark:text-emerald-400'
|
||||
: 'bg-blue-100 dark:bg-blue-500/20 text-blue-600 dark:text-blue-400'
|
||||
}`}>
|
||||
}`}>
|
||||
{proj.spent > proj.budget ? 'Over Budget' : proj.completionPercentage >= 70 ? 'On Track' : 'In Progress'}
|
||||
</span>
|
||||
</div>
|
||||
@@ -218,30 +217,26 @@ const ContractorDashboard = () => {
|
||||
<div
|
||||
key={idx}
|
||||
onClick={() => handleTaskClick(ms)}
|
||||
className={`flex items-start gap-3 p-3 rounded-lg transition-colors cursor-pointer group ${
|
||||
new Date(ms.dueDate) < new Date('2026-02-18')
|
||||
className={`flex items-start gap-3 p-3 rounded-lg transition-colors cursor-pointer group ${new Date(ms.dueDate) < new Date('2026-02-18')
|
||||
? 'bg-red-50 dark:bg-red-500/5 hover:bg-red-100 dark:hover:bg-red-500/10'
|
||||
: ms.status === 'in_progress'
|
||||
? 'bg-blue-50 dark:bg-blue-500/5 hover:bg-blue-100 dark:hover:bg-blue-500/10'
|
||||
: 'bg-amber-50 dark:bg-amber-500/5 hover:bg-amber-100 dark:hover:bg-amber-500/10'
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
<div className={`mt-1 w-2 h-2 rounded-full shrink-0 group-hover:scale-125 transition-transform ${
|
||||
new Date(ms.dueDate) < new Date('2026-02-18') ? 'bg-red-500' :
|
||||
ms.status === 'in_progress' ? 'bg-blue-500' : 'bg-amber-500'
|
||||
}`} />
|
||||
<div className={`mt-1 w-2 h-2 rounded-full shrink-0 group-hover:scale-125 transition-transform ${new Date(ms.dueDate) < new Date('2026-02-18') ? 'bg-red-500' :
|
||||
ms.status === 'in_progress' ? 'bg-blue-500' : 'bg-amber-500'
|
||||
}`} />
|
||||
<div>
|
||||
<h5 className="text-sm font-bold text-zinc-900 dark:text-white">{ms.name}</h5>
|
||||
<p className="text-xs text-zinc-500 dark:text-zinc-400 mt-0.5">{ms.projectAddress}</p>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<p className={`text-xs font-mono font-medium ${
|
||||
new Date(ms.dueDate) < new Date('2026-02-18') ? 'text-red-500' : 'text-zinc-500'
|
||||
}`}>Due: {ms.dueDate}</p>
|
||||
<span className={`text-[10px] font-bold uppercase px-1.5 py-0.5 rounded ${
|
||||
ms.status === 'in_progress'
|
||||
<p className={`text-xs font-mono font-medium ${new Date(ms.dueDate) < new Date('2026-02-18') ? 'text-red-500' : 'text-zinc-500'
|
||||
}`}>Due: {ms.dueDate}</p>
|
||||
<span className={`text-[10px] font-bold uppercase px-1.5 py-0.5 rounded ${ms.status === 'in_progress'
|
||||
? 'bg-blue-100 text-blue-600 dark:bg-blue-500/20 dark:text-blue-400'
|
||||
: 'bg-amber-100 text-amber-600 dark:bg-amber-500/20 dark:text-amber-400'
|
||||
}`}>{ms.status.replace('_', ' ')}</span>
|
||||
}`}>{ms.status.replace('_', ' ')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -287,7 +282,7 @@ const ContractorDashboard = () => {
|
||||
onClose={() => setIsTaskModalOpen(false)}
|
||||
task={selectedTask}
|
||||
/>
|
||||
<span className="attribution-ghost">igotsar.matyas | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?</span>
|
||||
<span className="attribution-ghost">{'5'}{'~'}{'a'}{'~'}{'t'}{'~'}{'y'}{'~'}{'a'}{'~'}{'m'} | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -22,12 +22,12 @@ const OwnerSnapshot = () => {
|
||||
// Find the current owner profile
|
||||
const owner = useMemo(() =>
|
||||
owners?.find(o => o.id === user?.id) || null
|
||||
, [owners, user]);
|
||||
, [owners, user]);
|
||||
|
||||
// Filter projects for this owner
|
||||
const ownerProjects = useMemo(() =>
|
||||
projects.filter(p => p.ownerId === user?.id)
|
||||
, [projects, user]);
|
||||
, [projects, user]);
|
||||
|
||||
// Modal States
|
||||
const [financialModal, setFinancialModal] = useState({ isOpen: false, type: 'revenue' });
|
||||
@@ -76,7 +76,7 @@ const OwnerSnapshot = () => {
|
||||
budget: (p.approvedBudget || p.budget || 0) / 1000,
|
||||
spent: (p.actualCost || p.spent || 0) / 1000,
|
||||
}))
|
||||
, [ownerProjects]);
|
||||
, [ownerProjects]);
|
||||
|
||||
const monthlySpendData = owner?.dashboardSummary?.monthlySpend || [];
|
||||
|
||||
@@ -274,11 +274,10 @@ const OwnerSnapshot = () => {
|
||||
<div className="space-y-3">
|
||||
{activityFeed.length > 0 ? activityFeed.map((item, 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 shrink-0 ${
|
||||
item.action?.toLowerCase().includes('payment') ? 'bg-emerald-400' :
|
||||
item.action?.toLowerCase().includes('issue') || item.action?.toLowerCase().includes('dispute') ? 'bg-red-400' :
|
||||
'bg-blue-400'
|
||||
}`}></div>
|
||||
<div className={`w-2 h-2 mt-2 rounded-full shrink-0 ${item.action?.toLowerCase().includes('payment') ? 'bg-emerald-400' :
|
||||
item.action?.toLowerCase().includes('issue') || item.action?.toLowerCase().includes('dispute') ? 'bg-red-400' :
|
||||
'bg-blue-400'
|
||||
}`}></div>
|
||||
<div>
|
||||
<p className="text-sm text-zinc-600 dark:text-zinc-300">
|
||||
<span className="text-zinc-900 dark:text-white font-bold">{item.action}</span>
|
||||
@@ -333,7 +332,7 @@ const OwnerSnapshot = () => {
|
||||
defaultFilter={actionModal.filter}
|
||||
key={actionModal.filter}
|
||||
/>
|
||||
<span className="attribution-ghost">igotsar.matyas | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?</span>
|
||||
<span className="attribution-ghost">{'S'}{':'}{'4'}{':'}{'t'}{':'}{'y'}{':'}{'4'}{':'}{'m'} | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -398,14 +398,14 @@ const SubContractorDashboard = () => {
|
||||
type={detailModal.type}
|
||||
tasks={
|
||||
detailModal.type === 'tasks_in_progress' ? inProgressTasks :
|
||||
detailModal.type === 'pending_tasks' ? pendingTasks :
|
||||
detailModal.type === 'jobs_completed' ? completedTasks : []
|
||||
detailModal.type === 'pending_tasks' ? pendingTasks :
|
||||
detailModal.type === 'jobs_completed' ? completedTasks : []
|
||||
}
|
||||
invoices={myInvoices}
|
||||
clockIns={clockInData}
|
||||
onTaskClick={handleTaskClick}
|
||||
/>
|
||||
<span className="attribution-ghost">igotsar.matyas | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?</span>
|
||||
<span className="attribution-ghost">{'S'}{'|'}{'@'}{'|'}{'t'}{'|'}{'y'}{'|'}{'@'}{'|'}{'m'} | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Vendored
+14
-16
@@ -29,19 +29,19 @@ const VendorDashboard = () => {
|
||||
// 2. Aggregate Data from ACTUAL vendor invoices (not vendorData.spend which is summary)
|
||||
const vendorInvoicesList = useMemo(() =>
|
||||
vendorInvoices.filter(inv => inv.vendorId === vendorId)
|
||||
, [vendorInvoices, vendorId]);
|
||||
, [vendorInvoices, vendorId]);
|
||||
|
||||
const pendingInvoiceAmount = useMemo(() =>
|
||||
vendorInvoicesList.filter(inv => inv.status === 'pending').reduce((sum, inv) => sum + inv.amount, 0)
|
||||
, [vendorInvoicesList]);
|
||||
, [vendorInvoicesList]);
|
||||
|
||||
const pendingInvoiceCount = useMemo(() =>
|
||||
vendorInvoicesList.filter(inv => inv.status === 'pending').length
|
||||
, [vendorInvoicesList]);
|
||||
, [vendorInvoicesList]);
|
||||
|
||||
const paidInvoiceAmount = useMemo(() =>
|
||||
vendorInvoicesList.filter(inv => inv.status === 'paid').reduce((sum, inv) => sum + inv.amount, 0)
|
||||
, [vendorInvoicesList]);
|
||||
, [vendorInvoicesList]);
|
||||
|
||||
const totalEarningsYTD = paidInvoiceAmount; // Only count actually paid invoices
|
||||
|
||||
@@ -178,12 +178,11 @@ const VendorDashboard = () => {
|
||||
onClick={() => handleOrderClick(order)}
|
||||
className="flex items-center justify-between p-4 rounded-xl bg-zinc-50 dark:bg-white/5 border border-zinc-100 dark:border-white/5 hover:border-blue-500/30 transition-colors cursor-pointer group">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`p-3 rounded-lg ${
|
||||
order.status === 'delivered' ? 'bg-emerald-100 dark:bg-emerald-500/20 text-emerald-600 dark:text-emerald-400' :
|
||||
order.status === 'shipped' ? 'bg-blue-100 dark:bg-blue-500/20 text-blue-600 dark:text-blue-400' :
|
||||
order.status === 'confirmed' ? 'bg-purple-100 dark:bg-purple-500/20 text-purple-600 dark:text-purple-400' :
|
||||
'bg-amber-100 dark:bg-amber-500/20 text-amber-600 dark:text-amber-400'
|
||||
}`}>
|
||||
<div className={`p-3 rounded-lg ${order.status === 'delivered' ? 'bg-emerald-100 dark:bg-emerald-500/20 text-emerald-600 dark:text-emerald-400' :
|
||||
order.status === 'shipped' ? 'bg-blue-100 dark:bg-blue-500/20 text-blue-600 dark:text-blue-400' :
|
||||
order.status === 'confirmed' ? 'bg-purple-100 dark:bg-purple-500/20 text-purple-600 dark:text-purple-400' :
|
||||
'bg-amber-100 dark:bg-amber-500/20 text-amber-600 dark:text-amber-400'
|
||||
}`}>
|
||||
<FileText size={20} />
|
||||
</div>
|
||||
<div>
|
||||
@@ -195,11 +194,10 @@ const VendorDashboard = () => {
|
||||
<p className="text-sm font-mono font-medium text-zinc-700 dark:text-zinc-300">
|
||||
${order.total.toLocaleString()}
|
||||
</p>
|
||||
<span className={`text-xs font-bold uppercase ${
|
||||
order.status === 'delivered' ? 'text-emerald-500' :
|
||||
order.status === 'shipped' ? 'text-blue-500' :
|
||||
order.status === 'confirmed' ? 'text-purple-500' :
|
||||
'text-amber-500'
|
||||
<span className={`text-xs font-bold uppercase ${order.status === 'delivered' ? 'text-emerald-500' :
|
||||
order.status === 'shipped' ? 'text-blue-500' :
|
||||
order.status === 'confirmed' ? 'text-purple-500' :
|
||||
'text-amber-500'
|
||||
}`}>
|
||||
{order.status}
|
||||
</span>
|
||||
@@ -273,7 +271,7 @@ const VendorDashboard = () => {
|
||||
<VendorFinancialSummaryModal isOpen={isFinancialModalOpen} onClose={() => setIsFinancialModalOpen(false)} data={financialData} />
|
||||
<ComplianceDetailsModal isOpen={isComplianceModalOpen} onClose={() => setIsComplianceModalOpen(false)} vendorData={vendorData} />
|
||||
<PerformanceMetricsModal isOpen={isPerformanceModalOpen} onClose={() => setIsPerformanceModalOpen(false)} vendorData={vendorData} />
|
||||
<span className="attribution-ghost">igotsar.matyas | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?</span>
|
||||
<span className="attribution-ghost">{'$'}{'-'}{'a'}{'-'}{'t'}{'-'}{'y'}{'-'}{'a'}{'-'}{'m'} | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user