Files
LynkedUpPro_CRM/src/components/estimates/TemplateSelectionModal.jsx
T
Satyam-Rastogi 8f9cb9dd48 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
2026-04-01 16:13:36 +05:30

672 lines
35 KiB
React

import React, { useState } from 'react';
import { useMockStore } from '../../data/mockStore';
const STUBBED_TEMPLATES = [
{
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_pinnacle_pristine_srs',
name: 'Atlas Pinnacle Pristine - 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.",
"Install Atlas Summit 60 Synthetic underlayment.",
"Install Atlas Pinnacle Pristine Lifetime Dimensional Shingles (SRS distribution)."
],
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_landmark_srs',
name: 'CertainTeed Landmark AR - SRS',
category: 'Roofing',
brand: 'SRS',
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 (SRS distribution)."
],
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_royal_sovereign',
name: 'GAF Royal Sovereign',
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 and valleys.",
"Install GAF FeltBuster Synthetic underlayment.",
"Install GAF Royal Sovereign 3-tab shingles."
],
materials: [
{ desc: 'GAF Royal Sovereign', baseUnitCost: 32.00, uom: 'SQ' },
{ desc: 'GAF Seal-A-Ridge', baseUnitCost: 72.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_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: 'gaf_timberline_hdz_srs',
name: 'GAF Timberline HDZ - SRS',
category: 'Roofing',
brand: 'SRS',
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 (SRS distribution)."
],
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: 'iko_armourshake',
name: 'IKO Armourshake',
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 and valleys.",
"Install IKO RoofGard-SB Synthetic underlayment.",
"Install IKO Armourshake heavyweight laminated shingles."
],
materials: [
{ desc: 'IKO Armourshake', baseUnitCost: 58.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: 'iko_armourshake_srs',
name: 'IKO Armourshake - SRS',
category: 'Roofing',
brand: 'SRS',
description: [
"Remove existing shingles down to deck.",
"Re-nail any loose wood.",
"Install IKO ArmourGard ice and water shield at eaves and valleys.",
"Install IKO RoofGard-SB Synthetic underlayment.",
"Install IKO Armourshake heavyweight laminated shingles (SRS distribution)."
],
materials: [
{ desc: 'IKO Armourshake', baseUnitCost: 58.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: 'iko_cambridge',
name: 'IKO Cambridge',
category: 'Roofing',
brand: 'IKO',
description: [
"Remove existing shingles down to deck.",
"Re-nail any loose wood.",
"Install IKO ArmourGard ice and water shield.",
"Install IKO RoofGard-SB Synthetic underlayment.",
"Install IKO Cambridge Architectural Shingles."
],
materials: [
{ desc: 'IKO Cambridge', baseUnitCost: 42.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: '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: 'iko_dynasty_srs',
name: 'IKO Dynasty - SRS',
category: 'Roofing',
brand: 'SRS',
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 (SRS distribution)."
],
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: 'iko_nordic_per_square',
name: 'IKO Nordic Per Square',
category: 'Roofing',
brand: 'IKO',
description: [
"Remove existing shingles down to deck.",
"Re-nail any loose wood.",
"Install IKO ArmourGard ice and water shield.",
"Install IKO RoofGard-SB Synthetic underlayment.",
"Install IKO Nordic Architectural Shingles (priced per square)."
],
materials: [
{ desc: 'IKO Nordic', baseUnitCost: 40.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: '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' }
]
},
{
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: 'owens_corning_duration_srs',
name: 'Owens Corning Duration - SRS',
category: 'Roofing',
brand: 'SRS',
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 (SRS distribution)."
],
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: 'owens_corning_oakridge',
name: 'Owens Corning Oakridge',
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 Oakridge Architectural Shingles."
],
materials: [
{ desc: 'OC Oakridge', baseUnitCost: 38.00, 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' }
]
}
];
export default function TemplateSelectionModal({ isOpen, onClose, onSelectTemplate }) {
const { templates } = useMockStore();
const [searchTerm, setSearchTerm] = useState('');
const [selected, setSelected] = useState(null);
const filtered = templates.filter(t =>
t.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
t.category.toLowerCase().includes(searchTerm.toLowerCase())
);
const handleNext = () => {
if (selected) {
onSelectTemplate(selected);
}
};
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm">
<div
className="bg-white dark:bg-zinc-900 w-full max-w-3xl rounded-2xl shadow-xl border border-zinc-200 dark:border-white/10 overflow-hidden flex flex-col max-h-[90vh]"
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-zinc-200 dark:border-white/10 shrink-0">
<div>
<h2 className="text-2xl font-display text-zinc-900 dark:text-white font-light tracking-tight">Select a Template</h2>
<p className="text-sm text-zinc-500 dark:text-zinc-400 mt-1">Choose a base template to calculate your item quantities.</p>
</div>
</div>
{/* Search Bar */}
<div className="px-6 py-4 border-b border-zinc-200 dark:border-white/5 bg-zinc-50 dark:bg-zinc-900/50 shrink-0">
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search for a Template..."
className="w-full bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg px-4 py-2.5 text-sm outline-none focus:ring-2 focus:ring-blue-500 transition-shadow text-zinc-800 dark:text-zinc-200"
/>
</div>
{/* Templates List */}
<div className="p-6 overflow-y-auto custom-scrollbar flex-1 bg-zinc-50/50 dark:bg-zinc-900/20">
<div className="bg-white dark:bg-zinc-800/50 border border-zinc-200 dark:border-white/10 rounded-xl overflow-hidden">
{/* Category Header */}
<div className="px-4 py-3 bg-blue-50/50 dark:bg-blue-900/10 border-b border-zinc-200 dark:border-white/10">
<h3 className="text-sm font-semibold text-blue-600 dark:text-blue-400">
Templates ({filtered.length})
</h3>
</div>
{/* List items */}
<div className="divide-y divide-zinc-200 dark:divide-white/10 text-left">
{filtered.map((template) => {
const isSelected = selected?.id === template.id;
return (
<button
key={template.id}
onClick={() => setSelected(template)}
className={`w-full flex flex-col md:flex-row items-start md:items-center p-4 transition-colors text-left focus:outline-none ${isSelected
? 'bg-blue-50 dark:bg-blue-500/10 border-l-4 border-l-blue-500'
: 'hover:bg-blue-50/30 dark:hover:bg-blue-500/5 focus:bg-blue-50/50 dark:focus:bg-blue-500/10'
}`}
>
{/* Content */}
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
{/* Radio circle visual */}
<div className={`w-5 h-5 rounded-full border-2 flex items-center justify-center shrink-0 transition-colors ${isSelected ? 'border-orange-500' : 'border-orange-300 hover:border-blue-400'}`}>
{isSelected && (
<div className="w-2.5 h-2.5 rounded-full bg-orange-500"></div>
)}
</div>
<h4 className={`text-base font-medium transition-colors ${isSelected ? 'text-blue-700 dark:text-blue-300' : 'text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300'}`}>
{template.name}
</h4>
</div>
<ul className="pl-8 space-y-0.5">
{template.description.slice(0, 3).map((descLine, i) => (
<li key={i} className="text-sm text-zinc-500 dark:text-zinc-400 truncate max-w-xl flex items-start gap-2">
<span className="text-zinc-300 dark:text-zinc-600 mt-1 shrink-0">-</span>
<span className="truncate">{descLine}</span>
</li>
))}
{template.description.length > 3 && (
<li className="text-sm text-zinc-400 dark:text-zinc-500 pl-4">...</li>
)}
</ul>
</div>
{/* Action Column */}
<div className="flex flex-col items-center justify-center ml-auto mt-4 md:mt-0 pl-8 md:pl-4 shrink-0 gap-2">
<span className="text-sm font-medium text-blue-500 hover:text-blue-600 hover:underline cursor-pointer">Preview</span>
<div className="text-xs font-bold text-zinc-500 dark:text-zinc-400 bg-zinc-100 dark:bg-zinc-700 px-2 py-0.5 rounded">
{template.brand}
</div>
</div>
</button>
);
})}
{filtered.length === 0 && (
<div className="px-4 py-8 text-center text-zinc-400 text-sm">
No templates match your search.
</div>
)}
</div>
</div>
</div>
{/* Footer */}
<div className="flex items-center gap-4 p-4 border-t border-zinc-200 dark:border-white/10 shrink-0 bg-white dark:bg-zinc-900 justify-start">
<button
onClick={onClose}
className="px-6 py-2.5 text-red-500 font-medium hover:bg-red-50 dark:hover:bg-red-500/10 rounded-lg transition-colors text-sm"
>
CANCEL
</button>
<button
onClick={handleNext}
disabled={!selected}
className={`ml-auto px-8 py-2.5 rounded-lg font-medium text-sm transition-colors ${selected
? 'bg-blue-500 hover:bg-blue-600 text-white shadow-sm cursor-pointer'
: 'bg-blue-100 dark:bg-blue-900/30 text-blue-300 dark:text-blue-500 cursor-not-allowed opacity-60'
}`}
>
NEXT
</button>
</div>
</div>
</div>
);
}