import React, { useState } from 'react'; import { useCalculatorStore } from '../../../store/calculatorStore'; type DeadLoadItem = { category: string; description: string; multiplier: number; psfMax: number; psfMin: number; }; // Mock Database of Items const ITEM_DB: Record = { Decking: [ { category: 'Decking', description: 'Metal Roof deck, 1.5, 22 ga.', multiplier: 1, psfMax: 1.7, psfMin: 1.2 }, { category: 'Decking', description: 'Wood decking 2x6', multiplier: 1, psfMax: 2.5, psfMin: 2.0 }, { category: 'Decking', description: 'Steel roof deck 3 in.', multiplier: 1, psfMax: 2.8, psfMin: 2.2 }, ], Framing: [ { category: 'Framing', description: 'Steel roof joists & girders', multiplier: 1, psfMax: 3.0, psfMin: 2.0 }, { category: 'Framing', description: 'Wood Rafters 2x10', multiplier: 1, psfMax: 4.0, psfMin: 3.0 }, { category: 'Framing', description: 'Light gauge steel trusses', multiplier: 1, psfMax: 3.5, psfMin: 2.5 }, ], Insulation: [ { category: 'Insulation', description: 'Rigid insulation, per 1"', multiplier: 1, psfMax: 1.5, psfMin: 0.7 }, { category: 'Insulation', description: 'Batt insulation', multiplier: 1, psfMax: 0.5, psfMin: 0.1 }, ], Ceiling: [ { category: 'Ceiling', description: '1/2" gypsum board', multiplier: 1, psfMax: 2.2, psfMin: 2.0 }, { category: 'Ceiling', description: '5/8" gypsum board', multiplier: 1, psfMax: 2.8, psfMin: 2.4 }, { category: 'Ceiling', description: 'Suspended acoustic tile', multiplier: 1, psfMax: 1.5, psfMin: 1.0 }, ], 'Mech & Elec': [ { category: 'Mech & Elec', description: 'Mech. & Elec.', multiplier: 1, psfMax: 2.0, psfMin: 0.0 }, // per unit? logic check needed if psf ], Misc: [ { category: 'Misc', description: 'Misc.', multiplier: 1, psfMax: 0.5, psfMin: 0.0 }, { category: 'Misc', description: 'Sprinklers (wet)', multiplier: 1, psfMax: 1.5, psfMin: 0.5 }, ] }; export const RoofDesignLoadsSheet: React.FC = () => { const { buildingGeometry } = useCalculatorStore(); // --- State --- const [selectedItems, setSelectedItems] = useState<{ [key: string]: DeadLoadItem | null }>({ Decking: ITEM_DB.Decking[0], Framing: ITEM_DB.Framing[0], Insulation: ITEM_DB.Insulation[0], Ceiling: ITEM_DB.Ceiling[0], 'Mech & Elec': ITEM_DB['Mech & Elec'][0], Misc: ITEM_DB.Misc[0], }); const [multipliers] = useState<{ [key: string]: number }>({ Insulation: 5.0, 'Mech & Elec': 3.0, Misc: 4.0, }); const [overrideDL, setOverrideDL] = useState({ enabled: true, value: 25.0, valueMin: 9.0 }); const [liveLoadArea, setLiveLoadArea] = useState(450); const [windOverride, setWindOverride] = useState({ pos: 16.0, neg: -43.8 }); // --- Calculations --- // Dead Load let totalDL_Max = 0; let totalDL_Min = 0; const rows = ['Decking', 'Framing', 'Insulation', 'Ceiling', 'Mech & Elec', 'Misc']; rows.forEach(key => { const item = selectedItems[key]; const mult = multipliers[key] || 1; if (item) { totalDL_Max += item.psfMax * mult; totalDL_Min += item.psfMin * mult; } }); const finalDL = overrideDL.enabled ? overrideDL.value : totalDL_Max; const finalDL_Min = overrideDL.enabled ? overrideDL.valueMin : totalDL_Min; // Live Load Reduction let Lr = 20.0; if (liveLoadArea <= 200) Lr = 20.0; else if (liveLoadArea <= 600) Lr = Math.max(12, 24 - 0.02 * liveLoadArea); else Lr = 12.0; // Snow Load const S = 21.0; // Wind Load const W_pos = windOverride.pos; const W_neg = windOverride.neg; // Combinations // ASD const asd_DL_Lr = finalDL + Lr; const asd_2 = finalDL + 0.75 * (0.6 * W_pos + Lr); const asd_3 = 0.6 * finalDL_Min + 0.6 * W_neg; // LRFD const lrfd_1 = 1.2 * finalDL + 1.6 * S + 0.5 * W_pos; const lrfd_2 = 1.2 * finalDL + 1.0 * W_pos + 0.5 * Lr; const lrfd_3 = 0.9 * finalDL_Min + 1.0 * W_neg; // Handler for dropdown changes const handleItemChange = (key: string, e: React.ChangeEvent) => { const selectedDescription = e.target.value; const selectedItem = ITEM_DB[key].find(item => item.description === selectedDescription); if (selectedItem) { setSelectedItems(prev => ({ ...prev, [key]: selectedItem })); } }; return (

Roof Design Loads

{/* Left Column: Calculator & Loads */}
{/* Dead Load Table */} {rows.map(key => { const item = selectedItems[key]; return ( ) })}
Items Description Multiple psf (max) psf (min)
{item?.description} {multipliers[key] ? (
x {multipliers[key].toFixed(1)}
) : ''}
{(item!.psfMax * (multipliers[key] || 1)).toFixed(1)} {(item!.psfMin * (multipliers[key] || 1)).toFixed(1)}
Actual Dead Load {totalDL_Max.toFixed(1)} {totalDL_Min.toFixed(1)}
setOverrideDL({ ...overrideDL, enabled: true })} /> setOverrideDL({ ...overrideDL, value: parseFloat(e.target.value) })} style={{ width: '50px', color: 'red', fontWeight: 'bold' }} /> setOverrideDL({ ...overrideDL, valueMin: parseFloat(e.target.value) })} style={{ width: '50px', color: 'red', fontWeight: 'bold' }} />
{/* Load Summary */} {/* ASD */} {/* LRFD */}
Live Load {Lr.toFixed(1)} 0.0
Snow Load {S.toFixed(1)} 0.0
Ultimate Wind (zone 2 - 100 sf) {W_pos.toFixed(1)} {W_neg.toFixed(1)}
ASD Loading D + Lr    {asd_DL_Lr.toFixed(1)} -
D + 0.75(0.6W + Lr)    {asd_2.toFixed(1)} -
0.6D + 0.6W    - {asd_3.toFixed(1)}
LRFD Loading 1.2D + 1.6S + 0.5W    {lrfd_1.toFixed(1)} -
1.2D + 1.0W + 0.5Lr    {lrfd_2.toFixed(1)} -
0.9D + 1.0W    - {lrfd_3.toFixed(1)}
{/* Live Load Reduction Section */}
Roof Live Load Reduction Roof angle {buildingGeometry.roofSlope} 0.4 deg
0 to 200 sf: 20.0 psf
200 to 600 sf: 24 - 0.02Area, but not less than 12 psf
over 600 sf: 12.0 psf
setLiveLoadArea(parseFloat(e.target.value))} style={{ width: '60px', color: 'red' }} />
{Lr.toFixed(1)} psf
{/* Right Column: Override */}
Edit default weights
Override roof wind
Positive
negative
setWindOverride({ ...windOverride, pos: parseFloat(e.target.value) })} style={{ width: '100%' }} /> setWindOverride({ ...windOverride, neg: parseFloat(e.target.value) })} style={{ width: '100%' }} />
The wind load shown in the table at left is to provide an idea as to whether it may control the design of roof members. The user needs to evaluate the wind zone and square footage for each roof component and can override the calculated value by entering a value to the right.
); };