Owners box:navigate user to project details page when click on project from subcontractor table list

This commit is contained in:
Mayur Shinde
2026-06-04 15:37:16 +05:30
parent 96381d4e0a
commit 2f1703870a
4 changed files with 27 additions and 347 deletions
@@ -1,4 +1,5 @@
import React, { useMemo, useState, useRef, useEffect } from 'react'; import React, { useMemo, useState, useRef, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Wallet, Users, Search, ChevronDown, ChevronRight, X, Check, FilterX } from 'lucide-react'; import { Wallet, Users, Search, ChevronDown, ChevronRight, X, Check, FilterX } from 'lucide-react';
// ───────────────────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────────────────
@@ -134,17 +135,25 @@ function StatusBadge({ status, colorMap }) {
// Expanded detail view (project → requests hierarchy) // Expanded detail view (project → requests hierarchy)
// ───────────────────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────────────────
function ContractorDetail({ projects }) { function ContractorDetail({ projects }) {
const navigate = useNavigate();
return ( return (
<div className="flex flex-col gap-3 py-3 px-3 sm:px-5"> <div className="flex flex-col gap-3 py-3 px-3 sm:px-5">
{projects.map((proj, pi) => ( {projects.map((proj, pi) => (
<div key={proj.projectId || proj.projectName || pi} className="rounded-xl border border-zinc-200 dark:border-white/[0.06] overflow-hidden bg-white dark:bg-zinc-800/50"> <div key={proj.projectId || proj.projectName || pi} className="rounded-xl border border-zinc-200 dark:border-white/[0.06] overflow-hidden bg-white dark:bg-zinc-800/50">
{/* Project header */} {/* Project header */}
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 px-4 py-2.5 bg-zinc-50 dark:bg-white/[0.03] border-b border-zinc-200 dark:border-white/[0.06]"> <div
onClick={() => proj.projectId && navigate(`/owner/projects/${proj.projectId}?tab=budget`)}
className={`flex flex-wrap items-center gap-x-4 gap-y-1 px-4 py-2.5 bg-zinc-50 dark:bg-white/[0.03] border-b border-zinc-200 dark:border-white/[0.06] ${proj.projectId ? 'cursor-pointer hover:bg-zinc-100 dark:hover:bg-white/[0.06] transition-colors' : ''}`}
>
<span className="text-[11px] font-black uppercase tracking-widest text-zinc-500 dark:text-zinc-400">Project</span> <span className="text-[11px] font-black uppercase tracking-widest text-zinc-500 dark:text-zinc-400">Project</span>
<span className="text-xs font-bold text-zinc-800 dark:text-zinc-200 flex-1 min-w-0 truncate">{proj.projectName}</span> <span className="text-xs font-bold text-zinc-800 dark:text-zinc-200 flex-1 min-w-0 truncate hover:text-amber-500 transition-colors">{proj.projectName}</span>
{proj.projectAddress && ( {proj.projectAddress && (
<span className="text-[10px] text-zinc-400 dark:text-zinc-500 truncate max-w-[200px]">{proj.projectAddress}</span> <span className="text-[10px] text-zinc-400 dark:text-zinc-500 truncate max-w-[200px]">{proj.projectAddress}</span>
)} )}
{proj.projectId && (
<ChevronRight size={12} className="text-zinc-400 dark:text-zinc-500 shrink-0" />
)}
</div> </div>
{/* Requests — table on md+, cards on mobile */} {/* Requests — table on md+, cards on mobile */}
@@ -298,6 +307,7 @@ const SubcontractorPaymentsPanel = ({
if (requested <= 0) continue; if (requested <= 0) continue;
const sub = subById.get(task.subcontractorId); const sub = subById.get(task.subcontractorId);
const project = task.projectId ? projectById.get(task.projectId) : null; const project = task.projectId ? projectById.get(task.projectId) : null;
if (!project) continue;
entries.push({ entries.push({
taskId: task.id, taskId: task.id,
subId: task.subcontractorId, subId: task.subcontractorId,
-341
View File
@@ -7612,139 +7612,6 @@ const MOCK_SUBCONTRACTOR_TASKS = [
updatedAt: '2026-05-18T23:10:00Z', updatedAt: '2026-05-18T23:10:00Z',
}, },
// -------------------------------------------------------------------
// sct_005 Maya (sub_002) Attic ventilation inspection, On Hold
// -------------------------------------------------------------------
{
id: 'sct_005',
companyId: 'lynkeduppro',
companyName: 'LynkedUp Pro Roofing',
subcontractorId: 'sub_002',
subcontractorName: 'Maya Patel',
assignedBy: 'a1',
assignedByName: 'Wade Hollis',
title: 'Inspect attic ventilation',
location: '6909 Custer Rd, Plano, TX 75023',
description: 'Customer reports unusually high attic temps. Check ridge vent, soffit airflow, and intake/exhaust balance. Recommend remediation.',
dueDate: '2026-05-30',
priority: 'medium',
category: 'Inspection',
assignedDate: '2026-05-12T10:00:00Z',
status: 'On Hold',
photos: [
{ id: 'sct_005_p1', name: 'Attic_View.jpg', url: '/assets/images/properties/Roof_Inspection_Angle.jpg', annotation: 'Access from south gable' },
],
statusHistory: [
{ id: 'sct_005_h1', status: 'Assigned', actorId: 'a1', actorName: 'Wade Hollis', comment: 'Task created.', at: '2026-05-12T10:00:00Z' },
{ id: 'sct_005_h2', status: 'In Progress', actorId: 'sub_002', actorName: 'Maya Patel', comment: 'Initial pass done; baffles look blocked.', at: '2026-05-14T11:00:00Z' },
{ id: 'sct_005_h3', status: 'On Hold', actorId: 'sub_002', actorName: 'Maya Patel', comment: 'Waiting on homeowner to clear stored boxes from access path.', at: '2026-05-15T16:00:00Z' },
],
thread: [
{ id: 'sct_005_t1', channel: 'group', senderId: 'sub_002', senderName: 'Maya Patel', senderRole: 'Subcontractor', body: 'Access is blocked — homeowner needs to move some storage before I can complete the inspection.', at: '2026-05-15T15:55:00Z', mine: true },
{ id: 'sct_005_t2', channel: 'direct', senderId: 'a1', senderName: 'Wade Hollis', senderRole: 'Admin', body: 'I will reach out to the homeowner and reschedule.', at: '2026-05-15T16:20:00Z' },
],
expenses: [
{ id: 'sct_005_e1', description: 'Infrared thermometer (rental)', category: 'Equipment Rental', amount: 35.0, notes: '', receiptUrl: '', createdAt: '2026-05-14T10:30:00Z' },
],
fees: [
{ id: 'sct_005_f1', description: 'Inspection — initial visit', type: 'Inspection Fee', amount: 125, createdAt: '2026-05-14T13:00:00Z' },
],
activities: [
{
id: 'sct_005_a1', type: 'status_change', status: 'Assigned',
note: 'Task created. Customer reports unusually high attic temps.',
photos: [],
actorId: 'a1', actorName: 'Wade Hollis', actorRole: 'Admin',
at: '2026-05-12T10:00:00Z',
},
{
id: 'sct_005_a2', type: 'status_change', status: 'In Progress',
note: 'Initial inspection pass — soffit baffles appear blocked. Need attic access for confirmation.',
photos: [
{ id: 'sct_005_a2_p1', url: '/assets/images/properties/Roof_Inspection_Angle.jpg', name: 'south-gable.jpg' },
],
actorId: 'sub_002', actorName: 'Maya Patel', actorRole: 'Subcontractor',
at: '2026-05-14T11:00:00Z',
},
{
id: 'sct_005_a3', type: 'status_change', status: 'On Hold',
note: 'Work stopped — homeowner needs to clear stored boxes blocking attic access before I can finish.',
photos: [
{ id: 'sct_005_a3_p1', url: '/assets/images/properties/Beige_Two_Story_House.jpg', name: 'access-blocked.jpg' },
],
actorId: 'sub_002', actorName: 'Maya Patel', actorRole: 'Subcontractor',
at: '2026-05-15T16:00:00Z',
},
],
projectId: 'PRJ-2026-007',
paymentStatus: 'Partially Paid',
paymentRequestedAt: '2026-05-14T13:00:00Z',
amountPaid: 80,
paidAt: '2026-05-18T09:00:00Z',
createdAt: '2026-05-12T10:00:00Z',
updatedAt: '2026-05-15T16:20:00Z',
},
// -------------------------------------------------------------------
// sct_006 Maya (sub_002) Gutter replacement, Pre-Work Inspection
// -------------------------------------------------------------------
{
id: 'sct_006',
companyId: 'lynkeduppro',
companyName: 'LynkedUp Pro Roofing',
subcontractorId: 'sub_002',
subcontractorName: 'Maya Patel',
assignedBy: 'own_001',
assignedByName: 'Justin Johnson',
title: 'Replace gutter on south elevation',
location: '7224 Independence Pkwy, Plano, TX 75025',
description: '30 ft section of K-style gutter — pulling away from fascia after storm. Replace with new 6" oversized gutter, match existing bronze color.',
dueDate: '2026-06-02',
priority: 'low',
category: 'Gutters',
assignedDate: '2026-05-17T14:00:00Z',
status: 'Pre-Work Inspection',
photos: [
{ id: 'sct_006_p1', name: 'Gutter_Damage.jpg', url: '/assets/images/properties/Broken_Roof_Surface.jpg', annotation: 'Gutter pulling away from fascia — south elevation' },
],
statusHistory: [
{ id: 'sct_006_h1', status: 'Assigned', actorId: 'own_001', actorName: 'Justin Johnson', comment: 'Task created and assigned.', at: '2026-05-17T14:00:00Z' },
{ id: 'sct_006_h2', status: 'Pre-Work Inspection', actorId: 'sub_002', actorName: 'Maya Patel', comment: 'Site walk done — materials staged, access confirmed. Measured 32 ft run, bronze 6" gutter ordered.', at: '2026-05-26T10:00:00Z' },
],
thread: [
{ id: 'sct_006_t1', channel: 'group', senderId: 'sub_002', senderName: 'Maya Patel', senderRole: 'Subcontractor', body: 'On site for pre-work walkthrough. Gutter is pulling at four hangers — 32 ft run needs full replacement. Ordering materials now.', at: '2026-05-26T10:05:00Z', mine: true },
{ id: 'sct_006_t2', channel: 'group', senderId: 'own_001', senderName: 'Justin Johnson', senderRole: 'Owner', body: 'Perfect. Confirm when materials arrive and I will coordinate access with the homeowner.', at: '2026-05-26T10:30:00Z' },
],
expenses: [
{ id: 'sct_006_e1', description: '6" K-style gutter bronze (32 ft)', category: 'Materials', amount: 118.0, notes: 'Special order — delivery expected May 28.', receiptUrl: '', createdAt: '2026-05-26T11:00:00Z' },
],
fees: [],
activities: [
{
id: 'sct_006_a1', type: 'status_change', status: 'Assigned',
note: 'Task created and assigned. 30 ft gutter section pulling away from fascia after storm damage.',
photos: [],
actorId: 'own_001', actorName: 'Justin Johnson', actorRole: 'Owner',
at: '2026-05-17T14:00:00Z',
},
{
id: 'sct_006_a2', type: 'status_change', status: 'Pre-Work Inspection',
note: 'Site walk done — measured 32 ft run, confirmed bronze colour match, four failed hangers identified. Materials ordered, work scheduled for May 29.',
photos: [
{ id: 'sct_006_a2_p1', url: '/assets/images/properties/Broken_Roof_Surface.jpg', name: 'gutter-inspection.jpg' },
],
actorId: 'sub_002', actorName: 'Maya Patel', actorRole: 'Subcontractor',
at: '2026-05-26T10:00:00Z',
},
],
projectId: 'PRJ-2026-009',
paymentStatus: 'Unpaid',
paymentRequestedAt: null,
amountPaid: 0,
createdAt: '2026-05-17T14:00:00Z',
updatedAt: '2026-05-26T11:00:00Z',
},
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// sct_007 Maya (sub_002) completed earlier this month // sct_007 Maya (sub_002) completed earlier this month
// ------------------------------------------------------------------- // -------------------------------------------------------------------
@@ -8011,60 +7878,6 @@ const MOCK_SUBCONTRACTOR_TASKS = [
updatedAt: '2026-05-27T11:05:00Z', updatedAt: '2026-05-27T11:05:00Z',
}, },
// -------------------------------------------------------------------
// sct_009 Jennifer Castillo (sub_004) Cancelled plumbing
// -------------------------------------------------------------------
{
id: 'sct_009',
companyId: 'lynkeduppro',
companyName: 'LynkedUp Pro Roofing',
subcontractorId: 'sub_004',
subcontractorName: 'Jennifer Castillo',
assignedBy: 'own_001',
assignedByName: 'Justin Johnson',
title: 'Rough-in plumbing for ADU bathroom',
location: '6112 Roundrock Dr, Plano, TX 75025',
description: 'New ADU build — bathroom rough-in. Coordinate with framing inspection.',
dueDate: '2026-06-15',
priority: 'medium',
category: 'Plumbing',
assignedDate: '2026-05-09T10:00:00Z',
status: 'Cancelled',
photos: [],
statusHistory: [
{ id: 'sct_009_h1', status: 'Assigned', actorId: 'own_001', actorName: 'Justin Johnson', comment: 'Task created and assigned.', at: '2026-05-09T10:00:00Z' },
{ id: 'sct_009_h2', status: 'Cancelled', actorId: 'own_001', actorName: 'Justin Johnson', comment: 'Homeowner cancelled the ADU project — entire scope suspended. Task voided, no further work required.', at: '2026-05-20T15:00:00Z' },
],
thread: [
{ id: 'sct_009_t1', channel: 'group', senderId: 'own_001', senderName: 'Justin Johnson', senderRole: 'Owner', body: 'Jennifer — unfortunately the homeowner has decided to cancel the ADU build entirely. No further work needed on this task. Apologies for the inconvenience.', at: '2026-05-20T15:02:00Z' },
{ id: 'sct_009_t2', channel: 'group', senderId: 'sub_004', senderName: 'Jennifer Castillo', senderRole: 'Subcontractor', body: 'Understood — no work had been started so no impact on our end. Thanks for the heads-up.', at: '2026-05-20T15:30:00Z', mine: true },
],
expenses: [],
fees: [],
activities: [
{
id: 'sct_009_a1', type: 'status_change', status: 'Assigned',
note: 'Task created. New ADU build — bathroom rough-in to coordinate with framing inspection.',
photos: [],
actorId: 'own_001', actorName: 'Justin Johnson', actorRole: 'Owner',
at: '2026-05-09T10:00:00Z',
},
{
id: 'sct_009_a2', type: 'status_change', status: 'Cancelled',
note: 'Task cancelled — homeowner suspended the ADU project. No work was started.',
photos: [],
actorId: 'own_001', actorName: 'Justin Johnson', actorRole: 'Owner',
at: '2026-05-20T15:00:00Z',
},
],
projectId: null,
paymentStatus: 'Unpaid',
paymentRequestedAt: null,
amountPaid: 0,
createdAt: '2026-05-09T10:00:00Z',
updatedAt: '2026-05-20T15:30:00Z',
},
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// sct_011 Maya Patel (sub_002) full roof replacement PRJ-2026-001, PAID // sct_011 Maya Patel (sub_002) full roof replacement PRJ-2026-001, PAID
// ------------------------------------------------------------------- // -------------------------------------------------------------------
@@ -8238,51 +8051,6 @@ const MOCK_SUBCONTRACTOR_TASKS = [
updatedAt: '2026-04-28T16:00:00Z', updatedAt: '2026-04-28T16:00:00Z',
}, },
// -------------------------------------------------------------------
// sct_015 Jennifer Castillo (sub_004) PRJ-2026-007, Partially Paid
// -------------------------------------------------------------------
{
id: 'sct_015',
companyId: 'lynkeduppro',
companyName: 'LynkedUp Pro Roofing',
subcontractorId: 'sub_004',
subcontractorName: 'Jennifer Castillo',
assignedBy: 'a1',
assignedByName: 'Wade Hollis',
title: 'Hot water heater replacement — PRJ-007',
location: '6909 Custer Rd, Plano, TX 75023',
description: 'Replace 40-gal gas water heater (unit failed). Install Bradford White 40-gal NG, reconnect supply & drain.',
dueDate: '2026-05-20',
priority: 'high',
category: 'Plumbing',
assignedDate: '2026-05-14T08:00:00Z',
status: 'Completed',
photos: [],
statusHistory: [
{ id: 'sct_015_h1', status: 'Assigned', actorId: 'a1', actorName: 'Wade Hollis', comment: 'Assigned.', at: '2026-05-14T08:00:00Z' },
{ id: 'sct_015_h2', status: 'Completed', actorId: 'sub_004', actorName: 'Jennifer Castillo', comment: 'Complete.', at: '2026-05-16T13:00:00Z' },
],
thread: [],
expenses: [
{ id: 'sct_015_e1', description: 'Bradford White 40-gal NG heater', category: 'Materials', amount: 680, notes: '', receiptUrl: '', createdAt: '2026-05-14T10:00:00Z' },
{ id: 'sct_015_e2', description: 'Gas flex connector + fittings', category: 'Materials', amount: 48, notes: '', receiptUrl: '', createdAt: '2026-05-14T10:05:00Z' },
{ id: 'sct_015_e3', description: 'Expansion tank + pressure relief', category: 'Materials', amount: 95, notes: '', receiptUrl: '', createdAt: '2026-05-14T10:10:00Z' },
{ id: 'sct_015_e4', description: 'Haul-away — old unit', category: 'Disposal / Dumpster', amount: 65, notes: '', receiptUrl: '', createdAt: '2026-05-16T14:00:00Z' },
],
fees: [
{ id: 'sct_015_f1', description: 'Labour — WHR install', type: 'Labour / Service Fee', amount: 420, createdAt: '2026-05-16T13:10:00Z' },
{ id: 'sct_015_f2', description: 'Mobilization', type: 'Mobilization Fee', amount: 75, createdAt: '2026-05-14T08:00:00Z' },
],
activities: [],
projectId: 'PRJ-2026-007',
paymentStatus: 'Partially Paid',
paymentRequestedAt: '2026-05-16T14:00:00Z',
amountPaid: 700,
paidAt: '2026-05-22T10:00:00Z',
createdAt: '2026-05-14T08:00:00Z',
updatedAt: '2026-05-16T14:00:00Z',
},
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// sct_016 Dwayne Boudreaux (sub_003) interior repaint PRJ-2026-004, Paid // sct_016 Dwayne Boudreaux (sub_003) interior repaint PRJ-2026-004, Paid
// ------------------------------------------------------------------- // -------------------------------------------------------------------
@@ -8325,47 +8093,6 @@ const MOCK_SUBCONTRACTOR_TASKS = [
updatedAt: '2026-05-03T16:00:00Z', updatedAt: '2026-05-03T16:00:00Z',
}, },
// -------------------------------------------------------------------
// sct_017 Dwayne Boudreaux (sub_003) PRJ-2026-007 ceiling repaint, Unpaid
// -------------------------------------------------------------------
{
id: 'sct_017',
companyId: 'lynkeduppro',
companyName: 'LynkedUp Pro Roofing',
subcontractorId: 'sub_003',
subcontractorName: 'Dwayne Boudreaux',
assignedBy: 'a1',
assignedByName: 'Wade Hollis',
title: 'Ceiling repaint after water damage — PRJ-007',
location: '6909 Custer Rd, Plano, TX 75023',
description: 'Repaint ceilings in master bedroom and en-suite after remediation. Stain block primer + 2 coats flat white.',
dueDate: '2026-05-28',
priority: 'medium',
category: 'Painting',
assignedDate: '2026-05-18T10:00:00Z',
status: 'Completed',
photos: [],
statusHistory: [
{ id: 'sct_017_h1', status: 'Assigned', actorId: 'a1', actorName: 'Wade Hollis', comment: 'Assigned.', at: '2026-05-18T10:00:00Z' },
{ id: 'sct_017_h2', status: 'Completed', actorId: 'sub_003', actorName: 'Dwayne Boudreaux', comment: 'Complete.', at: '2026-05-25T14:00:00Z' },
],
thread: [],
expenses: [
{ id: 'sct_017_e1', description: 'Zinsser stain-block primer — 1 gal', category: 'Materials', amount: 52, notes: '', receiptUrl: '', createdAt: '2026-05-19T08:00:00Z' },
{ id: 'sct_017_e2', description: 'Ceiling flat white — 1 gal', category: 'Materials', amount: 42, notes: '', receiptUrl: '', createdAt: '2026-05-19T08:05:00Z' },
],
fees: [
{ id: 'sct_017_f1', description: 'Labour — ceiling repaint (1.5 days)', type: 'Labour / Service Fee', amount: 580, createdAt: '2026-05-25T14:10:00Z' },
],
activities: [],
projectId: 'PRJ-2026-007',
paymentStatus: 'Unpaid',
paymentRequestedAt: '2026-05-25T15:00:00Z',
amountPaid: 0,
createdAt: '2026-05-18T10:00:00Z',
updatedAt: '2026-05-25T15:00:00Z',
},
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// sct_018 Greg Alston (sub_005) window replacement PRJ-2026-003, Partially Paid // sct_018 Greg Alston (sub_005) window replacement PRJ-2026-003, Partially Paid
// ------------------------------------------------------------------- // -------------------------------------------------------------------
@@ -8409,46 +8136,6 @@ const MOCK_SUBCONTRACTOR_TASKS = [
createdAt: '2026-05-03T09:00:00Z', createdAt: '2026-05-03T09:00:00Z',
updatedAt: '2026-05-13T16:00:00Z', updatedAt: '2026-05-13T16:00:00Z',
}, },
// -------------------------------------------------------------------
// sct_019 Greg Alston (sub_005) window sealing PRJ-2026-009, Unpaid
// -------------------------------------------------------------------
{
id: 'sct_019',
companyId: 'lynkeduppro',
companyName: 'LynkedUp Pro Roofing',
subcontractorId: 'sub_005',
subcontractorName: 'Greg Alston',
assignedBy: 'a1',
assignedByName: 'Wade Hollis',
title: 'Re-seal & caulk all windows — PRJ-009',
location: '7224 Independence Pkwy, Plano, TX 75025',
description: 'Full perimeter re-seal on 8 windows. Remove old silicone, clean frames, apply new NP1 polyurethane sealant.',
dueDate: '2026-06-05',
priority: 'low',
category: 'Windows & Glazing',
assignedDate: '2026-05-22T09:00:00Z',
status: 'In Progress',
photos: [],
statusHistory: [
{ id: 'sct_019_h1', status: 'Assigned', actorId: 'a1', actorName: 'Wade Hollis', comment: 'Assigned.', at: '2026-05-22T09:00:00Z' },
{ id: 'sct_019_h2', status: 'In Progress', actorId: 'sub_005', actorName: 'Greg Alston', comment: 'In progress.', at: '2026-05-28T08:30:00Z' },
],
thread: [],
expenses: [
{ id: 'sct_019_e1', description: 'NP1 polyurethane sealant (6 tubes)', category: 'Materials', amount: 78, notes: '', receiptUrl: '', createdAt: '2026-05-23T07:00:00Z' },
],
fees: [
{ id: 'sct_019_f1', description: 'Labour — window sealing (1 day)', type: 'Labour / Service Fee', amount: 380, createdAt: '2026-05-28T12:00:00Z' },
],
activities: [],
projectId: 'PRJ-2026-009',
paymentStatus: 'Unpaid',
paymentRequestedAt: '2026-05-28T12:00:00Z',
amountPaid: 0,
createdAt: '2026-05-22T09:00:00Z',
updatedAt: '2026-05-28T12:00:00Z',
},
]; ];
const MOCK_NOTIFICATIONS = [ const MOCK_NOTIFICATIONS = [
@@ -8470,20 +8157,6 @@ const MOCK_NOTIFICATIONS = [
taskId: 'sct_004', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', taskId: 'sct_004', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing',
isRead: false, createdAt: '2026-05-18T22:40:00Z', isRead: false, createdAt: '2026-05-18T22:40:00Z',
}, },
{
id: 'notif_003', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR',
type: 'task_assigned',
message: 'You have been assigned a task by LynkedUp Pro Roofing — "Replace gutter on south elevation"',
taskId: 'sct_006', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing',
isRead: false, createdAt: '2026-05-17T14:00:00Z',
},
{
id: 'notif_004', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR',
type: 'task_assigned',
message: 'You have been assigned a task by LynkedUp Pro Roofing — "Inspect attic ventilation"',
taskId: 'sct_005', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing',
isRead: true, createdAt: '2026-05-12T10:00:00Z',
},
// -- Message Received -------------------------- // -- Message Received --------------------------
{ {
id: 'notif_005', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', id: 'notif_005', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR',
@@ -8499,13 +8172,6 @@ const MOCK_NOTIFICATIONS = [
taskId: 'sct_004', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', taskId: 'sct_004', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing',
isRead: false, createdAt: '2026-05-18T22:42:00Z', isRead: false, createdAt: '2026-05-18T22:42:00Z',
}, },
{
id: 'notif_007', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR',
type: 'message_received',
message: 'Wade Hollis added an internal note on sct_005 — "I will reach out to the homeowner…"',
taskId: 'sct_005', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing',
isRead: true, createdAt: '2026-05-15T16:20:00Z',
},
// -- Task Updated ------------------------------ // -- Task Updated ------------------------------
{ {
id: 'notif_008', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', id: 'notif_008', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR',
@@ -8536,13 +8202,6 @@ const MOCK_NOTIFICATIONS = [
taskId: 'sct_001', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', taskId: 'sct_001', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing',
isRead: true, createdAt: '2026-05-16T08:30:00Z', isRead: true, createdAt: '2026-05-16T08:30:00Z',
}, },
{
id: 'notif_012', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR',
type: 'status_changed',
message: 'Status for sct_005 changed to On Hold by Maya Patel.',
taskId: 'sct_005', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing',
isRead: true, createdAt: '2026-05-15T16:00:00Z',
},
{ {
id: 'notif_013', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', id: 'notif_013', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR',
type: 'status_changed', type: 'status_changed',
+14 -3
View File
@@ -1,5 +1,5 @@
import React, { useState, useMemo, useRef, useEffect } from 'react'; import React, { useState, useMemo, useRef, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
import { useAuth } from '../../context/AuthContext'; import { useAuth } from '../../context/AuthContext';
import { useMockStore } from '../../data/mockStore'; import { useMockStore } from '../../data/mockStore';
import { LIFECYCLE_STAGES, REWORK_STAGE, nextStage, prevStage, stageToPct } from '../../data/lifecycle'; import { LIFECYCLE_STAGES, REWORK_STAGE, nextStage, prevStage, stageToPct } from '../../data/lifecycle';
@@ -199,7 +199,15 @@ const OwnerProjectDetail = () => {
} = useMockStore(); } = useMockStore();
const navigate = useNavigate(); const navigate = useNavigate();
const { can } = usePermissions(); const { can } = usePermissions();
const [activeTab, setActiveTab] = useState('overview'); const [searchParams, setSearchParams] = useSearchParams();
const [activeTab, setActiveTab] = useState(() => searchParams.get('tab') || 'overview');
useEffect(() => {
const tab = searchParams.get('tab');
if (tab && tab !== activeTab) {
setActiveTab(tab);
}
}, [searchParams, activeTab]);
const [selectedCO, setSelectedCO] = useState(null); const [selectedCO, setSelectedCO] = useState(null);
const [selectedInvoice, setSelectedInvoice] = useState(null); const [selectedInvoice, setSelectedInvoice] = useState(null);
const [selectedVersion, setSelectedVersion] = useState(null); const [selectedVersion, setSelectedVersion] = useState(null);
@@ -554,7 +562,10 @@ const OwnerProjectDetail = () => {
return ( return (
<button <button
key={tab.id} key={tab.id}
onClick={() => setActiveTab(tab.id)} onClick={() => {
setActiveTab(tab.id);
setSearchParams({ tab: tab.id });
}}
className={`flex items-center gap-1.5 sm:gap-2 px-2.5 sm:px-4 py-3 text-xs sm:text-sm font-semibold whitespace-nowrap border-b-2 transition-colors ${isActive className={`flex items-center gap-1.5 sm:gap-2 px-2.5 sm:px-4 py-3 text-xs sm:text-sm font-semibold whitespace-nowrap border-b-2 transition-colors ${isActive
? 'border-amber-500 text-amber-600 dark:text-amber-400' ? 'border-amber-500 text-amber-600 dark:text-amber-400'
: 'border-transparent text-zinc-500 hover:text-zinc-900 dark:hover:text-white' : 'border-transparent text-zinc-500 hover:text-zinc-900 dark:hover:text-white'
+1 -1
View File
@@ -602,7 +602,7 @@ const OwnerSnapshot = () => {
<SubcontractorPaymentsPanel <SubcontractorPaymentsPanel
subcontractorTasks={subcontractorTasks} subcontractorTasks={subcontractorTasks}
subcontractors={subcontractors} subcontractors={subcontractors}
projects={projects} projects={ownerProjects}
/> />
</SpotlightCard> </SpotlightCard>
</section> </section>