save
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import * as React from "react"
|
||||
import { useSearchParams } from "react-router-dom"
|
||||
import { Ban, Check, CircleCheck, Euro, Plus, X } from "lucide-react"
|
||||
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
@@ -40,9 +41,13 @@ const HEAT_OPTIONS: { value: Heat; label: string }[] = [
|
||||
{ value: "unbekannt", label: "Weiß ich nicht" },
|
||||
]
|
||||
|
||||
const INITIAL_BUILDINGS: Building[] = [
|
||||
{ id: 1, adresse: "", heat: "heizkoerper", wohnungen: 12, zimmer: 3 },
|
||||
]
|
||||
const INITIAL_BUILDING: Building = {
|
||||
id: 1,
|
||||
adresse: "",
|
||||
heat: "heizkoerper",
|
||||
wohnungen: 12,
|
||||
zimmer: 3,
|
||||
}
|
||||
|
||||
/** Grundpreise je Wohnung / Monat – Selbstmontage, wie in der Klarpreis-Liste. */
|
||||
const UVI_MONTHLY = 10.5
|
||||
@@ -81,6 +86,74 @@ function settle(value: number | "", min: number) {
|
||||
return value === "" || value < min ? min : value
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep-link preset. Pages elsewhere on the site link into the configurator with
|
||||
* parts of the form already answered, e.g. `/konfigurator?installation=0` for
|
||||
* the self-install route. Supported params: `installation`, `uvi`, `rwm`,
|
||||
* `adresse`, `heizung`, `wohnungen`, `zimmer`. Missing or unreadable values
|
||||
* keep the page defaults; numbers below the field minimum settle at it, just
|
||||
* as they do when typed into the form.
|
||||
*/
|
||||
type Preset = {
|
||||
uvi?: boolean
|
||||
rwm?: boolean
|
||||
install?: boolean
|
||||
/** Seeds the single building the configurator starts with. */
|
||||
building: Partial<Building>
|
||||
}
|
||||
|
||||
/** A bare flag (`?installation`) counts as "on" so short links stay readable. */
|
||||
function presetFlag(raw: string | null): boolean | undefined {
|
||||
if (raw === null) return undefined
|
||||
switch (raw.trim().toLowerCase()) {
|
||||
case "":
|
||||
case "1":
|
||||
case "true":
|
||||
case "ja":
|
||||
return true
|
||||
case "0":
|
||||
case "false":
|
||||
case "nein":
|
||||
return false
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function presetNumber(
|
||||
raw: string | null,
|
||||
parse: (raw: string) => number | ""
|
||||
): number | undefined {
|
||||
if (raw === null) return undefined
|
||||
const value = parse(raw)
|
||||
return value === "" ? undefined : settle(value, 1)
|
||||
}
|
||||
|
||||
function presetHeat(raw: string | null): Heat | undefined {
|
||||
return HEAT_OPTIONS.some((opt) => opt.value === raw)
|
||||
? (raw as Heat)
|
||||
: undefined
|
||||
}
|
||||
|
||||
function readPreset(params: URLSearchParams): Preset {
|
||||
const building: Partial<Building> = {}
|
||||
const adresse = params.get("adresse")
|
||||
if (adresse !== null) building.adresse = adresse
|
||||
const heat = presetHeat(params.get("heizung"))
|
||||
if (heat !== undefined) building.heat = heat
|
||||
const wohnungen = presetNumber(params.get("wohnungen"), parseCount)
|
||||
if (wohnungen !== undefined) building.wohnungen = wohnungen
|
||||
const zimmer = presetNumber(params.get("zimmer"), parseAmount)
|
||||
if (zimmer !== undefined) building.zimmer = zimmer
|
||||
|
||||
return {
|
||||
uvi: presetFlag(params.get("uvi")),
|
||||
rwm: presetFlag(params.get("rwm")),
|
||||
install: presetFlag(params.get("installation")),
|
||||
building,
|
||||
}
|
||||
}
|
||||
|
||||
/** glossy numerals for the three configurator steps */
|
||||
type Step = 1 | 2 | 3
|
||||
|
||||
@@ -254,12 +327,32 @@ function PriceRow({
|
||||
}
|
||||
|
||||
export default function ConfiguratorPage() {
|
||||
const [uvi, setUvi] = React.useState(true)
|
||||
const [rwm, setRwm] = React.useState(true)
|
||||
const [buildings, setBuildings] = React.useState<Building[]>(INITIAL_BUILDINGS)
|
||||
const [install, setInstall] = React.useState(true)
|
||||
const [searchParams] = useSearchParams()
|
||||
const preset = React.useMemo(() => readPreset(searchParams), [searchParams])
|
||||
|
||||
const [uvi, setUvi] = React.useState(preset.uvi ?? true)
|
||||
const [rwm, setRwm] = React.useState(preset.rwm ?? true)
|
||||
const [buildings, setBuildings] = React.useState<Building[]>(() => [
|
||||
{ ...INITIAL_BUILDING, ...preset.building },
|
||||
])
|
||||
const [install, setInstall] = React.useState(preset.install ?? true)
|
||||
const [email, setEmail] = React.useState("")
|
||||
const nextId = React.useRef(INITIAL_BUILDINGS.length + 1)
|
||||
const nextId = React.useRef(INITIAL_BUILDING.id + 1)
|
||||
|
||||
/*
|
||||
* Following a second preset link while the page stays mounted re-seeds the
|
||||
* form; the mount-time preset is already applied by the initialisers above.
|
||||
*/
|
||||
const appliedPreset = React.useRef(preset)
|
||||
React.useEffect(() => {
|
||||
if (appliedPreset.current === preset) return
|
||||
appliedPreset.current = preset
|
||||
setUvi(preset.uvi ?? true)
|
||||
setRwm(preset.rwm ?? true)
|
||||
setInstall(preset.install ?? true)
|
||||
setBuildings([{ ...INITIAL_BUILDING, ...preset.building }])
|
||||
nextId.current = INITIAL_BUILDING.id + 1
|
||||
}, [preset])
|
||||
|
||||
const updateBuilding = (id: number, patch: Partial<Building>) => {
|
||||
setBuildings((prev) => prev.map((b) => (b.id === id ? { ...b, ...patch } : b)))
|
||||
|
||||
Reference in New Issue
Block a user