This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import * as React from "react"
|
||||
import { useSearchParams } from "react-router-dom"
|
||||
import { Ban, Check, CircleCheck, Euro, Plus, X } from "lucide-react"
|
||||
import { Link, useSearchParams } from "react-router-dom"
|
||||
import { Check, Plus, X } from "lucide-react"
|
||||
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
type RenderAsset,
|
||||
} from "@/components/solenos/site-assets"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useAnimatedNumber } from "@/lib/use-animated-number"
|
||||
|
||||
type Heat = "heizkoerper" | "fussbodenheizung" | "gemischt" | "unbekannt"
|
||||
|
||||
@@ -50,22 +49,6 @@ const INITIAL_BUILDING: Building = {
|
||||
zimmer: 3,
|
||||
}
|
||||
|
||||
/** Grundpreise je Wohnung / Monat – Selbstmontage, wie in der Klarpreis-Liste. */
|
||||
const UVI_MONTHLY = 10.5
|
||||
const INSPECTION_MONTHLY = 2.2
|
||||
/** Aufschlag je Monatsposition, wenn SolenOS montiert – Platzhalter bis zum finalen Preismodell. */
|
||||
const INSTALL_MONTHLY_SURCHARGE = 2
|
||||
/** RWM-Hardware je Wohnung einmalig, inklusive Montage – wie in der Klarpreis-Liste. */
|
||||
const RWM_ONE_TIME_INSTALLED = 42
|
||||
/** Selbstmontage durch Kunde oder Fachbetrieb – Platzhalter bis zum finalen Preismodell. */
|
||||
const RWM_ONE_TIME_SELF = 30
|
||||
|
||||
function formatEuro(value: number) {
|
||||
return value.toLocaleString("de-DE", {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps a partially edited number field empty instead of snapping it to 0, so
|
||||
@@ -90,13 +73,14 @@ function settle(value: number | "", min: number) {
|
||||
/**
|
||||
* 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.
|
||||
* the self-install route. Supported params: `installation`, `uvi`,
|
||||
* `abrechnung`, `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
|
||||
billing?: boolean
|
||||
rwm?: boolean
|
||||
install?: boolean
|
||||
/** Seeds the single building the configurator starts with. */
|
||||
@@ -149,6 +133,7 @@ function readPreset(params: URLSearchParams): Preset {
|
||||
|
||||
return {
|
||||
uvi: presetFlag(params.get("uvi")),
|
||||
billing: presetFlag(params.get("abrechnung")),
|
||||
rwm: presetFlag(params.get("rwm")),
|
||||
install: presetFlag(params.get("installation")),
|
||||
building,
|
||||
@@ -185,35 +170,61 @@ function StepCard({
|
||||
* Selectable service tile (step 1) — the active option carries a brand border.
|
||||
*/
|
||||
function ServiceOption({
|
||||
id,
|
||||
checked,
|
||||
onCheckedChange,
|
||||
title,
|
||||
description,
|
||||
}: {
|
||||
id: string
|
||||
checked: boolean
|
||||
onCheckedChange: (checked: boolean) => void
|
||||
title: string
|
||||
description: string
|
||||
}) {
|
||||
return (
|
||||
<label
|
||||
<div
|
||||
className={cn(
|
||||
"flex cursor-pointer items-start gap-3 rounded-xl border p-4 transition-colors",
|
||||
"rounded-xl border p-4 transition-colors",
|
||||
checked
|
||||
? "border-primary bg-brand-50/60 shadow-pill"
|
||||
: "border-border bg-card hover:border-brand-200 hover:bg-brand-50/40"
|
||||
)}
|
||||
>
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
onCheckedChange={(v) => onCheckedChange(v === true)}
|
||||
className="mt-0.5"
|
||||
/>
|
||||
<span>
|
||||
<span className="block text-sm font-bold text-navy">{title}</span>
|
||||
<span className="block text-sm text-muted-foreground">{description}</span>
|
||||
</span>
|
||||
</label>
|
||||
<div className="flex items-start gap-3">
|
||||
<Checkbox
|
||||
id={id}
|
||||
checked={checked}
|
||||
onCheckedChange={(value) => onCheckedChange(value === true)}
|
||||
aria-describedby={`${id}-description ${id}-state`}
|
||||
className="mt-0.5 size-6"
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex flex-wrap items-start justify-between gap-2">
|
||||
<Label
|
||||
htmlFor={id}
|
||||
className="cursor-pointer text-sm font-bold leading-snug text-navy"
|
||||
>
|
||||
{title}
|
||||
</Label>
|
||||
<Badge
|
||||
id={`${id}-state`}
|
||||
variant={checked ? "default" : "muted"}
|
||||
aria-live="polite"
|
||||
className="shrink-0"
|
||||
>
|
||||
{checked ? "Enthalten · abwählbar" : "Hinzufügen"}
|
||||
</Badge>
|
||||
</div>
|
||||
<p
|
||||
id={`${id}-description`}
|
||||
className="mt-1 text-sm leading-relaxed text-muted-foreground"
|
||||
>
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -270,57 +281,21 @@ function PackageCard({
|
||||
/>
|
||||
{included ? null : (
|
||||
<p className="mt-5 text-sm text-muted-foreground">
|
||||
Leistung in Schritt 1 auswählen, um sie in den Preis aufzunehmen.
|
||||
Leistung in Schritt 1 auswählen, um sie in den Leistungsumfang
|
||||
aufzunehmen.
|
||||
</p>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* One position of the price indication: figure, cadence and whether the
|
||||
* position can be passed on to tenants (umlagefähig). Wording follows the
|
||||
* Klarpreis price list.
|
||||
*/
|
||||
function PriceRow({
|
||||
label,
|
||||
amount,
|
||||
unit,
|
||||
note,
|
||||
allocatable,
|
||||
}: {
|
||||
label: string
|
||||
amount: number
|
||||
unit: string
|
||||
note?: string
|
||||
allocatable: boolean
|
||||
}) {
|
||||
const shown = useAnimatedNumber(amount)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p className="text-sm font-bold text-navy">{label}</p>
|
||||
<p className="mt-1 text-3xl font-extrabold tracking-tight text-navy tabular-nums">
|
||||
ca. {formatEuro(shown)}
|
||||
{"\u00A0"}€
|
||||
</p>
|
||||
<p className="mt-0.5 text-sm text-muted-foreground">{unit}</p>
|
||||
{note ? (
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">{note}</p>
|
||||
) : null}
|
||||
<Badge variant={allocatable ? "default" : "destructive"} className="mt-3">
|
||||
{allocatable ? <CircleCheck /> : <Ban />}
|
||||
{allocatable ? "Umlagefähig" : "Nicht umlagefähig"}
|
||||
</Badge>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function ConfiguratorPage() {
|
||||
const [searchParams] = useSearchParams()
|
||||
const preset = React.useMemo(() => readPreset(searchParams), [searchParams])
|
||||
|
||||
const [uvi, setUvi] = React.useState(preset.uvi ?? true)
|
||||
const [billing, setBilling] = React.useState(preset.billing ?? true)
|
||||
const [rwm, setRwm] = React.useState(preset.rwm ?? true)
|
||||
const [buildings, setBuildings] = React.useState<Building[]>(() => [
|
||||
{ ...INITIAL_BUILDING, ...preset.building },
|
||||
@@ -338,6 +313,7 @@ export default function ConfiguratorPage() {
|
||||
if (appliedPreset.current === preset) return
|
||||
appliedPreset.current = preset
|
||||
setUvi(preset.uvi ?? true)
|
||||
setBilling(preset.billing ?? true)
|
||||
setRwm(preset.rwm ?? true)
|
||||
setInstall(preset.install ?? true)
|
||||
setBuildings([{ ...INITIAL_BUILDING, ...preset.building }])
|
||||
@@ -360,15 +336,6 @@ export default function ConfiguratorPage() {
|
||||
}
|
||||
|
||||
const totalUnits = buildings.reduce((sum, b) => sum + (b.wohnungen || 0), 0)
|
||||
const installSurcharge = install ? INSTALL_MONTHLY_SURCHARGE : 0
|
||||
const uviMonthly = UVI_MONTHLY + installSurcharge
|
||||
const inspectionMonthly = INSPECTION_MONTHLY + installSurcharge
|
||||
const installNote = install
|
||||
? `inklusive ${formatEuro(INSTALL_MONTHLY_SURCHARGE)}\u00A0€ Installationsanteil`
|
||||
: undefined
|
||||
const rwmRatePerUnit = install ? RWM_ONE_TIME_INSTALLED : RWM_ONE_TIME_SELF
|
||||
/* RWM hardware is billed once per Wohnung – the estimate is the sum. */
|
||||
const rwmOneTimeTotal = rwm ? rwmRatePerUnit * totalUnits : 0
|
||||
|
||||
return (
|
||||
<Container>
|
||||
@@ -383,7 +350,7 @@ export default function ConfiguratorPage() {
|
||||
/>
|
||||
<SectionHeading
|
||||
title="Konfigurator"
|
||||
lead="In 3 Schritten zur Preisindikation."
|
||||
lead="Leistungen auswählen, Gebäudedaten online erfassen und die nächsten Schritte transparent planen."
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -393,16 +360,25 @@ export default function ConfiguratorPage() {
|
||||
<StepCard step={1} title="Leistungen wählen">
|
||||
<div className="flex flex-col gap-3">
|
||||
<ServiceOption
|
||||
id="service-uvi"
|
||||
checked={uvi}
|
||||
onCheckedChange={setUvi}
|
||||
title="UVI"
|
||||
description="Unterjährige Verbrauchsinformation inklusive Messtechnik"
|
||||
title="Monatliche Verbrauchsinformation (UVI)"
|
||||
description="Monatliche Verbrauchswerte und Vergleiche für jede Wohnung einschließlich der benötigten Messtechnik."
|
||||
/>
|
||||
<ServiceOption
|
||||
id="service-billing"
|
||||
checked={billing}
|
||||
onCheckedChange={setBilling}
|
||||
title="Heizkostenabrechnung"
|
||||
description="Jährliche Heizkostenabrechnung einschließlich der gesetzlich vorgeschriebenen Pflichtangaben und Dokumente."
|
||||
/>
|
||||
<ServiceOption
|
||||
id="service-smoke-alarms"
|
||||
checked={rwm}
|
||||
onCheckedChange={setRwm}
|
||||
title="Rauchwarnmelder"
|
||||
description="Rauchwarnmelder inklusive digitaler Dienstleistungen"
|
||||
description="Rauchwarnmelder mit Ferninspektion, Geräteverwaltung sowie Prüf- und Statusdokumentation."
|
||||
/>
|
||||
</div>
|
||||
<Separator className="my-6" />
|
||||
@@ -414,29 +390,38 @@ export default function ConfiguratorPage() {
|
||||
: "border-border bg-card"
|
||||
)}
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<Label
|
||||
htmlFor="installation"
|
||||
className="cursor-pointer text-sm font-bold text-navy"
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Label
|
||||
htmlFor="installation"
|
||||
className="cursor-pointer text-sm font-bold text-navy"
|
||||
>
|
||||
Montage durch SolenOS
|
||||
</Label>
|
||||
<Badge variant={install ? "default" : "muted"}>
|
||||
{install ? "SolenOS montiert" : "Selbstmontage"}
|
||||
</Badge>
|
||||
</div>
|
||||
<p
|
||||
id="installation-description"
|
||||
className="mt-1 text-sm text-muted-foreground"
|
||||
>
|
||||
Installation durch SolenOS
|
||||
</Label>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{install
|
||||
? "SolenOS organisiert die Montage – Geräte- und Monatspreise enthalten die Installation."
|
||||
: "Sie selbst, Ihr Hausmeister oder Ihr Fachbetrieb montiert die Geräte."}
|
||||
? "SolenOS übernimmt Montage und Softwarekonfiguration. Der geprüfte Gesamtpreis folgt mit dem verbindlichen Angebot."
|
||||
: "Sie, Ihr Hausmeister oder Ihr Fachbetrieb montieren. Eine Softwarekonfiguration vor Ort ist nicht erforderlich."}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="installation"
|
||||
checked={install}
|
||||
onCheckedChange={setInstall}
|
||||
aria-describedby="installation-description"
|
||||
className="mt-0.5"
|
||||
/>
|
||||
</div>
|
||||
</StepCard>
|
||||
|
||||
<StepCard step={2} title="Gebäude angeben">
|
||||
<StepCard step={2} title="Gebäudedaten online erfassen">
|
||||
<div className="flex flex-col gap-5">
|
||||
{buildings.map((b, i) => (
|
||||
<div
|
||||
@@ -549,10 +534,37 @@ export default function ConfiguratorPage() {
|
||||
</div>
|
||||
</StepCard>
|
||||
|
||||
<StepCard step={3} title="Konfiguration speichern">
|
||||
<StepCard step={3} title="Persönlichen Link anfordern">
|
||||
<div className="rounded-xl border border-brand-200 bg-brand-50/60 p-4">
|
||||
<p className="text-sm font-bold text-navy">
|
||||
So geht es nach der E-Mail weiter
|
||||
</p>
|
||||
<ol className="mt-3 list-decimal space-y-2 pl-5 text-sm leading-relaxed text-muted-foreground">
|
||||
<li>
|
||||
Wir speichern Ihre bisherige Auswahl und senden Ihnen einen
|
||||
persönlichen Link.
|
||||
</li>
|
||||
<li>
|
||||
Über den Link ergänzen Sie die Gebäudedetails und laden im
|
||||
Portal Fotos vorhandener wasserführender Einbausituationen
|
||||
hoch. Fotos ersetzen Maße und Typangaben, soweit sie eine
|
||||
eindeutige Prüfung ermöglichen.
|
||||
</li>
|
||||
<li>
|
||||
SolenOS prüft die Angaben und Fotos manuell, bestimmt die
|
||||
endgültige Ausstattung und erstellt anschließend den genauen
|
||||
Gesamtpreis beziehungsweise ein verbindliches Angebot.
|
||||
</li>
|
||||
</ol>
|
||||
<p className="mt-3 text-xs leading-relaxed text-muted-foreground">
|
||||
Beispiel für spätere Detailfragen: Kinderzimmer und regelmäßig
|
||||
genutzte Gästezimmer zählen als Schlafräume; ein dauerhaftes
|
||||
Büro nicht.
|
||||
</p>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={(e) => e.preventDefault()}
|
||||
className="flex flex-col gap-3"
|
||||
onSubmit={(event) => event.preventDefault()}
|
||||
className="mt-5 flex flex-col gap-3"
|
||||
>
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<Label htmlFor="config-email">E-Mail-Adresse</Label>
|
||||
@@ -561,79 +573,121 @@ export default function ConfiguratorPage() {
|
||||
type="email"
|
||||
placeholder="name@unternehmen.de"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
onChange={(event) => setEmail(event.target.value)}
|
||||
aria-describedby="config-email-privacy"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" className="self-start">
|
||||
Konfiguration speichern & fortsetzen
|
||||
</Button>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Sie erhalten einen persönlichen Link
|
||||
zu Ihrer Konfiguration. Damit kannst du
|
||||
sofort weitermachen, später zurückkehren
|
||||
oder den Link intern weitergeben.
|
||||
<p
|
||||
id="config-email-privacy"
|
||||
className="text-xs leading-relaxed text-muted-foreground"
|
||||
>
|
||||
Wir verwenden Ihre E-Mail-Adresse, um den persönlichen Link
|
||||
zu versenden. Einzelheiten finden Sie in unseren{" "}
|
||||
<Link
|
||||
to="/wissen/datenschutz-sicherheit"
|
||||
className="font-semibold text-primary underline underline-offset-2"
|
||||
>
|
||||
Hinweisen zu Datenschutz & Sicherheit
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
<Button type="submit" className="self-start">
|
||||
Persönlichen Link anfordern
|
||||
</Button>
|
||||
</form>
|
||||
</StepCard>
|
||||
</div>
|
||||
|
||||
{/* Right column: sticky price indication */}
|
||||
{/* Right column: sticky configuration summary */}
|
||||
<div className="lg:sticky lg:top-24">
|
||||
<Card className="gap-0 overflow-hidden p-0 shadow-card-lg">
|
||||
<div className="flex items-center justify-between gap-3 bg-brand-gradient p-6 text-white sm:p-7">
|
||||
<p className="text-xs font-bold tracking-[0.14em] uppercase">
|
||||
Geschätzter Preis
|
||||
Ihre Konfiguration
|
||||
</p>
|
||||
<IconBadge variant="glass" size="lg">
|
||||
<Euro />
|
||||
<Check />
|
||||
</IconBadge>
|
||||
</div>
|
||||
<div className="p-6 sm:p-7">
|
||||
{uvi || rwm ? (
|
||||
<div className="flex flex-col gap-5">
|
||||
{uvi ? (
|
||||
<PriceRow
|
||||
label="UVI-Komplettbetrieb"
|
||||
amount={uviMonthly}
|
||||
unit="je Wohnung / Monat"
|
||||
note={installNote}
|
||||
allocatable
|
||||
/>
|
||||
) : null}
|
||||
{rwm ? (
|
||||
<>
|
||||
{uvi ? <Separator /> : null}
|
||||
<PriceRow
|
||||
label="Ferninspektion Rauchwarnmelder"
|
||||
amount={inspectionMonthly}
|
||||
unit="je Wohnung / Monat"
|
||||
note={installNote}
|
||||
allocatable
|
||||
/>
|
||||
<Separator />
|
||||
<PriceRow
|
||||
label="Rauchwarnmelder-Geräte"
|
||||
amount={rwmOneTimeTotal}
|
||||
unit="einmalig gesamt"
|
||||
note={`${totalUnits} ${totalUnits === 1 ? "Wohnung" : "Wohnungen"} × ${formatEuro(rwmRatePerUnit)}\u00A0€ · ${install ? "inklusive Montage" : "Selbstmontage"}`}
|
||||
allocatable={false}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Wählen Sie in Schritt 1 mindestens eine Leistung, um eine
|
||||
Preisindikation zu erhalten.
|
||||
<p className="text-sm font-bold text-navy">
|
||||
{buildings.length} Gebäude · {totalUnits}{" "}
|
||||
{totalUnits === 1 ? "Wohnung" : "Wohnungen"}
|
||||
</p>
|
||||
<Separator className="my-5" />
|
||||
<div aria-live="polite">
|
||||
<p className="text-xs font-bold tracking-wide text-navy uppercase">
|
||||
Gewählte Leistungen
|
||||
</p>
|
||||
)}
|
||||
<p className="mt-6 text-xs leading-relaxed text-muted-foreground">
|
||||
{install
|
||||
? "Preise inklusive Montage durch SolenOS. "
|
||||
: "Preise ohne Montage – Installation durch Sie oder Ihren Fachbetrieb. "}
|
||||
Die erste Berechnung ist eine unverbindliche Preisindikation.
|
||||
Der genaue Preis ergibt sich aus der vollständigen
|
||||
Objektkonfiguration.
|
||||
<ul className="mt-3 space-y-3 text-sm">
|
||||
<li
|
||||
className={cn(
|
||||
"flex items-start gap-2",
|
||||
uvi ? "text-navy" : "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
{uvi ? (
|
||||
<Check className="mt-0.5 size-4 shrink-0 text-primary" />
|
||||
) : (
|
||||
<X className="mt-0.5 size-4 shrink-0" />
|
||||
)}
|
||||
<span>
|
||||
Monatliche Verbrauchsinformation
|
||||
{uvi ? " – enthalten" : " – nicht enthalten"}
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
className={cn(
|
||||
"flex items-start gap-2",
|
||||
billing ? "text-navy" : "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
{billing ? (
|
||||
<Check className="mt-0.5 size-4 shrink-0 text-primary" />
|
||||
) : (
|
||||
<X className="mt-0.5 size-4 shrink-0" />
|
||||
)}
|
||||
<span>
|
||||
Heizkostenabrechnung
|
||||
{billing ? " – enthalten" : " – nicht enthalten"}
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
className={cn(
|
||||
"flex items-start gap-2",
|
||||
rwm ? "text-navy" : "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
{rwm ? (
|
||||
<Check className="mt-0.5 size-4 shrink-0 text-primary" />
|
||||
) : (
|
||||
<X className="mt-0.5 size-4 shrink-0" />
|
||||
)}
|
||||
<span>
|
||||
Rauchwarnmelder
|
||||
{rwm ? " – enthalten" : " – nicht enthalten"}
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2 text-navy">
|
||||
<Check className="mt-0.5 size-4 shrink-0 text-primary" />
|
||||
<span>
|
||||
{install
|
||||
? "Montage durch SolenOS"
|
||||
: "Montage durch Sie oder Ihren Fachbetrieb"}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<Separator className="my-5" />
|
||||
<p className="text-sm font-bold text-navy">
|
||||
Geprüfter Gesamtpreis nach Detailkonfiguration
|
||||
</p>
|
||||
<p className="mt-2 text-xs leading-relaxed text-muted-foreground">
|
||||
Wir veröffentlichen keine ungeprüften Preiswerte. Nach der
|
||||
manuellen Prüfung erhalten Sie ein verbindliches
|
||||
Gesamtangebot mit der erforderlichen Gateway-Infrastruktur,
|
||||
den gewählten Leistungen und der gewählten Montagevariante.
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -641,22 +695,30 @@ export default function ConfiguratorPage() {
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Was ist in der Schätzung enthalten? */}
|
||||
{/* Vollständiger Leistungsumfang des späteren Gesamtangebots */}
|
||||
<Section className="pt-0 sm:pt-0 lg:pt-0">
|
||||
<SectionHeading
|
||||
title="Was ist in dem Preis enthalten?"
|
||||
/>
|
||||
<div className="mt-8 grid gap-6 lg:grid-cols-2">
|
||||
<SectionHeading title="Was gehört in das Gesamtangebot?" />
|
||||
<div className="mt-8 grid gap-6 lg:grid-cols-3">
|
||||
<PackageCard
|
||||
render={renders.gateway}
|
||||
title="UVI"
|
||||
title="Monatliche Verbrauchsinformation (UVI)"
|
||||
included={uvi}
|
||||
items={[
|
||||
"geschätzte benötigte Messtechnik",
|
||||
"SolenOS Plattform",
|
||||
"Datenerfassung",
|
||||
"Unterjährige Verbrauchsinformation",
|
||||
"laufender digitaler Betrieb",
|
||||
"von SolenOS ermittelte notwendige Messtechnik",
|
||||
"erforderliche Gateway- und Kommunikationsinfrastruktur",
|
||||
"SolenOS Plattform, Datenerfassung und laufender digitaler Betrieb",
|
||||
"monatliche Verbrauchswerte und Vergleiche für jede Wohnung",
|
||||
]}
|
||||
/>
|
||||
<PackageCard
|
||||
render={renders.glassMoney}
|
||||
title="Heizkostenabrechnung"
|
||||
included={billing}
|
||||
items={[
|
||||
"jährliche Heizkostenabrechnung im Online-Portal",
|
||||
"Verteilung nach den Vorgaben der HeizkostenV",
|
||||
"Berücksichtigung von Nutzerwechseln und Leerständen",
|
||||
"Pflichtangaben nach § 6a Absatz 3 HeizkostenV",
|
||||
]}
|
||||
/>
|
||||
<PackageCard
|
||||
@@ -664,12 +726,12 @@ export default function ConfiguratorPage() {
|
||||
title="Rauchwarnmelder"
|
||||
included={rwm}
|
||||
items={[
|
||||
"geschätzte Anzahl Rauchwarnmelder",
|
||||
"Verwaltung der Geräte",
|
||||
"gebuchte digitale Dienstleistungen",
|
||||
"von SolenOS ermittelte Anzahl Rauchwarnmelder",
|
||||
"Verwaltung und Ferninspektion der Geräte",
|
||||
"Prüf- und Statusdokumentation",
|
||||
install
|
||||
? "Montage und Eigentumsübertragung"
|
||||
: "Eigentumsübertragung – Montage durch Sie",
|
||||
? "Montage durch SolenOS im verbindlichen Gesamtangebot"
|
||||
: "Montage durch Sie oder Ihren Fachbetrieb",
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user