diff --git a/src/components/Checkbox.tsx b/src/components/Checkbox.tsx new file mode 100644 index 00000000..63b414b5 --- /dev/null +++ b/src/components/Checkbox.tsx @@ -0,0 +1,31 @@ +import type { JSX } from "preact"; + +export interface CheckboxProps { + name: string; + id: string; + labelText: string; + required?: boolean; +} + +export function Checkbox({ + name, + id, + labelText, + required, +}: CheckboxProps): JSX.Element { + return ( +
+ + +
+ ); +} diff --git a/src/content/geothermal/what.mdx b/src/content/geothermal/what.mdx index 507fa4d1..5cbfe022 100644 --- a/src/content/geothermal/what.mdx +++ b/src/content/geothermal/what.mdx @@ -12,6 +12,13 @@ However, it’s important to remember that geothermal energy isn’t a new conce Humans have been using it for over 10,000 years for functions such as heating hot springs, cooking, and even space heating in cities such as Pompeii. Nowadays, it’s still mainly used for heating; however, if you install a converter, it can also be used for electricity. Due to this many large universities such as Yale, Brown, and Missouri S&T have been installing geothermal systems to power their campuses. -The switch to geothermal is still beginning but it will be a worthy investment for the future. +The switch to geothermal is still beginning, but it will be a worthy investment for the future. -Geothermal mountain +Geothermal mountain + +There are four types of geothermal energy solutions. + +1. Horizontal loop: Horizontal loop systems are best for properties with more land, as they are bigger and require deeper trenches. +2. Vertical Loop: Vertical loop systems are very narrow and great for residential areas! +3. Closed Loop: Closed loop systems mix water and antifreeze to accommodate cold climates, making them optimal for northern locations. +4. Open Loop: Open loop systems circulate water from an outside source, meaning that they are optimal for properties with a steady water source. diff --git a/src/content/geothermal/worth-it.mdx b/src/content/geothermal/worth-it.mdx index 77d3b760..b9a62a06 100644 --- a/src/content/geothermal/worth-it.mdx +++ b/src/content/geothermal/worth-it.mdx @@ -7,10 +7,13 @@ sectionHeader: Is It Worth It? Deciding to invest in a geothermal energy solution can be tricky. When comparing it to solar it’s a lot more expensive to start and can take up to 20 years to pay itself off. The pumps also require electricity to power so it’s not technically _free_. -Similar to solar energy, there are also federal tax credits that range from 22% to 30%, applicable on the installation. +Similar to solar energy, there are also federal tax credits that range from 22% to 30%, applicable on installation. +When considering what solution to buy, keep in mind that estimates are rarely over $4000 off, though the actual prices vary depending on local labor costs and other factors. +Additionally, if your state has high geothermic activity, then you will recoup your cost faster. However, geothermal can be much more efficient than wind and solar as it isn’t impacted by weather. It will always run as long as it’s powered. Also, depending on the loop system you choose and how it corresponds to your environment you can generate a lot more energy. Overall, investing in geothermal can be a smart choice depending on your location and budget as it will save you money over time and lessen carbon emissions. +Feel free to ask our Why Switch AI chatbot for more information! -Geothermal plant +Geothermal plant diff --git a/src/fresh.gen.ts b/src/fresh.gen.ts index 7fa38fc8..d1891561 100644 --- a/src/fresh.gen.ts +++ b/src/fresh.gen.ts @@ -12,7 +12,7 @@ import * as $index from "./routes/index.tsx"; import * as $solutions_category_slug_ from "./routes/solutions/[category]/[[slug]].tsx"; import * as $solutions_category_index from "./routes/solutions/[category]/index.tsx"; import * as $HeaderMenu from "./islands/HeaderMenu.tsx"; -import * as $StateSelector from "./islands/StateSelector.tsx"; +import * as $Selector from "./islands/Selector.tsx"; import { type Manifest } from "$fresh/server.ts"; const manifest = { @@ -29,7 +29,7 @@ const manifest = { }, islands: { "./islands/HeaderMenu.tsx": $HeaderMenu, - "./islands/StateSelector.tsx": $StateSelector, + "./islands/Selector.tsx": $Selector, }, baseUrl: import.meta.url, } satisfies Manifest; diff --git a/src/islands/StateSelector.tsx b/src/islands/Selector.tsx similarity index 71% rename from src/islands/StateSelector.tsx rename to src/islands/Selector.tsx index 5f5f0cc4..6efb80f6 100644 --- a/src/islands/StateSelector.tsx +++ b/src/islands/Selector.tsx @@ -2,22 +2,34 @@ import { IS_BROWSER } from "$fresh/runtime.ts"; import { Combobox, Transition } from "@headlessui/react"; import { useSignal } from "@preact/signals"; import { Fragment, type JSX } from "preact"; -import { type State, states } from "../utils/calc.ts"; import { IconCheck, IconChevronDown } from "../utils/icons.ts"; import { tw } from "../utils/tailwind.ts"; -export interface StateSelectorProps { - currentState?: State | undefined; +export interface SelectorProps { + name: string; + question: string; + list: SelectorListObject[]; + current?: U | undefined; + required?: boolean; } -export function StateSelector({ - currentState, -}: StateSelectorProps): JSX.Element { - const state = useSignal(currentState); +export interface SelectorListObject { + name: string; + value: T; +} + +export function Selector({ + name, + question, + list, + current: currentValue, + required, +}: SelectorProps): JSX.Element { + const current = useSignal(list.find((val) => val.name === currentValue)); const query = useSignal(""); - const filteredStates = states.filter((state) => - state + const filtered = list.filter((item) => + item.name .toLowerCase() .replace(/\s+/g, "") .includes(query.value.toLowerCase().replace(/\s+/g, "")), @@ -27,20 +39,20 @@ export function StateSelector({
{ - state.value = newState; + value={current.value} + onChange={(newValue) => { + current.value = newValue; }} > - - What state are you from? - + {question}
) => `${state.name}`} onChange={(event) => { if (event.target instanceof HTMLInputElement) { query.value = event.target.value; @@ -66,17 +78,17 @@ export function StateSelector({ query.value = ""; }} > - - {filteredStates.length === 0 && query.value !== "" ? ( + + {filtered.length === 0 && query.value !== "" ? (
No results found
) : ( - filteredStates.map((state) => ( + filtered.map((item) => ( {({ selected, active }) => ( <> @@ -85,9 +97,9 @@ export function StateSelector({ selected ? tw`font-medium` : tw`font-normal` }`} > - {state} + {item.name} - {selected ? ( + {selected && ( - ) : undefined} + )} )} diff --git a/src/routes/calculator.tsx b/src/routes/calculator.tsx index ebb79077..2f1905e9 100644 --- a/src/routes/calculator.tsx +++ b/src/routes/calculator.tsx @@ -1,11 +1,29 @@ import { Head } from "$fresh/runtime.ts"; import type { Handlers, PageProps } from "$fresh/server.ts"; import type { JSX } from "preact"; +import { useId } from "preact/compat"; +import { Checkbox } from "../components/Checkbox.tsx"; import { Cover } from "../components/Cover.tsx"; import { Meta } from "../components/Meta.tsx"; -import { StateSelector } from "../islands/StateSelector.tsx"; -import { type State, type StateData, stateData } from "../utils/calc.ts"; +import { Selector, type SelectorListObject } from "../islands/Selector.tsx"; +import { + type GeoCostBreakdown, + calculatePricingIfHardInstallation, + geothermalLoopType, +} from "../utils/calc/geo.ts"; +import { calculatePricingFromType } from "../utils/calc/geo.ts"; +import { calculatePricing } from "../utils/calc/geo.ts"; +import { calculatePricingIfRequiresPermit } from "../utils/calc/geo.ts"; +import { calculatePricingMultiplierFromArea } from "../utils/calc/geo.ts"; +import { + type State, + type StateData, + stateData, + states, +} from "../utils/calc/solar.ts"; import type { FreshContextHelper } from "../utils/handlers.ts"; +import { usdFormat } from "../utils/intl.ts"; +import { yearFormat } from "../utils/intl.ts"; import { getIpLocation } from "../utils/ip.ts"; import { isKey } from "../utils/type-helpers.ts"; @@ -18,7 +36,8 @@ export interface CalculatorSearchProps { export interface CalculatorShowProps { state: "display"; - regionData: StateData; + solarRegionData: StateData; + geoCostData: GeoCostBreakdown; } /** @@ -34,21 +53,40 @@ export const handler: Handlers = { const visitor = await getIpLocation(ctx.remoteAddr.hostname); const url = new URL(req.url); - const state = url.searchParams.get("region"); - if (state === null) { + const state = url.searchParams.get("region[value]"); + const isHilly = url.searchParams.get("hills"); + const renovations = url.searchParams.get("renovations"); + const geoType = geothermalLoopType.safeParse( + url.searchParams.get("geo-type[value]"), + ); + const squareFootage = url.searchParams.get("area"); + const requiresPermit = url.searchParams.get("permit"); + + if (!state) { return ctx.render({ state: "search", region: visitor?.region, }); } + if (!geoType.success || !squareFootage) { + return ctx.renderNotFound(); + } + if (!isKey(stateData, state)) { return ctx.renderNotFound(); } return ctx.render({ state: "display", - regionData: stateData[state], + solarRegionData: stateData[state], + geoCostData: { + isHilly: Boolean(isHilly), + needsRenovations: Boolean(renovations), + type: geoType.data, + squareFootage: Number(squareFootage), + requiresPermit: Boolean(requiresPermit), + }, }); }, }; @@ -67,7 +105,10 @@ export default function Calculator({ -
+
+ + Calculate and compare the pricing for solar and geothermal. +
@@ -80,42 +121,136 @@ function CalculatorPages(data: CalculatorProps): JSX.Element { return ; case "display": - return ; + return ; } } -function CalculatorDisplay(data: CalculatorShowProps): JSX.Element { +function CalculatorSolarDisplay(data: CalculatorShowProps): JSX.Element { return ( - - - - - - - - - - - - - - - - - - - - - - -
Monies
Energy FormTime to PayoffSavings per MonthInstall CostRebateEmissions per Month
Solar{data.regionData.payoff.toFixed(2)} Years${data.regionData.savings.toFixed(2)}${data.regionData.install.toFixed(2)}${data.regionData.rebate.toFixed(2)}{data.regionData.emissions.toFixed(2)} lbs of Carbon per kWh
+
+
+

Solar Power

+
+

Time to Payoff

+ {yearFormat.format(data.solarRegionData.payoff)} +
+
+

Savings per Month

+ {usdFormat.format(data.solarRegionData.savings)} +
+
+

Install Cost

+ {usdFormat.format(data.solarRegionData.install)} +
+
+

Rebate

+ {usdFormat.format(data.solarRegionData.rebate)} +
+
+

Emissions per Month

+ + {data.solarRegionData.emissions} pounds of Carbon per kilowatt-hour + +
+
+ +
+

Geothermal Energy

+
+

Base Price

+ + {usdFormat.format(calculatePricingFromType(data.geoCostData.type))} + +
+
+

Area-based Price Multiplier

+ + {calculatePricingMultiplierFromArea(data.geoCostData.squareFootage)} + × + +
+
+

Fees for Hard Installation

+ + {usdFormat.format( + calculatePricingIfHardInstallation( + data.geoCostData.isHilly, + data.geoCostData.needsRenovations, + ), + )} + +
+
+

Permit fees

+ + {usdFormat.format( + calculatePricingIfRequiresPermit(data.geoCostData.requiresPermit), + )} + +
+
+

Total Price

+ {usdFormat.format(calculatePricing(data.geoCostData))} +
+
+
); } function CalculatorSearch(data: CalculatorSearchProps): JSX.Element { + const labelForHill = useId(); + const labelForRenovation = useId(); + const labelForArea = useId(); + const labelForPermit = useId(); return (
- + => { + return { name: state, value: state }; + })} + current={data.region} + required + /> + + + +
+ + +
+