feat(estimates): add EstimatesPage hub with templates tab, estimate detail drawer, and template editor
- EstimatesPage: tabbed hub with Estimates and Templates tabs, KPI strip, search + filter - EstimateDetailModal: right-side drawer showing scope of work, computed line items, and financial summary from template + roof area - TemplateEditorModal: create/edit/duplicate templates with segmented mode toggle - Owner-only template management (create, edit, duplicate, delete); Admin/FA read-only view - Duplicate mode: toggle between 'Save as new copy' (default, editable name) and 'Replace original' - MASTER_TEMPLATES and MOCK_ESTIMATES moved into mockStore with full CRUD helpers - TemplateSelectionModal now reads from mockStore instead of local stub - Sidebar nav updated to point to /estimates hub for all roles - Routes added: /emp/fa/estimates, /admin/estimates, /owner/estimates
This commit is contained in:
@@ -3197,6 +3197,426 @@ export const DISPATCH_WEEKLY_STATS = [
|
||||
},
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MASTER TEMPLATES
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const TEMPLATE_CATEGORIES = ['Roofing', 'Siding', 'Gutters', 'Windows', 'Insulation', 'Other'];
|
||||
|
||||
const MASTER_TEMPLATES_INITIAL = [
|
||||
{
|
||||
id: 'atlas_glassmaster',
|
||||
name: 'Atlas GlassMaster',
|
||||
category: 'Roofing',
|
||||
brand: 'Atlas',
|
||||
description: [
|
||||
"Remove existing shingles down to deck.",
|
||||
"Re-nail any loose wood. If bad or rotten wood is discovered, it will be replaced at a price of $50 per sheet.",
|
||||
"Install 3' of Atlas WeatherMaster ice and water shield at all gutter lines, rakes edges, and valleys.",
|
||||
"Install Atlas Summit 60 Synthetic underlayment.",
|
||||
"Install Atlas Glassmaster AR Shingles."
|
||||
],
|
||||
materials: [
|
||||
{ desc: 'Atlas Glassmaster AR', baseUnitCost: 37.67, uom: 'SQ' },
|
||||
{ desc: 'Atlas Pro-Cut Hip & Ridge', baseUnitCost: 78.50, uom: 'BD' },
|
||||
{ desc: 'Atlas 6X Pro-Cut Starter', baseUnitCost: 70.80, uom: 'BD' },
|
||||
{ desc: 'Atlas Summit 60 Synthetic Underlayment', baseUnitCost: 116.00, uom: 'RL' },
|
||||
{ desc: 'Atlas WeatherMaster Ice & Water', baseUnitCost: 65.00, uom: 'RL' },
|
||||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'atlas_glassmaster_srs',
|
||||
name: 'Atlas GlassMaster - SRS',
|
||||
category: 'Roofing',
|
||||
brand: 'SRS',
|
||||
description: [
|
||||
"Remove existing shingles down to deck.",
|
||||
"Re-nail any loose wood.",
|
||||
"Install 3' of Atlas WeatherMaster ice and water shield at all gutter lines, rakes edges, and valleys.",
|
||||
"Install Atlas Summit 60 Synthetic underlayment.",
|
||||
"Install Atlas Glassmaster AR Shingles (SRS distribution)."
|
||||
],
|
||||
materials: [
|
||||
{ desc: 'Atlas Glassmaster AR', baseUnitCost: 37.67, uom: 'SQ' },
|
||||
{ desc: 'Atlas Pro-Cut Hip & Ridge', baseUnitCost: 78.50, uom: 'BD' },
|
||||
{ desc: 'Atlas 6X Pro-Cut Starter', baseUnitCost: 70.80, uom: 'BD' },
|
||||
{ desc: 'Atlas Summit 60 Synthetic Underlayment', baseUnitCost: 116.00, uom: 'RL' },
|
||||
{ desc: 'Atlas WeatherMaster Ice & Water', baseUnitCost: 65.00, uom: 'RL' },
|
||||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'atlas_pinnacle_pristine',
|
||||
name: 'Atlas Pinnacle Pristine',
|
||||
category: 'Roofing',
|
||||
brand: 'Atlas',
|
||||
description: [
|
||||
"Remove existing shingles down to deck.",
|
||||
"Re-nail any loose wood.",
|
||||
"Install 3' of Atlas WeatherMaster ice and water shield.",
|
||||
"Install Atlas Summit 60 Synthetic underlayment.",
|
||||
"Install Atlas Pinnacle Pristine Lifetime Dimensional Shingles."
|
||||
],
|
||||
materials: [
|
||||
{ desc: 'Atlas Pinnacle Pristine', baseUnitCost: 42.50, uom: 'SQ' },
|
||||
{ desc: 'Atlas Pro-Cut High Profile Hip & Ridge', baseUnitCost: 85.00, uom: 'BD' },
|
||||
{ desc: 'Atlas Pro-Cut Starter', baseUnitCost: 72.00, uom: 'BD' },
|
||||
{ desc: 'Atlas Summit 60 Synthetic Underlayment', baseUnitCost: 116.00, uom: 'RL' },
|
||||
{ desc: 'Atlas WeatherMaster Ice & Water', baseUnitCost: 65.00, uom: 'RL' },
|
||||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'atlas_stormmaster_shake',
|
||||
name: 'Atlas StormMaster Shake',
|
||||
category: 'Roofing',
|
||||
brand: 'Atlas',
|
||||
description: [
|
||||
"Remove existing shingles down to deck.",
|
||||
"Re-nail any loose wood.",
|
||||
"Install 3' of Atlas WeatherMaster ice and water shield at all eaves and valleys.",
|
||||
"Install Atlas Summit 60 Synthetic underlayment.",
|
||||
"Install Atlas StormMaster Shake Impact Resistant Shingles."
|
||||
],
|
||||
materials: [
|
||||
{ desc: 'Atlas StormMaster Shake', baseUnitCost: 55.00, uom: 'SQ' },
|
||||
{ desc: 'Atlas Pro-Cut Hip & Ridge', baseUnitCost: 78.50, uom: 'BD' },
|
||||
{ desc: 'Atlas 6X Pro-Cut Starter', baseUnitCost: 70.80, uom: 'BD' },
|
||||
{ desc: 'Atlas Summit 60 Synthetic Underlayment', baseUnitCost: 116.00, uom: 'RL' },
|
||||
{ desc: 'Atlas WeatherMaster Ice & Water', baseUnitCost: 65.00, uom: 'RL' },
|
||||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'certainteed_landmark',
|
||||
name: 'CertainTeed Landmark AR',
|
||||
category: 'Roofing',
|
||||
brand: 'CertainTeed',
|
||||
description: [
|
||||
"Remove existing shingles down to deck.",
|
||||
"Re-nail any loose wood.",
|
||||
"Install CertainTeed WinterGuard ice and water shield.",
|
||||
"Install CertainTeed RoofRunner Synthetic underlayment.",
|
||||
"Install CertainTeed Landmark AR Shingles."
|
||||
],
|
||||
materials: [
|
||||
{ desc: 'CertainTeed Landmark AR', baseUnitCost: 44.00, uom: 'SQ' },
|
||||
{ desc: 'CertainTeed ShadowRidge', baseUnitCost: 82.00, uom: 'BD' },
|
||||
{ desc: 'CertainTeed SwiftStart', baseUnitCost: 75.00, uom: 'BD' },
|
||||
{ desc: 'CertainTeed RoofRunner', baseUnitCost: 125.00, uom: 'RL' },
|
||||
{ desc: 'CertainTeed WinterGuard', baseUnitCost: 70.00, uom: 'RL' },
|
||||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'certainteed_presidential_shake',
|
||||
name: 'CertainTeed Presidential Shake TL',
|
||||
category: 'Roofing',
|
||||
brand: 'CertainTeed',
|
||||
description: [
|
||||
"Remove existing shingles down to deck.",
|
||||
"Re-nail any loose wood.",
|
||||
"Install CertainTeed WinterGuard ice and water shield at all eaves and valleys.",
|
||||
"Install CertainTeed RoofRunner Synthetic underlayment.",
|
||||
"Install CertainTeed Presidential Shake TL triple-layer shingles."
|
||||
],
|
||||
materials: [
|
||||
{ desc: 'CertainTeed Presidential Shake TL', baseUnitCost: 72.00, uom: 'SQ' },
|
||||
{ desc: 'CertainTeed Presidential Ridge', baseUnitCost: 92.00, uom: 'BD' },
|
||||
{ desc: 'CertainTeed SwiftStart', baseUnitCost: 75.00, uom: 'BD' },
|
||||
{ desc: 'CertainTeed RoofRunner', baseUnitCost: 125.00, uom: 'RL' },
|
||||
{ desc: 'CertainTeed WinterGuard', baseUnitCost: 70.00, uom: 'RL' },
|
||||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'gaf_timberline_hdz',
|
||||
name: 'GAF Timberline HDZ',
|
||||
category: 'Roofing',
|
||||
brand: 'GAF',
|
||||
description: [
|
||||
"Remove existing shingles down to deck.",
|
||||
"Re-nail any loose wood.",
|
||||
"Install GAF WeatherWatch ice and water shield at eaves, rakes, and valleys.",
|
||||
"Install GAF FeltBuster Synthetic underlayment.",
|
||||
"Install GAF Timberline HDZ Laminated Architectural Shingles."
|
||||
],
|
||||
materials: [
|
||||
{ desc: 'GAF Timberline HDZ', baseUnitCost: 45.00, uom: 'SQ' },
|
||||
{ desc: 'GAF TimberTex Premium Ridge Cap', baseUnitCost: 85.00, uom: 'BD' },
|
||||
{ desc: 'GAF Pro-Start Starter Strip', baseUnitCost: 68.00, uom: 'BD' },
|
||||
{ desc: 'GAF FeltBuster Synthetic Underlayment', baseUnitCost: 110.00, uom: 'RL' },
|
||||
{ desc: 'GAF WeatherWatch Ice & Water', baseUnitCost: 68.00, uom: 'RL' },
|
||||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'gaf_timberline_ultra_hd',
|
||||
name: 'GAF Timberline Ultra HD',
|
||||
category: 'Roofing',
|
||||
brand: 'GAF',
|
||||
description: [
|
||||
"Remove existing shingles down to deck.",
|
||||
"Re-nail any loose wood.",
|
||||
"Install GAF WeatherWatch ice and water shield at eaves, rakes, and valleys.",
|
||||
"Install GAF FeltBuster Synthetic underlayment.",
|
||||
"Install GAF Timberline Ultra HD extra-thick dimensional shingles."
|
||||
],
|
||||
materials: [
|
||||
{ desc: 'GAF Timberline Ultra HD', baseUnitCost: 52.00, uom: 'SQ' },
|
||||
{ desc: 'GAF TimberTex Premium Ridge Cap', baseUnitCost: 85.00, uom: 'BD' },
|
||||
{ desc: 'GAF Pro-Start Starter Strip', baseUnitCost: 68.00, uom: 'BD' },
|
||||
{ desc: 'GAF FeltBuster Synthetic Underlayment', baseUnitCost: 110.00, uom: 'RL' },
|
||||
{ desc: 'GAF WeatherWatch Ice & Water', baseUnitCost: 68.00, uom: 'RL' },
|
||||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'iko_dynasty',
|
||||
name: 'IKO Dynasty',
|
||||
category: 'Roofing',
|
||||
brand: 'IKO',
|
||||
description: [
|
||||
"Remove existing shingles down to deck.",
|
||||
"Re-nail any loose wood.",
|
||||
"Install IKO ArmourGard ice and water shield at eaves, rakes, and valleys.",
|
||||
"Install IKO RoofGard-SB Synthetic underlayment.",
|
||||
"Install IKO Dynasty Impact Resistant shingles."
|
||||
],
|
||||
materials: [
|
||||
{ desc: 'IKO Dynasty', baseUnitCost: 54.00, uom: 'SQ' },
|
||||
{ desc: 'IKO Hip & Ridge 12', baseUnitCost: 80.00, uom: 'BD' },
|
||||
{ desc: 'IKO Leading Edge Plus Starter Strip', baseUnitCost: 71.00, uom: 'BD' },
|
||||
{ desc: 'IKO RoofGard-SB Synthetic Underlayment', baseUnitCost: 118.00, uom: 'RL' },
|
||||
{ desc: 'IKO ArmourGard Ice & Water', baseUnitCost: 67.00, uom: 'RL' },
|
||||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'malarkey_vista_ar',
|
||||
name: 'Malarkey Vista AR',
|
||||
category: 'Roofing',
|
||||
brand: 'Malarkey',
|
||||
description: [
|
||||
"Remove existing shingles down to deck.",
|
||||
"Re-nail any loose wood.",
|
||||
"Install Malarkey EverGuard ice and water shield at eaves and valleys.",
|
||||
"Install Malarkey UDL 40 Synthetic underlayment.",
|
||||
"Install Malarkey Vista AR Architectural Shingles."
|
||||
],
|
||||
materials: [
|
||||
{ desc: 'Malarkey Vista AR', baseUnitCost: 46.00, uom: 'SQ' },
|
||||
{ desc: 'Malarkey Ridglass Hip & Ridge', baseUnitCost: 83.00, uom: 'BD' },
|
||||
{ desc: 'Malarkey Windsor Starter Strip', baseUnitCost: 73.00, uom: 'BD' },
|
||||
{ desc: 'Malarkey UDL 40 Synthetic Underlayment', baseUnitCost: 122.00, uom: 'RL' },
|
||||
{ desc: 'Malarkey EverGuard Ice & Water', baseUnitCost: 69.00, uom: 'RL' },
|
||||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'owens_corning_duration',
|
||||
name: 'Owens Corning Duration',
|
||||
category: 'Roofing',
|
||||
brand: 'Owens Corning',
|
||||
description: [
|
||||
"Remove existing shingles down to deck.",
|
||||
"Inspect and re-nail decking.",
|
||||
"Install Owens Corning WeatherLock G ice and water shield.",
|
||||
"Install Owens Corning ProArmor Synthetic underlayment.",
|
||||
"Install Owens Corning TruDefinition Duration Shingles."
|
||||
],
|
||||
materials: [
|
||||
{ desc: 'OC TruDefinition Duration', baseUnitCost: 46.50, uom: 'SQ' },
|
||||
{ desc: 'OC ProEdge Hip & Ridge', baseUnitCost: 88.00, uom: 'BD' },
|
||||
{ desc: 'OC Starter Strip Plus', baseUnitCost: 78.00, uom: 'BD' },
|
||||
{ desc: 'OC ProArmor Underlayment', baseUnitCost: 130.00, uom: 'RL' },
|
||||
{ desc: 'OC WeatherLock G', baseUnitCost: 75.00, uom: 'RL' },
|
||||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'mulehide_epdm',
|
||||
name: 'Mulehide .060 EPDM System',
|
||||
category: 'Roofing',
|
||||
brand: 'Mulehide',
|
||||
description: [
|
||||
"Remove existing flat roof membrane down to deck.",
|
||||
"Inspect and repair decking as needed.",
|
||||
"Install Mulehide .060 EPDM membrane fully adhered system.",
|
||||
"Install Mulehide bonding adhesive.",
|
||||
"Install drip edge and termination bars at perimeter."
|
||||
],
|
||||
materials: [
|
||||
{ desc: 'Mulehide .060 EPDM Membrane', baseUnitCost: 85.00, uom: 'SQ' },
|
||||
{ desc: 'Mulehide Bonding Adhesive', baseUnitCost: 48.00, uom: 'EA' },
|
||||
{ desc: 'Mulehide EPDM Lap Sealant', baseUnitCost: 12.00, uom: 'EA' },
|
||||
{ desc: 'Mulehide Termination Bar', baseUnitCost: 4.50, uom: 'LF' },
|
||||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MOCK ESTIMATES
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const MOCK_ESTIMATES_INITIAL = [
|
||||
{
|
||||
id: 'est_001',
|
||||
clientName: 'Robert Chambers',
|
||||
address: '2612 Dunwick Dr, Plano, TX 75023',
|
||||
date: '2026-03-28',
|
||||
status: 'Approved',
|
||||
total: 18450.00,
|
||||
templateId: 'certainteed_landmark',
|
||||
templateName: 'CertainTeed Landmark AR',
|
||||
estimatedBy: 'Marcus Webb',
|
||||
roofArea: 28.4,
|
||||
notes: 'Hail damage from March 15 storm. Insurance claim approved.',
|
||||
},
|
||||
{
|
||||
id: 'est_002',
|
||||
clientName: 'Sandra Kim',
|
||||
address: '6613 Phoenix Pl, Plano, TX 75023',
|
||||
date: '2026-03-25',
|
||||
status: 'Sent',
|
||||
total: 22100.00,
|
||||
templateId: 'gaf_timberline_hdz',
|
||||
templateName: 'GAF Timberline HDZ',
|
||||
estimatedBy: 'Kenji Flores',
|
||||
roofArea: 32.1,
|
||||
notes: 'Full replacement. Customer wants upgraded ridge cap.',
|
||||
},
|
||||
{
|
||||
id: 'est_003',
|
||||
clientName: 'David Okonkwo',
|
||||
address: '3913 Arizona Pl, Plano, TX 75023',
|
||||
date: '2026-03-22',
|
||||
status: 'Draft',
|
||||
total: 14800.00,
|
||||
templateId: 'atlas_glassmaster',
|
||||
templateName: 'Atlas GlassMaster',
|
||||
estimatedBy: 'Marcus Webb',
|
||||
roofArea: 24.6,
|
||||
notes: null,
|
||||
},
|
||||
{
|
||||
id: 'est_004',
|
||||
clientName: 'Patricia Hollis',
|
||||
address: '3905 Sailmaker Ln, Plano, TX 75023',
|
||||
date: '2026-03-18',
|
||||
status: 'Approved',
|
||||
total: 31200.00,
|
||||
templateId: 'certainteed_presidential_shake',
|
||||
templateName: 'CertainTeed Presidential Shake TL',
|
||||
estimatedBy: 'Aisha Patel',
|
||||
roofArea: 38.7,
|
||||
notes: 'Premium upgrade. Customer requested Presidential Shake for curb appeal.',
|
||||
},
|
||||
{
|
||||
id: 'est_005',
|
||||
clientName: 'James Thornton',
|
||||
address: '6909 Custer Rd, Plano, TX 75023',
|
||||
date: '2026-03-14',
|
||||
status: 'Rejected',
|
||||
total: 28750.00,
|
||||
templateId: 'gaf_timberline_ultra_hd',
|
||||
templateName: 'GAF Timberline Ultra HD',
|
||||
estimatedBy: 'Kenji Flores',
|
||||
roofArea: 35.2,
|
||||
notes: 'Customer rejected — going with competitor. Follow up in 30 days.',
|
||||
},
|
||||
{
|
||||
id: 'est_006',
|
||||
clientName: 'Maria Gonzalez',
|
||||
address: '2608 Dunwick Dr, Plano, TX 75023',
|
||||
date: '2026-03-10',
|
||||
status: 'Approved',
|
||||
total: 16900.00,
|
||||
templateId: 'atlas_stormmaster_shake',
|
||||
templateName: 'Atlas StormMaster Shake',
|
||||
estimatedBy: 'Marcus Webb',
|
||||
roofArea: 26.8,
|
||||
notes: 'Impact resistant upgrade. HOA approved color: Weathered Wood.',
|
||||
},
|
||||
{
|
||||
id: 'est_007',
|
||||
clientName: 'Thomas Reeves',
|
||||
address: '7224 Independence Pkwy, Plano, TX 75025',
|
||||
date: '2026-03-05',
|
||||
status: 'Sent',
|
||||
total: 54200.00,
|
||||
templateId: 'mulehide_epdm',
|
||||
templateName: 'Mulehide .060 EPDM System',
|
||||
estimatedBy: 'Aisha Patel',
|
||||
roofArea: 62.0,
|
||||
notes: 'Commercial flat roof. 3 HVAC curb flashings included.',
|
||||
},
|
||||
{
|
||||
id: 'est_008',
|
||||
clientName: 'Denise Watkins',
|
||||
address: '3901 Sailmaker Ln, Plano, TX 75023',
|
||||
date: '2026-02-28',
|
||||
status: 'Expired',
|
||||
total: 19350.00,
|
||||
templateId: 'iko_dynasty',
|
||||
templateName: 'IKO Dynasty',
|
||||
estimatedBy: 'Kenji Flores',
|
||||
roofArea: 29.1,
|
||||
notes: 'Estimate expired — no response after 3 follow-ups. Reactivate on request.',
|
||||
},
|
||||
{
|
||||
id: 'est_009',
|
||||
clientName: 'Calvin Monroe',
|
||||
address: '2100 Legacy Dr, Plano, TX 75023',
|
||||
date: '2026-02-20',
|
||||
status: 'Approved',
|
||||
total: 11600.00,
|
||||
templateId: 'owens_corning_duration',
|
||||
templateName: 'Owens Corning Duration',
|
||||
estimatedBy: 'Marcus Webb',
|
||||
roofArea: 18.9,
|
||||
notes: 'Apartment unit — common area roof section only.',
|
||||
},
|
||||
{
|
||||
id: 'est_010',
|
||||
clientName: 'Brenda Castillo',
|
||||
address: '6601 Phoenix Pl, Plano, TX 75023',
|
||||
date: '2026-02-14',
|
||||
status: 'Draft',
|
||||
total: 17250.00,
|
||||
templateId: 'malarkey_vista_ar',
|
||||
templateName: 'Malarkey Vista AR',
|
||||
estimatedBy: 'Aisha Patel',
|
||||
roofArea: 27.3,
|
||||
notes: null,
|
||||
},
|
||||
];
|
||||
|
||||
// --- CONTEXT SETUP ---
|
||||
|
||||
const MockStoreContext = createContext();
|
||||
@@ -3217,6 +3637,8 @@ export const MockStoreProvider = ({ children }) => {
|
||||
const [vendorInvoices, setVendorInvoices] = useState(MOCK_VENDOR_INVOICES);
|
||||
const [leads, setLeads] = useState([]);
|
||||
const [dispatchLeads, setDispatchLeads] = useState(DISPATCH_LEADS_INITIAL);
|
||||
const [templates, setTemplates] = useState(MASTER_TEMPLATES_INITIAL);
|
||||
const [estimates, setEstimates] = useState(MOCK_ESTIMATES_INITIAL);
|
||||
|
||||
// Initialize properties once
|
||||
useEffect(() => {
|
||||
@@ -3317,6 +3739,31 @@ export const MockStoreProvider = ({ children }) => {
|
||||
return newLead;
|
||||
},
|
||||
|
||||
// Templates
|
||||
templates,
|
||||
addTemplate: (tpl) => {
|
||||
const newTpl = { ...tpl, id: `tpl_${Date.now()}` };
|
||||
setTemplates(prev => [...prev, newTpl]);
|
||||
return newTpl;
|
||||
},
|
||||
updateTemplate: (id, data) => {
|
||||
setTemplates(prev => prev.map(t => t.id === id ? { ...t, ...data } : t));
|
||||
},
|
||||
deleteTemplate: (id) => {
|
||||
setTemplates(prev => prev.filter(t => t.id !== id));
|
||||
},
|
||||
|
||||
// Estimates
|
||||
estimates,
|
||||
addEstimate: (est) => {
|
||||
const newEst = { ...est, id: `est_${Date.now()}`, date: new Date().toISOString().slice(0, 10) };
|
||||
setEstimates(prev => [newEst, ...prev]);
|
||||
return newEst;
|
||||
},
|
||||
updateEstimate: (id, data) => {
|
||||
setEstimates(prev => prev.map(e => e.id === id ? { ...e, ...data } : e));
|
||||
},
|
||||
|
||||
// LynkDispatch
|
||||
dispatchLeads,
|
||||
assignDispatchLead: (leadId, repId) => {
|
||||
|
||||
Reference in New Issue
Block a user