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.

Revisions

  1. judge2020 renamed this gist Jul 9, 2024. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. judge2020 created this gist Jul 9, 2024.
    84 changes: 84 additions & 0 deletions _props.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,84 @@
    {
    "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"
    }
    250 changes: 250 additions & 0 deletions calculator.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,250 @@
    /* 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;