From c629890e32ade8f403b25ab5e79d560675f90c49 Mon Sep 17 00:00:00 2001 From: azcomputerguru Date: Tue, 10 Mar 2026 20:42:40 -0700 Subject: [PATCH] fix: Quote wizard - correct total calculation and email sender - Fix calculateQuote() to respect serviceInterests flags - Only include GPS/Support costs when user has enabled them - Update Step6Summary to conditionally render service sections - Add sender display name (Arizona Computer Guru) to emails - Add reply-to address (admin@azcomputerguru.com) - Fixes phantom $380 support charge appearing in totals Co-Authored-By: Claude Opus 4.5 --- .../components/wizard/steps/Step6Summary.tsx | 108 ++++++++++-------- .../frontend/src/hooks/useQuote.ts | 50 ++++---- .../quote-wizard/php-api/api/config.php | 3 + .../php-api/api/services/email_service.php | 14 +++ 4 files changed, 104 insertions(+), 71 deletions(-) diff --git a/projects/msp-tools/quote-wizard/frontend/src/components/wizard/steps/Step6Summary.tsx b/projects/msp-tools/quote-wizard/frontend/src/components/wizard/steps/Step6Summary.tsx index 17369af..403d7ee 100644 --- a/projects/msp-tools/quote-wizard/frontend/src/components/wizard/steps/Step6Summary.tsx +++ b/projects/msp-tools/quote-wizard/frontend/src/components/wizard/steps/Step6Summary.tsx @@ -88,54 +88,58 @@ export function Step6Summary({ )} - {/* GPS Monitoring Section */} - } - title="GPS Monitoring" - monthlyTotal={result.gpsMonthly} - onEdit={() => onGoToStep(1)} - > -
- - {quoteData.gps.includeEquipment && quoteData.gps.equipmentDeviceCount > 0 && ( + {/* GPS Monitoring Section - only show if enabled */} + {quoteData.serviceInterests.gps && ( + } + title="GPS Monitoring" + monthlyTotal={result.gpsMonthly} + onEdit={() => onGoToStep(1)} + > +
- )} -
-
+ {quoteData.gps.includeEquipment && quoteData.gps.equipmentDeviceCount > 0 && ( + + )} +
+
+ )} - {/* Support Plan Section */} - } - title="Support Plan" - monthlyTotal={result.supportMonthly} - onEdit={() => onGoToStep(2)} - > -
- {quoteData.support.planId === 'none' ? ( - - ) : ( - - )} - {blockTime && ( - - )} -
-
+ {/* Support Plan Section - only show if enabled */} + {quoteData.serviceInterests.support && ( + } + title="Support Plan" + monthlyTotal={result.supportMonthly} + onEdit={() => onGoToStep(2)} + > +
+ {quoteData.support.planId === 'none' ? ( + + ) : ( + + )} + {blockTime && ( + + )} +
+
+ )} {/* VoIP Section */} {quoteData.voip.enabled && ( @@ -230,15 +234,19 @@ export function Step6Summary({ Monthly Breakdown
- - - {quoteData.voip.enabled && ( + {quoteData.serviceInterests.gps && ( + + )} + {quoteData.serviceInterests.support && ( + + )} + {quoteData.serviceInterests.voip && quoteData.voip.enabled && ( )} - {quoteData.webHosting.enabled && ( + {quoteData.serviceInterests.webHosting && quoteData.webHosting.enabled && ( )} - {quoteData.email.enabled && ( + {quoteData.serviceInterests.email && quoteData.email.enabled && ( )}
diff --git a/projects/msp-tools/quote-wizard/frontend/src/hooks/useQuote.ts b/projects/msp-tools/quote-wizard/frontend/src/hooks/useQuote.ts index 4df2364..c2de951 100644 --- a/projects/msp-tools/quote-wizard/frontend/src/hooks/useQuote.ts +++ b/projects/msp-tools/quote-wizard/frontend/src/hooks/useQuote.ts @@ -568,32 +568,40 @@ export function useQuote(): UseQuoteReturn { }, [email]); const calculateQuote = useCallback((): QuoteResult => { - const gpsMonthly = getGPSMonthly(); - const supportMonthly = getSupportMonthly(); - const voipMonthly = getVoIPMonthly(); - const voipOneTime = getVoIPOneTime(); - const supportBlockTimeOneTime = getSupportBlockTimeOneTime(); - const webHostingMonthly = getWebHostingMonthly(); - const emailMonthly = getEmailMonthly(); + // Only include services that are enabled in serviceInterests + const gpsMonthly = serviceInterests.gps ? getGPSMonthly() : 0; + const supportMonthly = serviceInterests.support ? getSupportMonthly() : 0; + const supportBlockTimeOneTime = serviceInterests.support ? getSupportBlockTimeOneTime() : 0; + const voipMonthly = serviceInterests.voip ? getVoIPMonthly() : 0; + const voipOneTime = serviceInterests.voip ? getVoIPOneTime() : 0; + const webHostingMonthly = serviceInterests.webHosting ? getWebHostingMonthly() : 0; + const emailMonthly = serviceInterests.email ? getEmailMonthly() : 0; - // Calculate GPS breakdown - const gpsTier = gpsTiers.find((t) => t.id === gps.tierId); - const gpsMonitoring = gpsTier ? gpsTier.pricePerEndpoint * gps.endpointCount : 0; + // Calculate GPS breakdown (only if enabled) + let gpsMonitoring = 0; let gpsEquipment = 0; - if (gps.includeEquipment && gps.equipmentDeviceCount > 0) { - const additionalDevices = Math.max(0, gps.equipmentDeviceCount - equipmentMonitoring.baseDevices); - gpsEquipment = equipmentMonitoring.basePrice + (additionalDevices * equipmentMonitoring.additionalDevicePrice); + if (serviceInterests.gps) { + const gpsTier = gpsTiers.find((t) => t.id === gps.tierId); + gpsMonitoring = gpsTier ? gpsTier.pricePerEndpoint * gps.endpointCount : 0; + if (gps.includeEquipment && gps.equipmentDeviceCount > 0) { + const additionalDevices = Math.max(0, gps.equipmentDeviceCount - equipmentMonitoring.baseDevices); + gpsEquipment = equipmentMonitoring.basePrice + (additionalDevices * equipmentMonitoring.additionalDevicePrice); + } } - // Calculate support breakdown - const supportPlan = support.planId !== 'none' ? supportPlans.find((p) => p.id === support.planId) : null; - const supportPlanCost = supportPlan ? supportPlan.monthlyPrice : 0; + // Calculate support breakdown (only if enabled) + let supportPlanCost = 0; + if (serviceInterests.support && support.planId !== 'none') { + const supportPlan = supportPlans.find((p) => p.id === support.planId); + supportPlanCost = supportPlan ? supportPlan.monthlyPrice : 0; + } - // Calculate VoIP breakdown - const voipTier = voipTiers.find((t) => t.id === voip.tierId); - const voipService = voip.enabled && voipTier ? voipTier.pricePerUser * voip.userCount : 0; + // Calculate VoIP breakdown (only if enabled) + let voipService = 0; let voipHardwareMonthly = 0; - if (voip.enabled) { + if (serviceInterests.voip && voip.enabled) { + const voipTier = voipTiers.find((t) => t.id === voip.tierId); + voipService = voipTier ? voipTier.pricePerUser * voip.userCount : 0; voip.hardware.forEach((hw) => { if (hw.isRental) { const hardware = voipHardware.find((h) => h.id === hw.hardwareId); @@ -639,7 +647,7 @@ export function useQuote(): UseQuoteReturn { setQuoteResult(result); return result; - }, [gps, support, voip, webHosting, email, getGPSMonthly, getSupportMonthly, getSupportBlockTimeOneTime, getVoIPMonthly, getVoIPOneTime, getWebHostingMonthly, getEmailMonthly]); + }, [serviceInterests, gps, support, voip, webHosting, email, getGPSMonthly, getSupportMonthly, getSupportBlockTimeOneTime, getVoIPMonthly, getVoIPOneTime, getWebHostingMonthly, getEmailMonthly]); // ============================================================================ // Reset diff --git a/projects/msp-tools/quote-wizard/php-api/api/config.php b/projects/msp-tools/quote-wizard/php-api/api/config.php index c33e7ca..9bce0c1 100644 --- a/projects/msp-tools/quote-wizard/php-api/api/config.php +++ b/projects/msp-tools/quote-wizard/php-api/api/config.php @@ -29,6 +29,9 @@ define('GRAPH_TENANT_ID', 'ce61461e-81a0-4c84-bb4a-7b354a9a356d'); define('GRAPH_CLIENT_ID', '15b0fafb-ab51-4cc9-adc7-f6334c805c22'); define('GRAPH_CLIENT_SECRET', 'rRN8Q~FPfSL8O24iZthi_LVJTjGOCZG.DnxGHaSk'); define('GRAPH_SENDER_EMAIL', 'noreply@azcomputerguru.com'); +define('GRAPH_SENDER_NAME', 'Arizona Computer Guru'); +define('GRAPH_REPLY_TO_EMAIL', 'admin@azcomputerguru.com'); +define('GRAPH_REPLY_TO_NAME', 'Arizona Computer Guru'); // -------------------------------------------------------------------------- // Admin / Auth diff --git a/projects/msp-tools/quote-wizard/php-api/api/services/email_service.php b/projects/msp-tools/quote-wizard/php-api/api/services/email_service.php index 6455006..9ca27a3 100644 --- a/projects/msp-tools/quote-wizard/php-api/api/services/email_service.php +++ b/projects/msp-tools/quote-wizard/php-api/api/services/email_service.php @@ -124,9 +124,23 @@ function send_email(string $to_email, string $subject, string $body_html, ?strin 'contentType' => 'HTML', 'content' => $body_html, ], + 'from' => [ + 'emailAddress' => [ + 'name' => defined('GRAPH_SENDER_NAME') ? GRAPH_SENDER_NAME : 'Arizona Computer Guru', + 'address' => GRAPH_SENDER_EMAIL, + ], + ], 'toRecipients' => [ ['emailAddress' => ['address' => $to_email]], ], + 'replyTo' => [ + [ + 'emailAddress' => [ + 'name' => defined('GRAPH_REPLY_TO_NAME') ? GRAPH_REPLY_TO_NAME : 'Arizona Computer Guru', + 'address' => defined('GRAPH_REPLY_TO_EMAIL') ? GRAPH_REPLY_TO_EMAIL : GRAPH_SENDER_EMAIL, + ], + ], + ], ], 'saveToSentItems' => 'true', ];