From f65d1555d2beaca32adaeb0e74b2ce6dc0228cbd Mon Sep 17 00:00:00 2001 From: Todd Date: Mon, 25 May 2026 22:11:44 -0400 Subject: [PATCH] Add customer info step (name + project) between access control and quote --- client/src/components/QuoteSummary.jsx | 11 +++ client/src/components/StepCustomerInfo.jsx | 81 ++++++++++++++++++++++ client/src/components/Wizard.jsx | 24 +++++-- 3 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 client/src/components/StepCustomerInfo.jsx diff --git a/client/src/components/QuoteSummary.jsx b/client/src/components/QuoteSummary.jsx index c67b76f..8484d42 100644 --- a/client/src/components/QuoteSummary.jsx +++ b/client/src/components/QuoteSummary.jsx @@ -4,6 +4,7 @@ import { calculateQuote } from '../utils/quoteCalculator'; export default function QuoteSummary({ pricing, selections, onBack, onNew }) { const quoteRef = useRef(null); const quote = calculateQuote(pricing, selections); + const { customerInfo } = selections; const operator = selections.operator && typeof selections.operator === 'object' ? selections.operator @@ -78,6 +79,16 @@ export default function QuoteSummary({ pricing, selections, onBack, onNew }) {

Quotation Date: {new Date().toLocaleDateString('en-CA')}

+ {customerInfo?.name && ( +

+ {customerInfo.name} +

+ )} + {customerInfo?.project && ( +

+ {customerInfo.project} +

+ )}
diff --git a/client/src/components/StepCustomerInfo.jsx b/client/src/components/StepCustomerInfo.jsx new file mode 100644 index 0000000..f6d9e9e --- /dev/null +++ b/client/src/components/StepCustomerInfo.jsx @@ -0,0 +1,81 @@ +import { useState, useEffect } from 'react'; + +export default function StepCustomerInfo({ value, onChange, onBack, onNext }) { + const [name, setName] = useState(value.name || ''); + const [project, setProject] = useState(value.project || ''); + + useEffect(() => { + setName(value.name || ''); + setProject(value.project || ''); + }, [value.name, value.project]); + + const handleContinue = () => { + onChange({ name: name.trim(), project: project.trim() }); + onNext(); + }; + + return ( +
+
+ +
+

+ Step 4: Customer Information +

+

+ Enter the customer and project details for this quote +

+
+
+ +
+
+ + setName(e.target.value)} + placeholder="e.g. John Smith or ACME Corp" + className="w-full px-4 py-2.5 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all placeholder-gray-400" + /> +
+ +
+ + setProject(e.target.value)} + placeholder="e.g. Main Street Office Park" + className="w-full px-4 py-2.5 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all placeholder-gray-400" + /> +
+
+ +
+
+ {name || project ? 'Information entered' : 'Optional — you can leave these blank'} +
+ +
+
+ ); +} diff --git a/client/src/components/Wizard.jsx b/client/src/components/Wizard.jsx index d5a942b..bb9a112 100644 --- a/client/src/components/Wizard.jsx +++ b/client/src/components/Wizard.jsx @@ -2,6 +2,7 @@ import { useReducer } from 'react'; import StepOperator from './StepOperator'; import StepGroundLoops from './StepGroundLoops'; import StepAccessControl from './StepAccessControl'; +import StepCustomerInfo from './StepCustomerInfo'; import QuoteSummary from './QuoteSummary'; const initialState = { @@ -18,6 +19,7 @@ const initialState = { accessControl: [], remoteButtons: 4, remoteQuantity: 1, + customerInfo: { name: '', project: '' }, }; function reducer(state, action) { @@ -82,8 +84,10 @@ function reducer(state, action) { return { ...state, remoteButtons: action.payload }; case 'SET_REMOTE_QUANTITY': return { ...state, remoteQuantity: Math.max(1, action.payload) }; + case 'SET_CUSTOMER_INFO': + return { ...state, customerInfo: action.payload }; case 'NEXT_STEP': - return { ...state, step: Math.min(state.step + 1, 4) }; + return { ...state, step: Math.min(state.step + 1, 5) }; case 'PREV_STEP': return { ...state, step: Math.max(state.step - 1, 1) }; case 'SET_GROUND_LOOP_SIZE': @@ -98,7 +102,7 @@ function reducer(state, action) { }, }; case 'GO_TO_STEP': - return { ...state, step: Math.min(action.payload, 4) }; + return { ...state, step: Math.min(action.payload, 5) }; case 'RESET': return { ...initialState }; default: @@ -108,13 +112,14 @@ function reducer(state, action) { export default function Wizard({ pricing, onLogout }) { const [state, dispatch] = useReducer(reducer, initialState); - const { step, operator, armChoice, optionalParts, groundLoops, accessControl, remoteButtons, remoteQuantity } = state; + const { step, operator, armChoice, optionalParts, groundLoops, accessControl, remoteButtons, remoteQuantity, customerInfo } = state; const steps = [ { num: 1, label: 'Operator' }, { num: 2, label: 'Ground Loops' }, { num: 3, label: 'Access Control' }, - { num: 4, label: 'Quote' }, + { num: 4, label: 'Customer' }, + { num: 5, label: 'Quote' }, ]; return ( @@ -226,9 +231,18 @@ export default function Wizard({ pricing, onLogout }) { )} {step === 4 && ( + dispatch({ type: 'SET_CUSTOMER_INFO', payload: info })} + onBack={() => dispatch({ type: 'PREV_STEP' })} + onNext={() => dispatch({ type: 'NEXT_STEP' })} + /> + )} + + {step === 5 && ( dispatch({ type: 'PREV_STEP' })} onNew={() => dispatch({ type: 'RESET' })} />