Skip to content

Instantly share code, notes, and snippets.

@judge2020
Last active July 9, 2024 02:17
Show Gist options
  • Save judge2020/fab78fee2fac4a033bb8fe12afc65b58 to your computer and use it in GitHub Desktop.
Save judge2020/fab78fee2fac4a033bb8fe12afc65b58 to your computer and use it in GitHub Desktop.
Tesla Calculator code
/* eslint-disable camelcase */
import React, { useState } from "react";
import {
FormInputRange,
FormInputSelect,
FormItem,
FormLabelRange,
} from "@tesla/design-system-react";
import { Form, Input } from "@tesla/informed-tds";
import { arrayOf, func, number, shape, string } from "prop-types";
import "./override.css";
import "@tesla/design-system/dist/index.css";
import styles from "./calculator.module.css";
const defaultProps = {
copy: { chargeCost: "Charge Cost", cta: "", currency: "$", gasSavings: "Gas Savings", more: "" },
};
const propTypes = {
copy: shape({
chargeCost: string,
cta: string,
currency: string,
gasSavings: string,
more: string,
rangeLabel: string,
}),
allVehicles: arrayOf(shape({ value: string, label: string, product: string, "img-code": string }))
.isRequired,
calculationData: shape({
ms: shape({
fuel_efficiency_imperial: number,
fuel_efficiency_metric: number,
kwh_consumption_100: number,
supercharger_kwh_price: number,
fuel_price: number,
kwh_price: number,
electricity_last_updated: string,
last_updated: string,
}),
}).isRequired,
selectVehicle: func.isRequired,
selectedVehicle: shape({ product: string, img: string }).isRequired,
timeInput: string.isRequired,
};
const AVERAGE_NUM_DAY_MONTH = 30.437;
const DEFAULT_RANGE = "100";
const costOfGas = (distanceInMiles, fuelEfficiencyInMilesPerGallon, costOfGasperGallon) => {
return (distanceInMiles / fuelEfficiencyInMilesPerGallon) * costOfGasperGallon;
};
const round = (value, decimals) => {
const rounded = Number(`${Math.round(`${value}e${decimals}`)}e-${decimals}`);
return rounded.toFixed(2);
};
const dailyChargeCostCalc = (distanceInKM, energyEfficiencyInKWHPer100KM, costPerHour) => {
return round((distanceInKM / 100) * energyEfficiencyInKWHPer100KM * costPerHour, 2);
};
const Calculator = ({
copy,
allVehicles,
calculationData,
selectVehicle,
selectedVehicle,
timeInput,
}) => {
const defaultVehicleCalcData = calculationData[selectedVehicle.product];
const { fuel_efficiency_imperial, fuel_price, kwh_consumption_100, kwh_price } =
defaultVehicleCalcData;
const defaultRangeKM = DEFAULT_RANGE;
const defaultDailyChargeCost = dailyChargeCostCalc(DEFAULT_RANGE, kwh_consumption_100, kwh_price);
const defaultDailyGasCost = costOfGas(defaultRangeKM, fuel_efficiency_imperial, fuel_price);
const defaultMonthlyChargeCost = defaultDailyChargeCost * AVERAGE_NUM_DAY_MONTH;
const defaultMontlyGasCost = defaultDailyGasCost * AVERAGE_NUM_DAY_MONTH;
const [rangeValue, setRangeValue] = useState(DEFAULT_RANGE);
const [dailyChargeCost, setDailyChargeCost] = useState(defaultDailyChargeCost);
const [dailyGasSaving, setDailyGasSaving] = useState(
defaultDailyGasCost - defaultDailyChargeCost
);
const [monthlyChargeCost, setMonthlyChargeCost] = useState(defaultMonthlyChargeCost);
const [monthlyGasSaving, setMonthlyGasSaving] = useState(
defaultMontlyGasCost - defaultMonthlyChargeCost
);
const updateCalculation = (vehicle, vehicleUpdate = false, forceRange) => {
let updatedRangeValue;
if (!vehicleUpdate) {
updatedRangeValue = forceRange || 0;
} else {
updatedRangeValue = parseInt(rangeValue, 10) || 0;
}
const vehicleCalcData = calculationData[vehicle.product];
/* eslint-disable */
const { fuel_efficiency_imperial, fuel_price, kwh_consumption_100, kwh_price } =
vehicleCalcData;
const updatedDailyChargeCost = dailyChargeCostCalc(
updatedRangeValue,
kwh_consumption_100,
kwh_price
);
const updatedDailyCostOfGas = costOfGas(
updatedRangeValue,
fuel_efficiency_imperial,
fuel_price
);
const updatedDailyGasSaving = updatedDailyCostOfGas - updatedDailyChargeCost;
setDailyChargeCost(updatedDailyChargeCost);
setDailyGasSaving(updatedDailyGasSaving);
setMonthlyChargeCost(updatedDailyChargeCost * AVERAGE_NUM_DAY_MONTH);
setMonthlyGasSaving(updatedDailyGasSaving * AVERAGE_NUM_DAY_MONTH);
};
const handleIncrement = (mileageValue) => {
let inputRangeValue = mileageValue;
if (typeof inputRangeValue === undefined) {
inputRangeValue = 0
}
setRangeValue(inputRangeValue);
// Analytics
if (window.dataLayer) {
window.dataLayer.push({
event: "configurator",
"cfg-type": "charging-calculator",
interaction: `miles-driven-${mileageValue}`,
});
}
updateCalculation(selectedVehicle, false, inputRangeValue);
};
const sendAnalyticEvent = (interactionKey) => {
// Analytics
if (window.dataLayer) {
window.dataLayer.push({
event: "configurator",
"cfg-type": "charging-calculator",
interaction: interactionKey,
});
}
}
const path =
typeof window.tesla !== "undefined" &&
typeof window.tesla.App !== "undefined" &&
typeof window.tesla.App.base_url !== "undefined"
? window.tesla.App.base_url
: 'https://www.tesla.com';
return (
<>
<div className={`tds--vertical_padding ${styles.CalculatorDisplaySection}`}>
<ol
className={`tds-list tds-list--horizontal ${styles.CalculatorDisplayWrapper}`}
aria-live="polite"
aria-atomic="true"
>
<li className="tds-list-item tds-o-list-item">
<div
className="tds-text--h1 tds-list-item_title tds-text_color--black tds--no_padding"
data-id="range"
>
<span>{copy.currency}</span>
<span className="specs--value-label">
{timeInput === "daily" ? round(dailyChargeCost, 2) : round(monthlyChargeCost, 2)}
</span>
</div>
<span className="tds-text--body" data-id="range-label">
<span>{copy.chargeCost}</span>
</span>
</li>
<li className="tds-list-item tds-o-list-item">
<div
className="tds-text--h1 tds-list-item_title tds-text_color--black tds--no_padding"
data-id="top-speed"
>
<span>{copy.currency}</span>
<span className="specs--value-label">
{timeInput === "daily" ? round(dailyGasSaving, 2) : round(monthlyGasSaving, 2)}
</span>
</div>
<span className="tds-text--body" data-id="top-speed-label">
{copy.gasSavings}
</span>
</li>
</ol>
</div>
<Form className={styles.CalculatorWrapper}>
{({ formApi }) => (
<>
<FormItem className={styles.RangeInputWrapper}>
<FormLabelRange>{copy.rangeLabel}</FormLabelRange>
<FormInputRange
progress
value={rangeValue === "" ? "0" : rangeValue}
onChange={(e) => { formApi.setValue('mileage', parseInt(e.target.value, 10)); handleIncrement(parseInt(e.target.value, 10))}}
max="300"
/>
</FormItem>
<Input placeholder={0} name="mileage" type="number" defaultValue={0} initialValue={rangeValue} onChange={
formState => {
let newValue = formState.value !== undefined ? formState.value : 0;
if (formApi && (newValue > 3300 || newValue < 0 )) {
formApi.setValue('mileage', rangeValue);
newValue = rangeValue;
};
handleIncrement(newValue);
}} className={`${styles.NumberInputWrapper} ${styles.CalculatorNumberInput}`}/>
<FormItem className={styles.VehicleSelectWrapper}>
<FormInputSelect
className={styles.VehicleSelectWrapper}
id="select-1"
options={allVehicles}
onChange={(e) => {
selectVehicle(e, updateCalculation);
}}
/>
</FormItem>
</>
)}
</Form>
<div className={`tds-btn_group tds-btn_group--vertical ${styles.ButtonGroupWrapper}`}>
<a
className={`tds-btn ${styles.OrderButton}`}
href={`${path}/${selectedVehicle.product.replace('m', 'model')}/design#overview`}
onClick={sendAnalyticEvent.bind(null, 'order-now')}
>
{copy.cta}
</a>
<a href="/support/charging" className="tds-link" onClick={sendAnalyticEvent.bind(null, 'learn-more-about-charging')}>
{copy.more}
</a>
</div>
</>
);
};
Calculator.propTypes = propTypes;
Calculator.defaultProps = defaultProps;
export default Calculator;
{
"copy": {
"chargeCost": "Charge Cost",
"cta": "order now",
"ctaLink": "https://shop.tesla.com/product/wall-connector",
"currency": "$",
"gasSavings": "Gas Savings",
"more": "Learn More About Charging",
"rangeLabel": "Miles driven daily"
},
"allVehicles": [
{
"value": "ms",
"label": "Model S",
"data-product": "ms",
"data-img-code": "mslongrange"
},
{
"value": "m3",
"label": "Model 3",
"data-product": "m3",
"data-img-code": "m3standard"
},
{
"value": "mx",
"label": "Model X",
"data-product": "mx",
"data-img-code": "mxlongrange"
},
{
"value": "my",
"label": "Model Y",
"data-product": "my",
"data-img-code": "mylongrange"
}
],
"calculationData": {
"ms": {
"fuel_efficiency_imperial": 23,
"fuel_efficiency_metric": 0,
"kwh_consumption_100": 28,
"supercharger_kwh_price": 0.26,
"fuel_price": 3.9,
"kwh_price": 0.16,
"electricity_last_updated": "",
"last_updated": "2023-08-26"
},
"m3": {
"fuel_efficiency_imperial": 28,
"fuel_efficiency_metric": 0,
"kwh_consumption_100": 25.4,
"supercharger_kwh_price": 0.26,
"fuel_price": 3.9,
"kwh_price": 0.161,
"electricity_last_updated": "",
"last_updated": "2023-08-26"
},
"mx": {
"fuel_efficiency_imperial": 22,
"fuel_efficiency_metric": 0,
"kwh_consumption_100": 33,
"supercharger_kwh_price": 0.26,
"fuel_price": 3.9,
"kwh_price": 0.161,
"electricity_last_updated": "",
"last_updated": "2023-08-26"
},
"my": {
"fuel_efficiency_imperial": 25,
"fuel_efficiency_metric": 0,
"kwh_consumption_100": 28,
"supercharger_kwh_price": 0.26,
"fuel_price": 3.9,
"kwh_price": 0.16,
"electricity_last_updated": "",
"last_updated": "2023-08-26"
}
},
"selectedVehicle": {
"img": "mslongrange",
"product": "ms"
},
"timeInput": "daily"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment