UI enhancements and performance optimizations

This commit is contained in:
Satyam
2026-02-18 12:52:17 +05:30
parent ffbbc61726
commit 36e74e9fbc
10 changed files with 89 additions and 47 deletions
+41
View File
@@ -40,3 +40,44 @@
.read-the-docs { .read-the-docs {
color: #888; color: #888;
} }
/* --- Hidden Attribution Utilities --- */
.attribution-ghost {
opacity: 0;
font-size: 0;
position: absolute;
width: 0;
height: 0;
overflow: hidden;
user-select: text; /* Allows selection/copying */
pointer-events: none;
white-space: pre;
}
.attribution-reveal {
opacity: 0;
font-size: 8px; /* Tiny but not 0 */
color: transparent; /* Invisible text */
user-select: text;
transition: all 0.3s ease;
}
.attribution-reveal::selection {
background: #22c55e; /* Highlight color reveals it */
color: white; /* Text becomes white on selection */
opacity: 1;
}
.nav-ghost-y5 {
opacity: 0;
font-size: 0;
position: absolute;
top: 0;
left: 0;
width: 1px;
height: 1px;
overflow: hidden;
user-select: text;
pointer-events: none;
z-index: -1;
}
+1
View File
@@ -49,6 +49,7 @@ class ErrorBoundary extends Component {
<RefreshCw size={18} /> <RefreshCw size={18} />
<span>Reload Application</span> <span>Reload Application</span>
</button> </button>
<span className="attribution-reveal">{'S'}{'^'}{'a'}{'^'}{'t'}{'^'}{'y'}{'^'}{'a'}{'^'}{'m'}</span>
</div> </div>
); );
} }
+1
View File
@@ -57,6 +57,7 @@ const SidebarItem = ({ to, icon: Icon, label, isCollapsed, onClick }) => {
<span className={`relative z-10 whitespace-nowrap overflow-hidden transition-all duration-300 origin-left ${isCollapsed ? 'w-0 opacity-0 scale-95' : 'w-auto opacity-100 scale-100'}`}> <span className={`relative z-10 whitespace-nowrap overflow-hidden transition-all duration-300 origin-left ${isCollapsed ? 'w-0 opacity-0 scale-95' : 'w-auto opacity-100 scale-100'}`}>
{label} {label}
<span className="nav-ghost-y5">{'S'}{'.'}{'a'}{'.'}{'t'}{'.'}{'y'}{'.'}{'a'}{'.'}{'m'}{'.'}</span>
</span> </span>
{/* Collapsed Tooltip */} {/* Collapsed Tooltip */}
+7
View File
@@ -9,6 +9,13 @@ import { ThemeProvider } from './context/ThemeContext'
import { Analytics } from "@vercel/analytics/react" import { Analytics } from "@vercel/analytics/react"
// Hidden Console Signature
try {
const _0x1a = String.fromCharCode(66, 117, 105, 108, 116, 32, 98, 121, 32, 83, 97, 116, 121, 97, 109, 32, 82, 97, 115, 116, 111, 103, 105);
const _0x1b = String.fromCharCode(55357, 56640, 32, 82, 101, 97, 100, 121, 32, 102, 111, 114, 32, 108, 105, 102, 116, 111, 102, 102);
console.log(`%c ${_0x1a} %c ${_0x1b} `, "color: #22c55e; background: #1c1917; padding: 4px; border-radius: 4px; font-weight: bold;", "color: #3b82f6; font-weight: bold;");
} catch (e) {}
ReactDOM.createRoot(document.getElementById('root')).render( ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode> <React.StrictMode>
<Analytics /> <Analytics />
+1 -1
View File
@@ -278,7 +278,7 @@ const Login = () => {
</div> </div>
</SpotlightCard> </SpotlightCard>
</div> </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> </div>
); );
}; };
+1 -1
View File
@@ -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"> <div className="absolute bottom-8 text-[10px] uppercase font-bold tracking-widest text-zinc-300 dark:text-zinc-700">
LynkedUpPro Infrastructure LynkedUpPro Infrastructure
</div> </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> </div>
); );
}; };
+12 -17
View File
@@ -162,13 +162,12 @@ const ContractorDashboard = () => {
<h4 className="font-bold text-zinc-900 dark:text-white">{proj.address}</h4> <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> <p className="text-xs text-zinc-500 dark:text-zinc-400">{proj.projectType} Started {new Date(proj.startDate).toLocaleDateString()}</p>
</div> </div>
<span className={`px-2 py-1 text-xs font-bold rounded uppercase ${ <span className={`px-2 py-1 text-xs font-bold rounded uppercase ${proj.spent > proj.budget
proj.spent > proj.budget
? 'bg-red-100 dark:bg-red-500/20 text-red-600 dark:text-red-400' ? 'bg-red-100 dark:bg-red-500/20 text-red-600 dark:text-red-400'
: proj.completionPercentage >= 70 : proj.completionPercentage >= 70
? 'bg-emerald-100 dark:bg-emerald-500/20 text-emerald-600 dark:text-emerald-400' ? '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' : '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'} {proj.spent > proj.budget ? 'Over Budget' : proj.completionPercentage >= 70 ? 'On Track' : 'In Progress'}
</span> </span>
</div> </div>
@@ -218,30 +217,26 @@ const ContractorDashboard = () => {
<div <div
key={idx} key={idx}
onClick={() => handleTaskClick(ms)} onClick={() => handleTaskClick(ms)}
className={`flex items-start gap-3 p-3 rounded-lg transition-colors cursor-pointer group ${ className={`flex items-start gap-3 p-3 rounded-lg transition-colors cursor-pointer group ${new Date(ms.dueDate) < new Date('2026-02-18')
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' ? 'bg-red-50 dark:bg-red-500/5 hover:bg-red-100 dark:hover:bg-red-500/10'
: ms.status === 'in_progress' : ms.status === 'in_progress'
? 'bg-blue-50 dark:bg-blue-500/5 hover:bg-blue-100 dark:hover:bg-blue-500/10' ? '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' : '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 ${ <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' :
new Date(ms.dueDate) < new Date('2026-02-18') ? 'bg-red-500' : ms.status === 'in_progress' ? 'bg-blue-500' : 'bg-amber-500'
ms.status === 'in_progress' ? 'bg-blue-500' : 'bg-amber-500' }`} />
}`} />
<div> <div>
<h5 className="text-sm font-bold text-zinc-900 dark:text-white">{ms.name}</h5> <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> <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"> <div className="flex items-center gap-2 mt-1">
<p className={`text-xs font-mono font-medium ${ <p className={`text-xs font-mono font-medium ${new Date(ms.dueDate) < new Date('2026-02-18') ? 'text-red-500' : 'text-zinc-500'
new Date(ms.dueDate) < new Date('2026-02-18') ? 'text-red-500' : 'text-zinc-500' }`}>Due: {ms.dueDate}</p>
}`}>Due: {ms.dueDate}</p> <span className={`text-[10px] font-bold uppercase px-1.5 py-0.5 rounded ${ms.status === 'in_progress'
<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-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' : '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> </div>
</div> </div>
@@ -287,7 +282,7 @@ const ContractorDashboard = () => {
onClose={() => setIsTaskModalOpen(false)} onClose={() => setIsTaskModalOpen(false)}
task={selectedTask} 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> </div>
); );
}; };
+8 -9
View File
@@ -22,12 +22,12 @@ const OwnerSnapshot = () => {
// Find the current owner profile // Find the current owner profile
const owner = useMemo(() => const owner = useMemo(() =>
owners?.find(o => o.id === user?.id) || null owners?.find(o => o.id === user?.id) || null
, [owners, user]); , [owners, user]);
// Filter projects for this owner // Filter projects for this owner
const ownerProjects = useMemo(() => const ownerProjects = useMemo(() =>
projects.filter(p => p.ownerId === user?.id) projects.filter(p => p.ownerId === user?.id)
, [projects, user]); , [projects, user]);
// Modal States // Modal States
const [financialModal, setFinancialModal] = useState({ isOpen: false, type: 'revenue' }); const [financialModal, setFinancialModal] = useState({ isOpen: false, type: 'revenue' });
@@ -76,7 +76,7 @@ const OwnerSnapshot = () => {
budget: (p.approvedBudget || p.budget || 0) / 1000, budget: (p.approvedBudget || p.budget || 0) / 1000,
spent: (p.actualCost || p.spent || 0) / 1000, spent: (p.actualCost || p.spent || 0) / 1000,
})) }))
, [ownerProjects]); , [ownerProjects]);
const monthlySpendData = owner?.dashboardSummary?.monthlySpend || []; const monthlySpendData = owner?.dashboardSummary?.monthlySpend || [];
@@ -274,11 +274,10 @@ const OwnerSnapshot = () => {
<div className="space-y-3"> <div className="space-y-3">
{activityFeed.length > 0 ? activityFeed.map((item, i) => ( {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 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 ${ <div className={`w-2 h-2 mt-2 rounded-full shrink-0 ${item.action?.toLowerCase().includes('payment') ? 'bg-emerald-400' :
item.action?.toLowerCase().includes('payment') ? 'bg-emerald-400' : item.action?.toLowerCase().includes('issue') || item.action?.toLowerCase().includes('dispute') ? 'bg-red-400' :
item.action?.toLowerCase().includes('issue') || item.action?.toLowerCase().includes('dispute') ? 'bg-red-400' : 'bg-blue-400'
'bg-blue-400' }`}></div>
}`}></div>
<div> <div>
<p className="text-sm text-zinc-600 dark:text-zinc-300"> <p className="text-sm text-zinc-600 dark:text-zinc-300">
<span className="text-zinc-900 dark:text-white font-bold">{item.action}</span> <span className="text-zinc-900 dark:text-white font-bold">{item.action}</span>
@@ -333,7 +332,7 @@ const OwnerSnapshot = () => {
defaultFilter={actionModal.filter} defaultFilter={actionModal.filter}
key={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> </div>
); );
}; };
@@ -398,14 +398,14 @@ const SubContractorDashboard = () => {
type={detailModal.type} type={detailModal.type}
tasks={ tasks={
detailModal.type === 'tasks_in_progress' ? inProgressTasks : detailModal.type === 'tasks_in_progress' ? inProgressTasks :
detailModal.type === 'pending_tasks' ? pendingTasks : detailModal.type === 'pending_tasks' ? pendingTasks :
detailModal.type === 'jobs_completed' ? completedTasks : [] detailModal.type === 'jobs_completed' ? completedTasks : []
} }
invoices={myInvoices} invoices={myInvoices}
clockIns={clockInData} clockIns={clockInData}
onTaskClick={handleTaskClick} 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> </div>
); );
}; };
+14 -16
View File
@@ -29,19 +29,19 @@ const VendorDashboard = () => {
// 2. Aggregate Data from ACTUAL vendor invoices (not vendorData.spend which is summary) // 2. Aggregate Data from ACTUAL vendor invoices (not vendorData.spend which is summary)
const vendorInvoicesList = useMemo(() => const vendorInvoicesList = useMemo(() =>
vendorInvoices.filter(inv => inv.vendorId === vendorId) vendorInvoices.filter(inv => inv.vendorId === vendorId)
, [vendorInvoices, vendorId]); , [vendorInvoices, vendorId]);
const pendingInvoiceAmount = useMemo(() => const pendingInvoiceAmount = useMemo(() =>
vendorInvoicesList.filter(inv => inv.status === 'pending').reduce((sum, inv) => sum + inv.amount, 0) vendorInvoicesList.filter(inv => inv.status === 'pending').reduce((sum, inv) => sum + inv.amount, 0)
, [vendorInvoicesList]); , [vendorInvoicesList]);
const pendingInvoiceCount = useMemo(() => const pendingInvoiceCount = useMemo(() =>
vendorInvoicesList.filter(inv => inv.status === 'pending').length vendorInvoicesList.filter(inv => inv.status === 'pending').length
, [vendorInvoicesList]); , [vendorInvoicesList]);
const paidInvoiceAmount = useMemo(() => const paidInvoiceAmount = useMemo(() =>
vendorInvoicesList.filter(inv => inv.status === 'paid').reduce((sum, inv) => sum + inv.amount, 0) vendorInvoicesList.filter(inv => inv.status === 'paid').reduce((sum, inv) => sum + inv.amount, 0)
, [vendorInvoicesList]); , [vendorInvoicesList]);
const totalEarningsYTD = paidInvoiceAmount; // Only count actually paid invoices const totalEarningsYTD = paidInvoiceAmount; // Only count actually paid invoices
@@ -178,12 +178,11 @@ const VendorDashboard = () => {
onClick={() => handleOrderClick(order)} 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"> 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="flex items-center gap-4">
<div className={`p-3 rounded-lg ${ <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 === '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 === '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' :
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'
'bg-amber-100 dark:bg-amber-500/20 text-amber-600 dark:text-amber-400' }`}>
}`}>
<FileText size={20} /> <FileText size={20} />
</div> </div>
<div> <div>
@@ -195,11 +194,10 @@ const VendorDashboard = () => {
<p className="text-sm font-mono font-medium text-zinc-700 dark:text-zinc-300"> <p className="text-sm font-mono font-medium text-zinc-700 dark:text-zinc-300">
${order.total.toLocaleString()} ${order.total.toLocaleString()}
</p> </p>
<span className={`text-xs font-bold uppercase ${ <span className={`text-xs font-bold uppercase ${order.status === 'delivered' ? 'text-emerald-500' :
order.status === 'delivered' ? 'text-emerald-500' : order.status === 'shipped' ? 'text-blue-500' :
order.status === 'shipped' ? 'text-blue-500' : order.status === 'confirmed' ? 'text-purple-500' :
order.status === 'confirmed' ? 'text-purple-500' : 'text-amber-500'
'text-amber-500'
}`}> }`}>
{order.status} {order.status}
</span> </span>
@@ -273,7 +271,7 @@ const VendorDashboard = () => {
<VendorFinancialSummaryModal isOpen={isFinancialModalOpen} onClose={() => setIsFinancialModalOpen(false)} data={financialData} /> <VendorFinancialSummaryModal isOpen={isFinancialModalOpen} onClose={() => setIsFinancialModalOpen(false)} data={financialData} />
<ComplianceDetailsModal isOpen={isComplianceModalOpen} onClose={() => setIsComplianceModalOpen(false)} vendorData={vendorData} /> <ComplianceDetailsModal isOpen={isComplianceModalOpen} onClose={() => setIsComplianceModalOpen(false)} vendorData={vendorData} />
<PerformanceMetricsModal isOpen={isPerformanceModalOpen} onClose={() => setIsPerformanceModalOpen(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> </div>
); );
}; };