get main #423

Merged
IB-Cornelsen merged 7 commits from main into Jens 2025-03-06 17:45:54 +00:00
39 changed files with 1178 additions and 1062 deletions

View File

@@ -5,6 +5,7 @@ export const createCaller = createCallerFactory({
"klimafaktoren": await import("../src/pages/api/klimafaktoren.ts"),
"postleitzahlen": await import("../src/pages/api/postleitzahlen.ts"),
"unterlage": await import("../src/pages/api/unterlage.ts"),
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"),
"admin/ausstellen": await import("../src/pages/api/admin/ausstellen.ts"),
"admin/bestellbestaetigung": await import("../src/pages/api/admin/bestellbestaetigung.ts"),
"admin/erinnern": await import("../src/pages/api/admin/erinnern.ts"),
@@ -12,7 +13,6 @@ export const createCaller = createCallerFactory({
"admin/post-ausstellen": await import("../src/pages/api/admin/post-ausstellen.ts"),
"admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"),
"admin/stornieren": await import("../src/pages/api/admin/stornieren.ts"),
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"),
"ausweise": await import("../src/pages/api/ausweise/index.ts"),
"auth/access-token": await import("../src/pages/api/auth/access-token.ts"),
"auth/forgot-password": await import("../src/pages/api/auth/forgot-password.ts"),
@@ -30,11 +30,11 @@ export const createCaller = createCallerFactory({
"ticket": await import("../src/pages/api/ticket/index.ts"),
"user": await import("../src/pages/api/user/index.ts"),
"user/self": await import("../src/pages/api/user/self.ts"),
"verbrauchsausweis-gewerbe/[uid]": await import("../src/pages/api/verbrauchsausweis-gewerbe/[uid].ts"),
"verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/index.ts"),
"webhooks/mollie": await import("../src/pages/api/webhooks/mollie.ts"),
"verbrauchsausweis-wohnen/[uid]": await import("../src/pages/api/verbrauchsausweis-wohnen/[uid].ts"),
"verbrauchsausweis-wohnen": await import("../src/pages/api/verbrauchsausweis-wohnen/index.ts"),
"webhooks/mollie": await import("../src/pages/api/webhooks/mollie.ts"),
"verbrauchsausweis-gewerbe/[uid]": await import("../src/pages/api/verbrauchsausweis-gewerbe/[uid].ts"),
"verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/index.ts"),
"aufnahme/[uid]/bilder": await import("../src/pages/api/aufnahme/[uid]/bilder.ts"),
"aufnahme/[uid]": await import("../src/pages/api/aufnahme/[uid]/index.ts"),
"aufnahme/[uid]/unterlagen": await import("../src/pages/api/aufnahme/[uid]/unterlagen.ts"),

View File

@@ -4,7 +4,7 @@ import { exclude } from "#lib/exclude.js";
import Cookies from "js-cookie";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
import { AufnahmeClient, BedarfsausweisWohnenClient, BildClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient, } from "#components/Ausweis/types.js";
import { Enums } from "#lib/client/prisma";
import { Enums } from "#lib/client/prisma.js";
export async function ausweisSpeichern(
ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient,

View File

@@ -1,7 +1,22 @@
import { writable, Writable } from "svelte/store";
import { ZodEffects, ZodNullable, ZodOptional, ZodType } from "zod";
export function localStorageSync<T>(initial: T, name: string, modifier: (stored: any) => T = (stored) => JSON.parse(stored), reverseModifier: (value: T) => string = (value) => JSON.stringify(value)): Writable<T> {
/**
* Synchronisiert den ersten Writable in den zweiten.
*/
export function writableSync<T>(source: Writable<T>) {
let $val;
source.subscribe($ => $val = $)();
const target = writable<T>($val)
source.subscribe((value) => {
target.set(value);
})
return target;
}
export function localStorageSync<T>(initial: T, name: string, modifier: (stored: any) => T = (stored) => stored, reverseModifier: (value: T) => string = (value) => value as string): Writable<T> {
const stored = localStorage.getItem(name) as T
let value = initial;
if (stored) {
@@ -29,7 +44,7 @@ export function isZodInstanceOf<T extends ZodType<any>>(
if (schema instanceof ZodOptional || schema instanceof ZodNullable) {
return isZodInstanceOf(schema._def.innerType, targetType);
}else if (schema instanceof ZodEffects) {
return getZodBaseType(schema._def.schema)
return isZodInstanceOf(getZodBaseType(schema._def.schema), targetType)
}
return false;

View File

@@ -1,91 +0,0 @@
<script lang="ts">
import HelpLabel from "#components/labels/HelpLabel.svelte";
import Inputlabel from "#components/labels/InputLabel.svelte";
import { BenutzerClient } from "./types.js";
export let user: BenutzerClient;
</script>
<div id="ansprechpartner" class="bereich-box grid
grid-cols-1 gap-x-4 gap-y-8
sm:grid-cols-2 sm:gap-x-6 sm:gap-y-8
xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
">
<!-- Anrede * -->
<!--
<div class="input-standard order-3 md:order-3 xl:order-3">
<Inputlabel title="Anrede *"></Inputlabel>
<select name="anrede" bind:value={user.anrede}>
<option disabled selected>bitte auswählen</option>
<option value="Herr">Herr</option>
<option value="Frau">Frau</option>
</select>
</div> -->
<!-- Vorname * -->
<div class="input-standard order-1 md:order-1 xl:order-1">
<Inputlabel title="Vorname *"></Inputlabel>
<input
name="vorname"
type="text"
bind:value={user.vorname}
required
/>
<div class="help-label">
<HelpLabel>
Bitte Geben Sie hier den Vornamen des Ansprechpartners&nbsp;ein.
</HelpLabel>
</div>
</div>
<!-- Nachname * -->
<div class="input-standard order-2 md:order-2 xl:order-2">
<Inputlabel title="Nachname *"></Inputlabel>
<input
name="name"
type="text"
bind:value={user.name}
required
/>
<div class="help-label">
<HelpLabel>
Bitte Geben Sie hier den Nachnamen des Ansprechpartners&nbsp;ein.
</HelpLabel>
</div>
</div>
<!-- Telefon & Email * -->
<div class="input-standard order-3 md:order-3 xl:order-3">
<Inputlabel title="Telefonnummer *"></Inputlabel>
<input
name="telefon"
bind:value={user.telefon}
type="text"
/>
<div class="help-label">
<HelpLabel>
Bitte Geben Sie hier die Telefonnummer des Ansprechpartners&nbsp;ein.
</HelpLabel>
</div>
</div>
</div>

View File

@@ -48,7 +48,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required
data-cy="ausstellgrund"
>
<option disabled selected value={null}>Bitte auswählen</option>
<option disabled selected value>Bitte auswählen</option>
{#each Object.entries(Enums.Ausstellgrund) as [name, ausstellgrund]}
<option value={ausstellgrund}>{name}</option>
{/each}
@@ -72,7 +72,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required
bind:value={aufnahme.gebaeudetyp}
>
<option disabled selected value={null}>Bitte auswählen</option>
<option disabled selected value>Bitte auswählen</option>
{#if ausweisart==Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisWohnen || ausweisart === Enums.Ausweisart.BedarfsausweisWohnen}
<option value="Einfamilienhaus">Einfamilienhaus</option>
@@ -224,7 +224,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required
bind:value={aufnahme.saniert}
>
<option disabled selected value={null}>Bitte auswählen</option>
<option disabled selected value>Bitte auswählen</option>
<option value={true}>saniert</option>
<option value={false}>unsaniert</option>
</select>

View File

@@ -17,12 +17,18 @@
export let objekt: ObjektClient;
export let aufnahme: AufnahmeClient;
export let ausweisart: Enums.Ausweisart
export let form: HTMLFormElement;
export let showWeiter: boolean = true;
export let ausweistyp: AusweisTyp = Enums.AusweisTyp.Standard;
async function ausweisAbschicken() {
if (!form.checkValidity()) {
form.reportValidity()
return
}
openWindowWithPost("/kundendaten", {
ausweis,
objekt,
@@ -170,7 +176,7 @@ grid-cols-1 gap-x-2 gap-y-4
<hr class="m-0" />
<button class="button" on:click={hilfeBestellen}>jetzt Hilfe bestellen</button>
<button class="button" type="button" on:click={hilfeBestellen}>jetzt Hilfe bestellen</button>
<button
class="button absolute top-2 right-2 w-[30px] h-[30px] text-sm p-0"

View File

@@ -1,104 +1,105 @@
<script lang="ts">
import HelpLabel from "#components/labels/HelpLabel.svelte";
import Inputlabel from "#components/labels/InputLabel.svelte";
import ZipSearch from "#components/PlzSuche.svelte";
import { Enums } from "#lib/client/prisma.js"
import ZipSearch from "#components/PlzSuche.svelte";
import { Enums } from "#lib/client/prisma.js";
import { AufnahmeClient, ObjektClient } from "./types.js";
export let aufnahme: AufnahmeClient;
export let aufnahme: AufnahmeClient;
export let objekt: ObjektClient;
export let ausweisart: Enums.Ausweisart;
</script>
<div id="gebaeudedaten" class="bereich-box grid
<div
id="gebaeudedaten"
class="bereich-box grid
grid-cols-1 gap-x-4 gap-y-8
sm:grid-cols-2 sm:gap-x-6 sm:gap-y-8
xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
">
"
>
<!-- Straße, Hausnummer * -->
<!-- Straße, Hausnummer * -->
<div class="input-standard order-1 md:order-1 xl:order-1">
<Inputlabel title="Straße, Hausnummer *"></Inputlabel>
<div class="input-standard order-1 md:order-1 xl:order-1">
<Inputlabel title="Straße, Hausnummer *"></Inputlabel>
<input
name="adresse"
data-test="adresse"
type="text"
autocomplete="off"
required
data-msg-minlength="min. 5 Zeichen"
data-msg-maxlength="max. 40 Zeichen"
bind:value={objekt.adresse}
/>
<div class="help-label">
<HelpLabel>
Straße und Hausnummer des betrachteten Gebäudes. Die Angaen werden auf dem Energieausweis vermerkt.
</HelpLabel>
</div>
</div>
<!-- PLZ / ORT -->
<div class="grid grid-cols-[2fr_6fr] gap-x-4 order-2 md:order-2 xl:order-2 relative">
<div class="input-noHelp">
<Inputlabel title="PLZ *"></Inputlabel>
<ZipSearch
bind:zip={objekt.plz}
bind:city={objekt.ort}
name="plz"
/>
</div>
<div class="input-standard">
<Inputlabel title="Ort *"></Inputlabel>
<input
name="ort"
data-test="ort"
readonly={false}
bind:value={objekt.ort}
type="text"
<input
name="adresse"
data-test="adresse"
type="text"
autocomplete="off"
required
data-msg-minlength="min. 5 Zeichen"
data-msg-maxlength="max. 40 Zeichen"
bind:value={objekt.adresse}
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die PLZ des betrachteten Gebäudes ein, der Ort wird dann angezeigt und kann ausgewählt werden.
Die Angaen werden auf dem Energieausweis vermerkt.
</HelpLabel>
<HelpLabel>
Straße und Hausnummer des betrachteten Gebäudes. Die Angaen
werden auf dem Energieausweis vermerkt.
</HelpLabel>
</div>
</div>
</div>
<!-- PLZ / ORT -->
<div
class="grid grid-cols-[2fr_6fr] gap-x-4 order-2 md:order-2 xl:order-2 relative"
>
<div class="input-noHelp">
<Inputlabel title="PLZ *"></Inputlabel>
</div>
<ZipSearch
bind:zip={objekt.plz}
bind:city={objekt.ort}
name="plz"
/>
</div>
{#if ausweisart=="VerbrauchsausweisWohnen"}
<!-- Wohnfläche m² * -->
<div class="input-standard">
<Inputlabel title="Ort *"></Inputlabel>
<div class="input-standard order-3 md:order-3 xl:order-3">
<Inputlabel title="Wohnfläche m² *"></Inputlabel>
<input
name="ort"
data-test="ort"
readonly={false}
required
bind:value={objekt.ort}
type="text"
/>
<input
name="flaeche"
data-test="flaeche"
maxlength="4"
type="number"
required
autocomplete="off"
data-rule-minlength="2"
data-msg-minlength="min. 2 Zeichen"
bind:value={aufnahme.flaeche}
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die PLZ des betrachteten Gebäudes ein, der
Ort wird dann angezeigt und kann ausgewählt werden. Die
Angaen werden auf dem Energieausweis vermerkt.
</HelpLabel>
</div>
</div>
</div>
<div class="help-label">
{#if ausweisart == "VerbrauchsausweisWohnen"}
<!-- Wohnfläche m² * -->
<div class="input-standard order-3 md:order-3 xl:order-3">
<Inputlabel title="Wohnfläche m² *"></Inputlabel>
<input
name="flaeche"
data-test="flaeche"
maxlength="4"
type="number"
required
autocomplete="off"
data-rule-minlength="2"
data-msg-minlength="min. 2 Zeichen"
bind:value={aufnahme.flaeche}
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie hier die beheizte Wohnfläche in m² ein.
Dabei handelt es sich um die Wohnfläche abzüglich
@@ -106,91 +107,92 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
befinden. (Balkone, Terassen,etc.).
</HelpLabel>
</div>
</div>
</div>
{/if}
{/if}
<!-- Dachgeschoss *-->
<!-- Dachgeschoss *-->
<div class="input-standard order-4 md:order-2 xl:order-4">
<Inputlabel title="Dachgeschoss *"></Inputlabel>
<div class="input-standard order-4 md:order-2 xl:order-4">
<Inputlabel title="Dachgeschoss *"></Inputlabel>
<select
name="dachgeschoss"
data-test="dachgeschoss"
bind:value={aufnahme.dachgeschoss}
required
>
<option disabled selected value={null}>Bitte auswählen</option>
<option value={Enums.Heizungsstatus.NICHT_VORHANDEN}>nicht vorhanden</option>
<select
name="dachgeschoss"
data-test="dachgeschoss"
bind:value={aufnahme.dachgeschoss}
required
>
<option disabled selected value>Bitte auswählen</option>
<option value={Enums.Heizungsstatus.NICHT_VORHANDEN}
>nicht vorhanden</option
>
<option value={Enums.Heizungsstatus.UNBEHEIZT}>unbeheizt</option>
<option value={Enums.Heizungsstatus.BEHEIZT}>beheizt</option>
</select>
<div class="help-label">
<HelpLabel>
Bei Obergeschoss mit vorhandenen Dachschrägen oder ausgebautem Dachgeschoss beheizt auswählen.
Bei Kaltdächern oder unausgebautem Dachgeschoss bitte unbeheizt auswählen. Bei Flachdächern wird nicht vorhanden ausgewählt.
</HelpLabel>
</div>
</div>
<!-- Keller * -->
<div class="input-standard order-4 md:order-2 xl:order-4">
<Inputlabel title="Keller *"></Inputlabel>
<select
name="keller"
data-test="keller"
required
bind:value={aufnahme.keller}
>
<option disabled selected value={false}>Bitte auswählen</option>
<option value={Enums.Heizungsstatus.NICHT_VORHANDEN}>nicht vorhanden</option>
<option value={Enums.Heizungsstatus.UNBEHEIZT}>unbeheizt</option>
<option value={Enums.Heizungsstatus.BEHEIZT}>beheizt</option>
</select>
<div class="help-label">
<HelpLabel>
Diese Angabe hat folgenden Einfluss.<br>
<b>Keller beheizt:<br>Nutzfläche = Wohnfläche x 1,35.</b>
<b>Keller unbeheizt:<br>Nutzfläche = Wohnfläche x 1,2.</b>
Dies ist nur zulässig wenn die Nutzfläche unbekannt ist. Geben Sie diese daher unbedingt im nächsten Feld ein wenn bekannt.
</HelpLabel>
</div>
</div>
<!-- Gesamtfläche * -->
<div class="input-standard order-6 md:order-6 xl:order-6">
<Inputlabel title="Nutzfläche m² *"></Inputlabel>
<input
name="nutzflaeche"
data-test="nutzflaeche"
maxlength="4"
type="number"
required
autocomplete="off"
data-rule-minlength="2"
data-msg-minlength="min. 2 Zeichen"
bind:value={aufnahme.nutzflaeche}
/>
</select>
<div class="help-label">
<HelpLabel>
Bitte geben Sie hier die beheizte Gesamtfläche in m² ein
(wenn bekannt). Dabei handelt es sich um die Wohnfläche +
weiterer Flächen innerhalb des Gebäudes (z.B. Fläche des
beheizten Kellers). <b>Diese Fläche wird dann im Energieausweis
als energetische Nutzfläche (An) ausgewiesen.</b>
</HelpLabel>
</div>
<HelpLabel>
Bei Obergeschoss mit vorhandenen Dachschrägen oder ausgebautem
Dachgeschoss beheizt auswählen. Bei Kaltdächern oder
unausgebautem Dachgeschoss bitte unbeheizt auswählen. Bei
Flachdächern wird nicht vorhanden ausgewählt.
</HelpLabel>
</div>
</div>
<!-- Keller * -->
<div class="input-standard order-4 md:order-2 xl:order-4">
<Inputlabel title="Keller *"></Inputlabel>
<select
name="keller"
data-test="keller"
required
bind:value={aufnahme.keller}
>
<option disabled selected value>Bitte auswählen</option>
<option value={Enums.Heizungsstatus.NICHT_VORHANDEN}
>nicht vorhanden</option
>
<option value={Enums.Heizungsstatus.UNBEHEIZT}>unbeheizt</option>
<option value={Enums.Heizungsstatus.BEHEIZT}>beheizt</option>
</select>
<div class="help-label">
<HelpLabel>
Diese Angabe hat folgenden Einfluss.<br />
<b>Keller beheizt:<br />Nutzfläche = Wohnfläche x 1,35.</b>
<b>Keller unbeheizt:<br />Nutzfläche = Wohnfläche x 1,2.</b>
Dies ist nur zulässig wenn die Nutzfläche unbekannt ist. Geben Sie
diese daher unbedingt im nächsten Feld ein wenn bekannt.
</HelpLabel>
</div>
</div>
<!-- Gesamtfläche * -->
<div class="input-standard order-6 md:order-6 xl:order-6">
<Inputlabel title="Nutzfläche m² *"></Inputlabel>
<input
name="nutzflaeche"
data-test="nutzflaeche"
maxlength="4"
type="number"
required
bind:value={aufnahme.nutzflaeche}
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie hier die beheizte Gesamtfläche in m² ein (wenn
bekannt). Dabei handelt es sich um die Wohnfläche + weiterer
Flächen innerhalb des Gebäudes (z.B. Fläche des beheizten
Kellers). <b
>Diese Fläche wird dann im Energieausweis als energetische
Nutzfläche (An) ausgewiesen.</b
>
</HelpLabel>
</div>
</div>
</div>
</div>

View File

@@ -1,137 +1,123 @@
<script lang="ts">
import HelpLabel from "#components/labels/HelpLabel.svelte";
import Inputlabel from "#components/labels/InputLabel.svelte";
import { AufnahmeClient } from "./types.js";
export let aufnahme: AufnahmeClient;
export let aufnahme: AufnahmeClient;
</script>
<div id="lueftungundleerstand" class="bereich-box grid
<div
id="lueftungundleerstand"
class="bereich-box grid
grid-cols-1 gap-x-4 gap-y-8
sm:grid-cols-2 sm:gap-x-6 sm:gap-y-8
xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
">
"
>
<!-- Gebäudeteil -->
<div class="grid grid-cols-2 gap-x-4 order-2 md:order-2 xl:order-2">
<div class="input-standard">
<Inputlabel title="Gebäudeteil *"></Inputlabel>
<select
name="gebaeudeteil"
data-test="gebaeudeteil"
bind:value={aufnahme.gebaeudeteil}
required
>
<option disabled selected value>Bitte auswählen</option>
<option value="Gesamtgebäude">Gesamtgebäude</option>
<option value="Wohnen">Wohnen</option>
</select>
<!-- Gebäudeteil -->
<div class="help-label">
<HelpLabel>
Bitte geben Sie hier den Gebäudeteil ein. In den meisten
Fällen handelt es sich um das Gesamtgebäude. Sollte es sich
allerdings um ein Gebäude mit mehr als 10% Gewerbeanteil
handeln, so sollten 2 Ausweise erstellt werden. In diesem
Fall wählen Sie Gebäudeteil 'Wohnen' bzw. Gebäudeteil
'Gewerbe'.
</HelpLabel>
</div>
</div>
<div class="grid grid-cols-2 gap-x-4 order-2 md:order-2 xl:order-2">
<div class="input-standard">
<Inputlabel title="Leerstand in %"></Inputlabel>
<div class="input-standard">
<Inputlabel title="Gebäudeteil *"></Inputlabel>
<select
name="gebaeudeteil"
data-test="gebaeudeteil"
bind:value={aufnahme.gebaeudeteil}
required
>
<option disabled selected value={null}>Bitte auswählen</option>
<option value="Gesamtgebäude">Gesamtgebäude</option>
<option value="Wohnen">Wohnen</option>
</select>
<div class="help-label">
<HelpLabel>
Bitte geben Sie hier den Gebäudeteil ein. In den meisten
Fällen handelt es sich um das Gesamtgebäude. Sollte es sich
allerdings um ein Gebäude mit mehr als 10% Gewerbeanteil
handeln, so sollten 2 Ausweise erstellt werden. In diesem
Fall wählen Sie Gebäudeteil 'Wohnen' bzw. Gebäudeteil
'Gewerbe'.
</HelpLabel>
</div>
</div>
<input
name="leerstand"
data-test="leerstand"
maxlength="2"
type="number"
bind:value={aufnahme.leerstand}
/>
<div class="input-standard">
<Inputlabel title="Leerstand in %"></Inputlabel>
<input
name="leerstand"
data-test="leerstand"
maxlength="2"
type="number"
bind:value={aufnahme.leerstand}
/>
<div class="help-label">
<div class="help-label">
<HelpLabel>
Bitte geben Sie hier den Leerstand in % des Gesamtzeitraumes
(3 Jahre) ein. Zum Beispiel 4 Monate Leerstand in 36 Monaten
wären dann ca. 11%.
</HelpLabel>
</div>
</div>
</div>
</div>
</div>
<!-- Lüftung -->
<!-- Lüftung -->
<div class="input-standard order-2 md:order-2 xl:order-2">
<Inputlabel title="Lüftung durch *"></Inputlabel>
<div class="input-standard order-2 md:order-2 xl:order-2">
<Inputlabel title="ftung durch *"></Inputlabel>
<select
name="lueftung"
data-test="lueftung"
required
bind:value={aufnahme.lueftung}
>
<option disabled selected value={null}>Bitte auswählen</option>
<option value="Fensterlueftung">Fensterlüftung</option>
<option value="Schachtlueftung">Schachtlüftung</option>
<option value="LueftungsanlageOhneWaermerueckgewinnung"
>Lüftungsanlage ohne Wärmerückgewinnung</option
>
<option value="LueftungsanlageMitWaermerueckgewinnung"
>Lüftungsanlage mit Wärmerückgewinnung</option
>
</select>
<select
name="lueftung"
data-test="lueftung"
required
bind:value={aufnahme.lueftung}
>
<option disabled selected value>Bitte auswählen</option>
<option value="Fensterlueftung">Fensterlüftung</option>
<option value="Schachtlueftung">Schachtlüftung</option>
<option value="LueftungsanlageOhneWaermerueckgewinnung"
>Lüftungsanlage ohne Wärmerückgewinnung</option
>
<option value="LueftungsanlageMitWaermerueckgewinnung"
>Lüftungsanlage mit Wärmerückgewinnung</option
>
</select>
<div class="help-label">
<HelpLabel>
Bitte geben Sie hier den Gebäudeteil ein. In den meisten
Fällen handelt es sich um das Gesamtgebäude. Sollte es sich
allerdings um ein Gebäude mit mehr als 10% Gewerbeanteil
Bitte geben Sie hier ein ob über die Fenster natürlich
belüftet wird oder über eine Lüftungsanlage.
</HelpLabel>
</div>
</div>
<HelpLabel>
Bitte geben Sie hier den Gebäudeteil ein. In den meisten Fällen
handelt es sich um das Gesamtgebäude. Sollte es sich allerdings
um ein Gebäude mit mehr als 10% Gewerbeanteil Bitte geben Sie
hier ein ob über die Fenster natürlich belüftet wird oder über
eine Lüftungsanlage.
</HelpLabel>
</div>
</div>
<!-- Kühlung -->
<!-- Kühlung -->
<div class="input-standard order-2 md:order-2 xl:order-2">
<Inputlabel title="Anlage Kühlung *"></Inputlabel>
<div class="input-standard order-2 md:order-2 xl:order-2">
<Inputlabel title="Anlage Kühlung *"></Inputlabel>
<select
name="kuehlung"
data-test="kuehlung"
required
bind:value={aufnahme.kuehlung}
>
<option disabled selected value={null}>Bitte auswählen</option>
<option value="1">vorhanden</option>
<option value="0">nicht vorhanden</option>
</select>
<select
name="kuehlung"
data-test="kuehlung"
required
bind:value={aufnahme.kuehlung}
>
<option disabled selected value>Bitte auswählen</option>
<option value="1">vorhanden</option>
<option value="0">nicht vorhanden</option>
</select>
<div class="help-label">
<HelpLabel>
Bitte geben Sie an ob das Gebäude im Sommer zusätzlich
gekühlt wird.
</HelpLabel>
</div>
<HelpLabel>
Bitte geben Sie an ob das Gebäude im Sommer zusätzlich gekühlt
wird.
</HelpLabel>
</div>
</div>
</div>
</div>

View File

@@ -1,297 +0,0 @@
<script lang="ts">
import HelpLabel from "#components/labels/HelpLabel.svelte";
import Inputlabel from "#components/labels/InputLabel.svelte";
import ZipSearch from "#components/PlzSuche.svelte";
import { BenutzerClient, RechnungClient } from "./types.js";
export let rechnung: Partial<RechnungClient>;
$: {
if (!rechnung.abweichende_versand_adresse) {
rechnung.versand_empfaenger = rechnung.empfaenger
rechnung.versand_ort = rechnung.ort
rechnung.versand_plz = rechnung.plz
rechnung.versand_strasse = rechnung.strasse
rechnung.versand_zusatzzeile = rechnung.zusatzzeile
}
}
</script>
<div
id="rechnungsadresse"
class="bereich-box grid
grid-cols-1 gap-x-4 gap-y-8
sm:grid-cols-2 sm:gap-x-6 sm:gap-y-8
xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
">
<!-- Empfänger * -->
<div class="input-standard order-1 md:order-1 xl:order-1">
<Inputlabel title="Rechnungs-Empfänger *"></Inputlabel>
<input
name="rechnung_empfaenger"
type="text"
bind:value={rechnung.empfaenger}
required
data-rule-maxlength="100"
data-msg-maxlength="max. 100 Zeichen"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie den Empfänger ein, auf den die Rechnung
ausgestellt wird.
</HelpLabel>
</div>
</div>
<!-- Straße, Hausnummer * -->
<div class="input-standard order-2 md:order-2 xl:order-2">
<Inputlabel title="Straße, Hausnummer *"></Inputlabel>
<input
name="rechnung_strasse"
bind:value={rechnung.strasse}
type="text"
required
data-rule-maxlength="40"
data-msg-maxlength="max. 40 Zeichen"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die Strasse und Hausnummer, so wie Sie auf der
Rechnung erscheinen soll, ein.
</HelpLabel>
</div>
</div>
<!-- PLZ / ORT -->
<div class="grid grid-cols-[2fr_4fr] gap-x-4 order-3 md:order-3 xl:order-3">
<div class="input-noHelp">
<Inputlabel title="PLZ *"></Inputlabel>
<ZipSearch
name="rechnung_plz"
bind:zip={rechnung.plz}
bind:city={rechnung.ort}
/>
</div>
<div class="input-standard">
<Inputlabel title="Ort *"></Inputlabel>
<input
name="rechnung_ort"
type="text"
required
bind:value={rechnung.ort}
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die PLZ des Ortes, so wie Sie auf der
Rechnung erscheinen soll, ein.
</HelpLabel>
</div>
</div>
</div>
<!-- Zusatzzeile -->
<div class="input-standard order-4 md:order-4 xl:order-4">
<Inputlabel title="Zusatzzeile"></Inputlabel>
<input
name="rechnung_zusatzzeile"
bind:value={rechnung.zusatzzeile}
type="text"
data-rule-maxlength="80"
data-msg-maxlength="max. 80 Zeichen"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie, falls erforderlich, zusätzliche nformationen
ein.
</HelpLabel>
</div>
</div>
<!-- E-mail -->
<div class="input-standard order-5 md:order-5 xl:order-5">
<Inputlabel title="E-mail *"></Inputlabel>
<input name="rechnung_email" bind:value={rechnung.email} type="email" />
<div class="help-label">
<HelpLabel>
Bitte geben Sie die E-Mail Adresse des Rechnungsempfängers ein.
</HelpLabel>
</div>
</div>
<div class="sm:col-span-3 order-7 md:order-7 xl:order-7">
<div
class="grid grid-cols-[25px_max-content] items-center justify-items-start"
>
<input
id="abweichende_versand_adresse"
class="w-[15px] h-[15px]"
type="checkbox"
name="abweichende_versand_adresse"
bind:checked={rechnung.abweichende_versand_adresse}
/>
<label for="abweichende_versand_adresse" class="cursor-pointer"
>abweichende Versandadresse</label
>
</div>
</div>
{#if rechnung.abweichende_versand_adresse}
<!-- Versand Empfänger * -->
<div class="input-standard order-8 md:order-8 xl:order-8">
<Inputlabel title="Versand-Empfänger *"></Inputlabel>
<input
name="versand_empfaenger"
type="text"
readonly={!rechnung.abweichende_versand_adresse}
bind:value={rechnung.versand_empfaenger}
required
data-rule-maxlength="100"
data-msg-maxlength="max. 100 Zeichen"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie den Namen des Versand-Empfängers ein.
</HelpLabel>
</div>
</div>
<!-- Versand Straße, Hausnummer * -->
<div class="input-standard order-9 md:order-9 xl:order-9">
<Inputlabel title="Straße, Hausnummer *"></Inputlabel>
<input
name="versand_strasse"
type="text"
readonly={!rechnung.abweichende_versand_adresse}
bind:value={rechnung.versand_strasse}
required
data-rule-maxlength="40"
data-msg-maxlength="max. 40 Zeichen"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die Versand-Empfänger Strasse und Hausnummer
ein, an die die Rechnung versandt wird.
</HelpLabel>
</div>
</div>
<!-- PLZ / ORT -->
<div
class="grid grid-cols-[2fr_4fr] gap-x-4 order-10 md:order-10 xl:order-10"
>
<div class="input-noHelp">
<Inputlabel title="PLZ *"></Inputlabel>
<ZipSearch
name="versand_plz"
readonly={!rechnung.abweichende_versand_adresse}
bind:zip={rechnung.versand_plz}
bind:city={rechnung.versand_ort}
/>
</div>
<div class="input-standard">
<Inputlabel title="Ort *"></Inputlabel>
<input
name="versand_ort"
type="text"
readonly
required
bind:value={rechnung.versand_ort}
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die Versand-Empfänger PLZ des Ortes ein,
an den die Rechnung versandt wird.
</HelpLabel>
</div>
</div>
</div>
<!-- Zusatzzeile -->
<div class="input-standard order-11 md:order-11 xl:order-11">
<Inputlabel title="Zusatzzeile"></Inputlabel>
<input
name="versand_zusatzzeile"
type="text"
readonly={!rechnung.abweichende_versand_adresse}
bind:value={rechnung.versand_zusatzzeile}
data-rule-maxlength="80"
data-msg-maxlength="max. 80 Zeichen"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie, falls erforderlich, zusätzliche
nformationen ein.
</HelpLabel>
</div>
</div>
<!-- E-mail -->
<div class="input-standard order-12 md:order-12 xl:order-12">
<Inputlabel title="E-mail *"></Inputlabel>
<input
name="rechnung_email"
bind:value={rechnung.email}
type="email"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die E-Mail Adresse des Versand-Empfängers
ein.
</HelpLabel>
</div>
</div>
<!-- Telefon
<div class="input-standard order-[13] md:order-[13] xl:order-[13]">
<Inputlabel title="Telefon *"></Inputlabel>
<input
name="rechnung_telefon"
bind:value={rechnung.telefon}
type="text"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die Telefonnummer des Versand-Empfängers ein.
</HelpLabel>
</div>
</div>
-->
{/if}
</div>

View File

@@ -236,7 +236,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
bind:value={aufnahme.brennstoff_1}
required
>
<option disabled selected >Bitte auswählen</option>
<option disabled selected value>Bitte auswählen</option>
{#each Object.keys(fuelMap) as fuel}
<option value={fuel}>{fuel}</option>
{/each}
@@ -276,7 +276,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
disabled={!aufnahme.brennstoff_1}
required
>
<option disabled selected>Bitte auswählen</option>
<option disabled selected value>Bitte auswählen</option>
{#each fuelMap.hasOwnProperty(aufnahme.brennstoff_1) ? fuelMap[aufnahme.brennstoff_1] : [] as unit}
<option value={unit}>{unit}</option>
{/each}

View File

@@ -1,203 +1,188 @@
<script lang="ts">
import HelpLabel from "#components/labels/HelpLabel.svelte";
import Inputlabel from "#components/labels/InputLabel.svelte";
import HelpLabel from "#components/labels/HelpLabel.svelte";
import Inputlabel from "#components/labels/InputLabel.svelte";
export let ausweis;
export let ausweis;
$: {
if (!ausweis.warmwasser_enthalten) {
ausweis.warmwasser_anteil_bekannt = false;
$: {
if (!ausweis.warmwasser_enthalten) {
ausweis.warmwasser_anteil_bekannt = false;
}
}
}
</script>
<div id="warmwasser" class="bereich-box grid
<div
id="warmwasser"
class="bereich-box grid
grid-cols-1 gap-x-4 gap-y-8
sm:grid-cols-2 sm:gap-x-6 sm:gap-y-8
xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
">
"
>
<div class="input-standard order-1 md:order-1 xl:order-1">
<Inputlabel title="Warmwasser im Verbrauch enthalten"></Inputlabel>
<Inputlabel title="Warmwasser im Verbrauch enthalten"></Inputlabel>
<div class="input-row items-center gap-2">
<div class="grid grid-cols-[25px_max-content] items-center justify-items-start">
<input
id="warmwasser_enthalten"
type="checkbox"
class="checkbox"
name="warmwasser_enthalten"
data-test="warmwasser_enthalten"
bind:checked={ausweis.warmwasser_enthalten}
/>
<label for="warmwasser_enthalten" class="cursor-pointer">Ja</label>
</div>
<div class="grid grid-cols-[25px_max-content] items-center justify-items-start">
<input
id="warmwasser_anteil_bekannt"
type="checkbox"
class="checkbox"
name="warmwasser_anteil_bekannt"
data-test="warmwasser_anteil_bekannt"
bind:checked={ausweis.warmwasser_anteil_bekannt}
disabled={!ausweis.warmwasser_enthalten}
/>
<label for="warmwasser_anteil_bekannt" class="cursor-pointer">Anteil bekannt</label>
</div>
</div>
<div class="help-label">
<HelpLabel>
TEXT FEHLT
</HelpLabel>
</div>
</div>
<!-- % Anteil Warmwasser -->
<div class="input-standard order-2 md:order-2 xl:order-2">
<Inputlabel title="% Anteil Warmwasser"></Inputlabel>
<input
name="anteil_warmwasser_1"
data-test="anteil_warmwasser_1"
maxlength="2"
type="number"
bind:value={ausweis.anteil_warmwasser_1}
disabled={!ausweis.warmwasser_anteil_bekannt ||
!ausweis.warmwasser_enthalten}
autocomplete="off"
/>
<div class="help-label">
<HelpLabel>
Wenn bekannt geben Sie den Anteil der Warmwasser-Versorgung
hier ein. Standardmäßig wird ein Anteil von 18% angenommen.
</HelpLabel>
<div class="input-row items-center gap-2">
<div
class="grid grid-cols-[25px_max-content] items-center justify-items-start"
>
<input
id="warmwasser_enthalten"
type="checkbox"
class="checkbox"
name="warmwasser_enthalten"
data-test="warmwasser_enthalten"
bind:checked={ausweis.warmwasser_enthalten}
/>
<label for="warmwasser_enthalten" class="cursor-pointer"
>Ja</label
>
</div>
</div>
<!--zusätzliche Heizquelle -->
<div class="input-standard order-3 md:order-3 xl:order-3">
<Inputlabel title="zusätzliche Heizquelle"></Inputlabel>
<input
name="anteil_warmwasser_2"
data-test="anteil_warmwasser_2"
maxlength="3"
type="number"
autocomplete="off"
bind:value={ausweis.anteil_warmwasser_2}
disabled={!ausweis.zusaetzliche_heizquelle ||
!ausweis.warmwasser_anteil_bekannt ||
!ausweis.warmwasser_enthalten}
/>
<div
class="grid grid-cols-[25px_max-content] items-center justify-items-start"
>
<input
id="warmwasser_anteil_bekannt"
type="checkbox"
class="checkbox"
name="warmwasser_anteil_bekannt"
data-test="warmwasser_anteil_bekannt"
bind:checked={ausweis.warmwasser_anteil_bekannt}
disabled={!ausweis.warmwasser_enthalten}
/>
<label for="warmwasser_anteil_bekannt" class="cursor-pointer"
>Anteil bekannt</label
>
</div>
</div>
<div class="help-label">
<HelpLabel>
Wenn bekannt geben Sie den Anteil der Warmwasser-Versorgung
von der zusätzlichen Heizquelle hier ein. Standardmäßig wird
ein Anteil von 18% angenommen.
</HelpLabel>
<HelpLabel>TEXT FEHLT</HelpLabel>
</div>
</div>
<!-- % Anteil Warmwasser -->
<div class="input-standard order-2 md:order-2 xl:order-2">
<Inputlabel title="% Anteil Warmwasser"></Inputlabel>
<input
name="anteil_warmwasser_1"
data-test="anteil_warmwasser_1"
maxlength="2"
type="number"
bind:value={ausweis.anteil_warmwasser_1}
disabled={!ausweis.warmwasser_anteil_bekannt ||
!ausweis.warmwasser_enthalten}
autocomplete="off"
/>
<div class="help-label">
<HelpLabel>
Wenn bekannt geben Sie den Anteil der Warmwasser-Versorgung hier
ein. Standardmäßig wird ein Anteil von 18% angenommen.
</HelpLabel>
</div>
</div>
<!--zusätzliche Heizquelle -->
<div class="input-standard order-3 md:order-3 xl:order-3">
<Inputlabel title="zusätzliche Heizquelle"></Inputlabel>
<input
name="anteil_warmwasser_2"
data-test="anteil_warmwasser_2"
maxlength="3"
type="number"
autocomplete="off"
bind:value={ausweis.anteil_warmwasser_2}
disabled={!ausweis.zusaetzliche_heizquelle ||
!ausweis.warmwasser_anteil_bekannt ||
!ausweis.warmwasser_enthalten}
/>
<div class="help-label">
<HelpLabel>
Wenn bekannt geben Sie den Anteil der Warmwasser-Versorgung von
der zusätzlichen Heizquelle hier ein. Standardmäßig wird ein
Anteil von 18% angenommen.
</HelpLabel>
</div>
</div>
<!--Alternative Energieversorgungssyteme -->
<div
class="input-standard order-4 md:order-4 xl:order-4 sm:col-span-2 items-center"
>
<Inputlabel title="Alternative Energieversorgungssyteme"></Inputlabel>
<div class="input-row items-center gap-2 lg:gap-20 xl:gap-24">
<div class="grid grid-cols-[max-content_40px] gap-2 items-center">
<input
id="alternative_heizung"
type="checkbox"
name="alternative_heizung"
data-test="alternative_heizung"
bind:checked={ausweis.alternative_heizung}
value="Heizung"
/>
<label
for="alternative_heizung"
class="checkbox-inline cursor-pointer">Heizung</label
>
</div>
<div class="grid grid-cols-[max-content_40px] gap-2 items-center">
<input
id="alternative_warmwasser"
type="checkbox"
name="alternative_warmwasser"
data-test="alternative_warmwasser"
bind:checked={ausweis.alternative_warmwasser}
value="Warmwasser"
/>
<label
for="alternative_warmwasser"
class="checkbox-inline cursor-pointer">Warmwasser</label
>
</div>
<div class="grid grid-cols-[max-content_40px] gap-2 items-center">
<input
id="alternative_lueftung"
type="checkbox"
name="alternative_lueftung"
data-test="alternative_lueftung"
bind:checked={ausweis.alternative_lueftung}
value="Lüftung"
/>
<label
for="alternative_lueftung"
class="checkbox-inline cursor-pointer">Lüftung</label
>
</div>
<div class="grid grid-cols-[max-content_40px] gap-2 items-center">
<input
id="alternative_kuehlung"
type="checkbox"
name="alternative_kuehlung"
data-test="alternative_kuehlung"
bind:checked={ausweis.alternative_kuehlung}
value="Kühlung"
/>
<label
for="alternative_kuehlung"
class="checkbox-inline cursor-pointer">Kühlung</label
>
</div>
</div>
<div class="help-label">
<HelpLabel></HelpLabel>
</div>
</div>
</div>
<!--Alternative Energieversorgungssyteme -->
<div class="input-standard order-4 md:order-4 xl:order-4 sm:col-span-2 items-center">
<Inputlabel title="Alternative Energieversorgungssyteme"></Inputlabel>
<div class="input-row items-center gap-2 lg:gap-20 xl:gap-24">
<div class="grid grid-cols-[max-content_40px] gap-2 items-center">
<input
id="alternative_heizung"
type="checkbox"
name="alternative_heizung"
data-test="alternative_heizung"
bind:checked={ausweis.alternative_heizung}
value="Heizung"
/>
<label for="alternative_heizung" class="checkbox-inline cursor-pointer">Heizung</label>
</div>
<div class="grid grid-cols-[max-content_40px] gap-2 items-center">
<input
id="alternative_warmwasser"
type="checkbox"
name="alternative_warmwasser"
data-test="alternative_warmwasser"
bind:checked={ausweis.alternative_warmwasser}
value="Warmwasser"
/>
<label for="alternative_warmwasser" class="checkbox-inline cursor-pointer">Warmwasser</label>
</div>
<div class="grid grid-cols-[max-content_40px] gap-2 items-center">
<input
id="alternative_lueftung"
type="checkbox"
name="alternative_lueftung"
data-test="alternative_lueftung"
bind:checked={ausweis.alternative_lueftung}
value="Lüftung"
/>
<label for="alternative_lueftung" class="checkbox-inline cursor-pointer">Lüftung</label>
</div>
<div class="grid grid-cols-[max-content_40px] gap-2 items-center">
<input
id="alternative_kuehlung"
type="checkbox"
name="alternative_kuehlung"
data-test="alternative_kuehlung"
bind:checked={ausweis.alternative_kuehlung}
value="Kühlung"
/>
<label for="alternative_kuehlung" class="checkbox-inline cursor-pointer">Kühlung</label>
</div>
</div>
<div class="help-label">
<HelpLabel>
</HelpLabel>
</div>
</div>
</div>

View File

@@ -12,7 +12,7 @@ import {
VerbrauchsausweisGewerbe,
VerbrauchsausweisWohnen,
GEGNachweisWohnen,
} from "#lib/client/prisma";
} from "#lib/client/prisma.js";
import { z, ZodSchema } from "zod";
export type OmitKeys<T, K extends keyof T> = Omit<T, K>;
@@ -101,7 +101,7 @@ export type TicketClient = OmitKeys<Tickets, "bearbeiter_id" | "benutzer_id" | "
export type BenutzerClient = OmitKeys<Benutzer, "id" | "passwort">;
export type RechnungClient = OmitKeys<Rechnung, "aufnahme_id" | "benutzer_id" | "id">
export type RechnungClient = OmitKeys<Rechnung, "id">
export function ZodOverlap<T, S = z.ZodType<T, z.ZodTypeDef, T>>(arg: S): S {
return arg;

View File

@@ -1,15 +1,13 @@
<script lang="ts">
import { ripple } from "svelte-ripple-action";
import type { RippleOptions } from "svelte-ripple-action/dist/constants.js";
import { Home, Reader, EnvelopeClosed, Cube, Bell, Gear, LockClosed, HamburgerMenu, CaretDown } from "radix-svelte-icons"
import { Reader, Bell, Gear, LockClosed, CaretDown } from "radix-svelte-icons"
import NotificationProvider from "#components/NotificationProvider/NotificationProvider.svelte";
import DashboardNotification from "./DashboardNotification.svelte";
import { notifications } from "#components/NotificationProvider/shared.js";
import ThemeController from "#components/ThemeController.svelte";
import { BenutzerClient } from "#components/Ausweis/types.js";
import Cross1 from "radix-svelte-icons/src/lib/icons/Cross1.svelte";
import { flex } from "#lib/pdf/elements/index.js";
export let lightTheme: boolean;
export let benutzer: BenutzerClient;
@@ -21,7 +19,7 @@
let headerOpen = false;
</script>
<aside class:hidden={!headerOpen} class="fixed left-0 top-16 w-full h-[calc(100%-4rem)] flex z-30 md:relative md:h-auto md:w-auto md:top-0 md:flex bg-base-200 border-r border-r-base-300 flex-col py-4">
<aside class:hidden={!headerOpen} class="fixed left-0 top-16 w-full h-[calc(100%-4rem)] flex md:relative md:h-auto md:w-auto md:top-0 md:flex bg-base-200 border-r border-r-base-300 flex-col py-4">
<div class="flex flex-row items-center px-4">

View File

@@ -47,7 +47,7 @@
<div use:clickOutside={() => {
hideZipDropdown = true;
}}>
}} class="relative">
<input
name={name}
@@ -55,6 +55,7 @@
type="text"
required
readonly={readonly}
{...$$restProps}
bind:value={zip}
on:input={fetchZipCodeInformation}
on:focus={() => {

View File

@@ -6,25 +6,28 @@
async function showTicketPopup() {
const success = await dialogs.modal(TicketPopup);
if (success) {
console.log(success);
if (success === true) {
dialogs.alert({
title: "Ticket erstellt",
text: "Ihr Support Ticket wurde erfolgreich erstellt. Wir werden uns schnellstmöglich um ihre Angelegenheit kümmern. Vielen Dank für ihre Geduld.",
dismissButtonText: "Schließen",
dismissButtonClass: "btn btn-primary",
dialogClass: "modal-box",
dismissButtonClass: "button",
dialogClass: "bg-white rounded-lg p-4",
headerClass: "bg-base-100 text-center",
titleClass: "text-base-content text-xl font-medium",
dividerClass: "hidden",
footerClass: "bg-base-100 justify-center gap-4 mt-4",
});
} else {
} else if (success === false) {
dialogs.alert({
title: "Ticket erstellen fehlgeschlagen",
text: "Leider ist beim erstellen des Tickets ein Fehler aufgetreten. Bitte versuchen sie es später erneut oder kontaktieren sie uns direkt per email unter info@ib-cornelsen.de.",
dismissButtonText: "Schließen",
dismissButtonClass: "btn btn-error",
dialogClass: "modal-box",
dismissButtonClass: "button",
dialogClass: "bg-white rounded-lg p-4",
headerClass: "bg-base-100 text-center",
titleClass: "text-base-content text-xl font-medium",
dividerClass: "hidden",
@@ -35,7 +38,7 @@
</script>
<button
class="btn btn-primary fixed bottom-0 right-8 rounded-b-none rounded-t-xl w-48 h-12 text-xl hover:h-14 transition-all"
class="fixed bottom-0 right-8 rounded-b-none rounded-t-xl w-48 h-12 text-xl hover:h-14 transition-all bg-primary text-primary-content font-bold"
on:click={showTicketPopup}
>Support Ticket</button
>

View File

@@ -44,9 +44,7 @@
<h4>Kategorie *</h4>
<select class="select select-bordered" bind:value={category}>
<option value={null} disabled selected>Bitte Auswählen</option>
<option value="Verständnisproblem">Verständnisproblem</option>
<option value="Technischer Fehler">Technischer Fehler</option>
<option value="Feature anfordern">Feature anfordern</option>
<option value="Fehlende Funktionalität"
>Fehlende Funktionalität</option
>

View File

@@ -1,5 +1,12 @@
---
import { BenutzerClient } from "#components/Ausweis/types";
import HeaderLogin from "#components/design/header/HeaderLogin.svelte";
export type Props = {
user: BenutzerClient | null
}
const { user } = Astro.props;
---
<header id="header" class="hidden sm:block">
@@ -63,7 +70,7 @@ lg:px-0 lg:gap-x-4">
<div class="w-full justify-self-center">
<HeaderLogin client:load />
<HeaderLogin {user} client:load />
</div>

View File

@@ -1,8 +1,11 @@
<script lang="ts">
import { BenutzerClient } from "#components/Ausweis/types.js";
import { loginClient } from "#lib/login.js";
import { CrossCircled } from "radix-svelte-icons";
import { fade } from "svelte/transition";
export let user: BenutzerClient | null;
let email: string;
let passwort: string;
@@ -24,6 +27,7 @@
id="card-login"
class="rounded-none lg:rounded-lg lg:card lg:box lg:ring-2 border-t-2 lg:border-none border-gray-500/50 ring-gray-500/50 xs:px-2 md:px-4 py-2"
>
{#if !user}
<form on:submit={login}>
<div class="grid grid-cols-[1fr] xs:grid-cols-[2fr_2fr_1fr] gap-2">
<input
@@ -82,6 +86,19 @@
</div>
</div>
</form>
{:else}
<div class="flex flex-row gap-4 items-center">
<img src={user.profilbild || "/images/profile-placeholder.png"} class="w-14 h-14 rounded-full border">
<div class="flex flex-col">
<span class="text-base font-bold">{user.vorname} {user.name}</span>
<span class="text-black text-opacity-50">{user.email}</span>
<div class="flex flex-row gap-4">
<a href="/dashboard" class="text-sm">Dashboard</a>
<a href="/auth/logout" class="text-sm">Ausloggen</a>
</div>
</div>
</div>
{/if}
</div>
<style lang="postcss">

View File

@@ -1,23 +1,23 @@
export * from "./anteilshaber"
export * from "./apirequests"
export * from "./aufnahme"
export * from "./bedarfsausweisgewerbe"
export * from "./bedarfsausweiswohnen"
export * from "./benutzer"
export * from "./bild"
export * from "./event"
export * from "./gegeinpreisung"
export * from "./gegnachweisgewerbe"
export * from "./gegnachweiswohnen"
export * from "./klimafaktoren"
export * from "./objekt"
export * from "./postleitzahlen"
export * from "./rechnung"
export * from "./refreshtokens"
export * from "./tickets"
export * from "./unterlage"
export * from "./verbrauchsausweisgewerbe"
export * from "./verbrauchsausweiswohnen"
export * from "./documenttemplates"
export * from "./documenttypes"
export * from "./tokens"
export * from "./anteilshaber.js"
export * from "./apirequests.js"
export * from "./aufnahme.js"
export * from "./bedarfsausweisgewerbe.js"
export * from "./bedarfsausweiswohnen.js"
export * from "./benutzer.js"
export * from "./bild.js"
export * from "./event.js"
export * from "./gegeinpreisung.js"
export * from "./gegnachweisgewerbe.js"
export * from "./gegnachweiswohnen.js"
export * from "./klimafaktoren.js"
export * from "./objekt.js"
export * from "./postleitzahlen.js"
export * from "./rechnung.js"
export * from "./refreshtokens.js"
export * from "./tickets.js"
export * from "./unterlage.js"
export * from "./verbrauchsausweisgewerbe.js"
export * from "./verbrauchsausweiswohnen.js"
export * from "./documenttemplates.js"
export * from "./documenttypes.js"
export * from "./tokens.js"

View File

@@ -6,6 +6,9 @@ import Header from "#components/design/header/Header.astro";
import Footer from "#components/design/footer/Footer.astro";
import SidebarLeft from "#components/design/sidebars/SidebarLeft.astro";
import { NotificationWrapper } from "@ibcornelsen/ui";
import { getCurrentUser } from "#lib/server/user";
const user = await getCurrentUser(Astro)
export interface Props {
title: string;
@@ -115,7 +118,7 @@ const { title } = Astro.props;
</head>
<body>
<Header />
<Header {user} />
<main
class="w-full p-0 grid

View File

@@ -1,7 +1,7 @@
---
// import "svelte-ripple-action/ripple.css";
import "../style/global.css";
import "../../svelte-dialogs.config";
import "../../svelte-dialogs.config.js";
import DashboardSidebar from "../components/Dashboard/DashboardSidebar.svelte";
import { validateAccessTokenServer } from "#server/lib/validateAccessToken";
import { BenutzerClient } from "#components/Ausweis/types";

View File

@@ -6,6 +6,10 @@ import Footer from "#components/design/footer/Footer.astro";
import SidebarLeft from "#components/design/sidebars/SidebarLeft.astro";
import SidebarRight from "#components/design/sidebars/SidebarRight.astro";
import { NotificationWrapper } from "@ibcornelsen/ui";
import TicketButton from "#components/Tickets/TicketButton.svelte";
import { getCurrentUser } from "#lib/server/user";
const user = await getCurrentUser(Astro);
export interface Props {
title: string;
@@ -94,7 +98,7 @@ window.addEventListener("scroll", (event) => {
<body>
<Header />
<Header {user} />
<main id="main"
class="w-full p-0 grid
@@ -119,6 +123,7 @@ window.addEventListener("scroll", (event) => {
<Footer />
<NotificationWrapper client:load />
<TicketButton client:load></TicketButton>
</body>
</html>

View File

@@ -2,7 +2,7 @@
import { AufnahmeKomplettClient, BenutzerClient } from "#components/Ausweis/types.js";
import Carousel from "#components/Carousel.svelte";
import DashboardAusweis from "#components/Dashboard/DashboardAusweis.svelte";
import { Objekt } from "#lib/client/prisma";
import { Objekt } from "#lib/client/prisma.js";
import { ChevronLeft, ChevronRight } from "radix-svelte-icons";
export let user: BenutzerClient;

View File

@@ -5,34 +5,32 @@
Cube,
Person,
} from "radix-svelte-icons";
import { Tabs, Tab, TabList, TabPanel } from "../../components/Tabs";
import { dialogs } from "../../../svelte-dialogs.config";
import { BenutzerClient } from "#components/Ausweis/types";
// import { client } from "src/trpc";
import { exclude } from "#lib/exclude";
import { Tabs, Tab, TabList, TabPanel } from "../../components/Tabs/index.js";
import { dialogs } from "../../../svelte-dialogs.config.js";
import { BenutzerClient } from "#components/Ausweis/types.js";
import { exclude } from "#lib/exclude.js";
import { api } from "astro-typesafe-api/client";
import Cookies from "js-cookie";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
export let benutzer: BenutzerClient;
let passwort: string | undefined = undefined;
let passwortVerify: string | undefined = undefined;
let passwort: string = "";
let passwortVerify: string = "";
async function profilSpeichern(e: SubmitEvent) {
e.preventDefault()
if (!passwort) {
passwort = undefined
} else {
if (passwort.length < 8) {
dialogs.alert({
title: "Passwort zu kurz",
text: "Das Passwort muss mindestens 8 Zeichen lang sein.",
})
return
} else if (passwort !== passwortVerify) {
dialogs.alert({
title: "Passwörter stimmen nicht überein",
text: "Die eingegebenen Passwörter stimmen nicht überein.",
})
return
}
if (passwort.length < 8) {
dialogs.alert({
title: "Passwort zu kurz",
text: "Das Passwort muss mindestens 8 Zeichen lang sein.",
})
return
} else if (passwort !== passwortVerify) {
dialogs.alert({
title: "Passwörter stimmen nicht überein",
text: "Die eingegebenen Passwörter stimmen nicht überein.",
})
return
}
const response = await dialogs.confirm({
@@ -51,7 +49,11 @@
// Wir wollen die Rolle nicht mit übertragen.
// Diese wird zwar sowieso rausgeschmissen aber sonst kommen wir nicht durch die Validation durch...
await client.v1.benutzer.update.mutate(benutzerObjekt)
await api.user.POST.fetch(benutzerObjekt, {
headers: {
Authorization: `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
dialogs.success({
title: "Profil gespeichert",
@@ -175,7 +177,7 @@
}
:global(.tab) {
@apply rounded-none px-8 justify-start outline-0 gap-4 items-center text-base font-normal text-base-content;
@apply flex flex-row py-4 rounded-none px-8 justify-start outline-0 gap-4 items-center text-base font-normal text-base-content;
}
:global(.tab:hover) {

View File

@@ -1,24 +1,121 @@
<script lang="ts">
import { BenutzerClient, ObjektKomplettClient } from "#components/Ausweis/types.js";
import DashboardAusweis from "#components/Dashboard/DashboardAusweis.svelte";
import "../../style/formular.css";
import {
BenutzerClient,
ObjektClient,
ObjektKomplettClient,
} from "#components/Ausweis/types.js";
import DashboardObjekt from "#components/Dashboard/DashboardObjekt.svelte";
import Overlay from "#components/Overlay.svelte";
import PlzSuche from "#components/PlzSuche.svelte";
import TagInput from "#components/TagInput.svelte";
import { api } from "astro-typesafe-api/client";
import NotificationProvider from "#components/NotificationProvider/NotificationProvider.svelte";
import NotificationWrapper from "#components/Notifications/NotificationWrapper.svelte";
import { addNotification } from "#components/Notifications/shared.js";
import Cookies from "js-cookie";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
export let user: BenutzerClient;
export let objekte: ObjektKomplettClient[];
let objektOverlayHidden = true;
let objekt: Omit<ObjektClient, "uid"> = {
adresse: "",
erstellungsdatum: new Date(),
latitude: 0,
longitude: 0,
ort: "",
plz: ""
};
async function objektErstellen() {
if (!objekt.adresse || !objekt.ort || !objekt.plz) {
addNotification({
message: "Daten unvollständig.",
subtext: "Ihre eingegebenen Daten sind unvollständig, bitte vervollständigen sie diese und versuchen sie es erneut..",
timeout: 3000,
dismissable: true,
type: "error"
})
return;
}
const result = await api.objekt.PUT.fetch({
adresse: objekt.adresse,
erstellungsdatum: new Date(),
latitude: 0,
longitude: 0,
ort: objekt.ort,
plz: objekt.plz
}, {
headers: {
Authorization: `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
if (!result) {
addNotification({
message: "Etwas ist schiefgelaufen.",
subtext: "Das Objekt konnte nicht erstellt werden.",
timeout: 3000,
dismissable: true,
type: "error"
})
return;
}
objektOverlayHidden = true;
objekte.push({
...objekt,
aufnahmen: [],
uid: result.uid
})
objekt = {
adresse: "",
erstellungsdatum: new Date(),
latitude: 0,
longitude: 0,
ort: "",
plz: ""
}
objekte = objekte
}
</script>
<h1>Gebäudeübersicht</h1>
<hr>
<hr />
<div class="relative mb-6">
<button class="button flex flex-row rounded-lg gap-2 bg-secondary text-white text-center">
Gebäude anlegen +
</button>
</div>
<button class="button" on:click={() => {
objektOverlayHidden = false
}}> Gebäude anlegen + </button>
</div>
<div class="columns columns-1 md:columns-2 lg:columns-3 gap-4">
{#each objekte as objekt}
<div class="columns columns-1 md:columns-2 lg:columns-3 gap-4">
{#each objekte as objekt}
<DashboardObjekt {objekt}></DashboardObjekt>
{/each}
</div>
</div>
<Overlay bind:hidden={objektOverlayHidden}>
<div class="bg-white w-full max-w-screen-sm px-4 py-6 flex flex-col gap-4">
<h2 class="p-0 m-0">Gebäude erstellen</h2>
<input type="text" placeholder="Adresse" bind:value={objekt.adresse}>
<div class="flex flex-row gap-4 justify-between">
<PlzSuche bind:city={objekt.ort} bind:zip={objekt.plz} name="" placeholder="PLZ"></PlzSuche>
<input type="text" bind:value={objekt.ort} placeholder="Ort">
</div>
<button class="button mt-4" on:click={objektErstellen}>Gebäude Erstellen</button>
</div>
</Overlay>
<NotificationWrapper></NotificationWrapper>

View File

@@ -2,12 +2,8 @@
import PerformanceScore from "#components/Ausweis/PerformanceScore.svelte";
import Progressbar from "#components/Ausweis/Progressbar.svelte";
import Bereich from "#components/labels/Bereich.svelte";
import Ansprechpartner from "#components/Ausweis/Ansprechpartner.svelte";
import Rechnungsadresse from "#components/Ausweis/Rechnungsadresse.svelte";
import type { Bezahlmethoden } from "#lib/client/prisma.js";
import { Enums } from "#lib/client/prisma.js";
import { dialogs } from "svelte-dialogs";
import LoginDialog from "#components/LoginDialog.svelte";
import {
API_ACCESS_TOKEN_COOKIE_NAME,
PRICES,
@@ -18,7 +14,6 @@
BenutzerClient,
BildClient,
ObjektClient,
RechnungClient,
UnterlageClient,
VerbrauchsausweisWohnenClient,
} from "#components/Ausweis/types.js";
@@ -31,8 +26,12 @@
import { addNotification } from "#components/Notifications/shared.js";
import NotificationWrapper from "#components/Notifications/NotificationWrapper.svelte";
import { nachweisSpeichern } from "#client/lib/nachweisSpeichern.js";
import { EnterFullScreen } from "radix-svelte-icons";
import { localStorageSync, writableSync } from "#client/lib/helpers.js";
import HelpLabel from "#components/labels/HelpLabel.svelte";
import InputLabel from "#components/labels/InputLabel.svelte";
import PlzSuche from "#components/PlzSuche.svelte";
import { writable } from "svelte/store";
export let user: Partial<BenutzerClient>;
export let ausweis: VerbrauchsausweisWohnenClient;
export let aufnahme: AufnahmeClient;
@@ -43,18 +42,41 @@
export let aktiveBezahlmethode: Bezahlmethoden = Enums.Bezahlmethoden.paypal;
export let ausweistyp: Enums.AusweisTyp = Enums.AusweisTyp.Standard;
let rechnung: Partial<RechnungClient> = {
email: user.email,
empfaenger: user.vorname + " " + user.name,
strasse: user.adresse,
plz: user.plz,
ort: user.ort,
versand_empfaenger: user.vorname + " " + user.name,
versand_strasse: user.adresse,
versand_plz: user.plz,
versand_ort: user.ort,
telefon: user.telefon,
};
const email = localStorageSync(user.email || "", "kundendaten.email")
const vorname = localStorageSync(user.vorname || "", "kundendaten.vorname")
const name = localStorageSync(user.name || "", "kundendaten.name")
const empfaenger = localStorageSync(`${user.vorname} ${user.name}`, "kundendaten.empfaenger", JSON.parse, JSON.stringify)
const strasse = localStorageSync(user.adresse || "", "kundendaten.strasse")
const plz = localStorageSync(user.plz || "", "kundendaten.plz")
const ort = localStorageSync(user.ort || "", "kundendaten.ort")
const zusatzzeile = localStorageSync("", "kundendaten.zusatzzeile")
const telefon = localStorageSync(user.telefon || "", "kundendaten.telefon")
const abweichende_versand_adresse = localStorageSync(false, "kundendaten.abweichende_versand_adresse", JSON.parse, JSON.stringify)
let versand_email,
versand_zusatzzeile,
versand_empfaenger,
versand_strasse,
versand_plz,
versand_ort;
$: {
if ($abweichende_versand_adresse) {
versand_email = writable($email)
versand_zusatzzeile = writable($zusatzzeile)
versand_empfaenger = writable($empfaenger)
versand_strasse = writable($strasse)
versand_plz = writable($plz)
versand_ort = writable($ort)
} else {
versand_email = writableSync(email)
versand_zusatzzeile = writableSync(zusatzzeile)
versand_empfaenger = writableSync(empfaenger)
versand_strasse = writableSync(strasse)
versand_plz = writableSync(plz)
versand_ort = writableSync(ort)
}
}
let services: {
name: string;
@@ -116,29 +138,6 @@
`/angebot-anfragen/bedarfsausweis-gewerbe-anfragen?uid=${ausweis.uid}`,
}[ausweisart];
async function speichern(e: SubmitEvent) {
e.preventDefault();
// Um einen Ausweis zu speichern müssen wir eingeloggt sein, andernfalls wird die API den call ablehnen.
// Wir prüfen also ob wir eingeloggt sind und leiten den Nutzer ggf. auf die Login Seite weiter.
if (!(await validateAccessTokenClient())) {
// TOOD: Auf Dialog umstellen.
const loggedIn = await dialogs.modal(LoginDialog);
if (!loggedIn) {
return false;
}
}
// Falls der Ausweis noch keine benutzer_id hat müssen wir ihn claimen, damit er dem jetzigen Nutzer zugewiesen wird...
// await client.v1.verbrauchsausweisWohnen.claim.mutate({
// uid: ausweis.uid,
// });
localStorage.clear();
window.location.href = `/kaufabschluss?uid=${ausweis.uid}`;
}
async function anfordern() {
if (!form.checkValidity()) {
addNotification({
@@ -153,7 +152,6 @@
if (!await validateAccessTokenClient()) {
loginAction = bestellen
rechnung = rechnung
loginOverlayHidden = false;
return
}
@@ -161,33 +159,34 @@
loginOverlayHidden = true
let result: Awaited<ReturnType<typeof ausweisSpeichern>> | null = null;
if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen) {
result = await nachweisSpeichern(ausweis, objekt, aufnahme, bilder, unterlagen, ausweisart)
} else {
result = await ausweisSpeichern(ausweis, objekt, aufnahme, bilder, ausweisart)
}
if (result === null) {
addNotification({
dismissable: true,
message: "Ups... Das hat nicht geklappt.",
subtext: "Der Nachweis konnte nicht gespeichert werden, bitte versuchen sie es erneut oder kontaktieren sie unseren Support."
})
}
try {
if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen) {
result = await nachweisSpeichern(ausweis, objekt, aufnahme, bilder, unterlagen, ausweisart)
} else {
result = await ausweisSpeichern(ausweis, objekt, aufnahme, bilder, ausweisart)
}
} catch(e) {
addNotification({
dismissable: true,
message: "Ups... Das hat nicht geklappt.",
subtext: "Speichern ist fehlgeschlagen, bitte versuchen sie es erneut oder kontaktieren sie unseren Support."
})
}
try {
const { uid } = await api.rechnung.anfordern.PUT.fetch(
{
email: rechnung.email,
empfaenger: rechnung.empfaenger,
strasse: rechnung.strasse,
plz: rechnung.plz,
ort: rechnung.ort,
versand_empfaenger: rechnung.versand_empfaenger,
versand_strasse: rechnung.versand_strasse,
versand_plz: rechnung.versand_plz,
versand_ort: rechnung.versand_ort,
telefon: rechnung.telefon,
email: $email,
empfaenger: $empfaenger,
strasse: $strasse,
plz: $plz,
ort: $ort,
versand_empfaenger: $versand_empfaenger,
versand_strasse: $versand_strasse,
versand_plz: $versand_plz,
versand_ort: $versand_ort,
telefon: $telefon,
nachweis_uid: ausweis.uid,
},
{
@@ -210,6 +209,42 @@
}
}
async function speichern() {
loginAction = speichern;
if (!await validateAccessTokenClient()) {
loginOverlayHidden = false;
return
}
loginOverlayHidden = true
let result: Awaited<ReturnType<typeof ausweisSpeichern>> | null = null;
try {
if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen) {
result = await nachweisSpeichern(ausweis, objekt, aufnahme, bilder, unterlagen, ausweisart)
} else {
result = await ausweisSpeichern(ausweis, objekt, aufnahme, bilder, ausweisart)
}
} catch(e) {
addNotification({
dismissable: true,
message: "Ups... Das hat nicht geklappt.",
subtext: "Speichern ist fehlgeschlagen, bitte versuchen sie es erneut oder kontaktieren sie unseren Support."
})
}
if (result !== null) {
window.history.pushState(
{},
"",
`${location.pathname}?uid=${ausweis.uid}`
);
localStorage.clear();
window.location.href = `/speichern-erfolgreich?uid=${ausweis.uid}`
}
}
async function bestellen() {
if (!form.checkValidity()) {
addNotification({
@@ -223,9 +258,8 @@
}
if (!await validateAccessTokenClient()) {
loginAction = bestellen
rechnung = rechnung
loginOverlayHidden = false;
loginAction = bestellen
loginOverlayHidden = false;
return
}
@@ -262,16 +296,16 @@
services: services
.filter((service) => service.selected)
.map((service) => service.id),
email: rechnung.email,
empfaenger: rechnung.empfaenger,
strasse: rechnung.strasse,
plz: rechnung.plz,
ort: rechnung.ort,
versand_empfaenger: rechnung.versand_empfaenger,
versand_strasse: rechnung.versand_strasse,
versand_plz: rechnung.versand_plz,
versand_ort: rechnung.versand_ort,
telefon: rechnung.telefon,
email: $email,
empfaenger: $empfaenger,
strasse: $strasse,
plz: $plz,
ort: $ort,
versand_empfaenger: $versand_empfaenger,
versand_strasse: $versand_strasse,
versand_plz: $versand_plz,
versand_ort: $versand_ort,
telefon: $telefon,
ausweis_uid: ausweis.uid,
ausweistyp,
},
@@ -371,13 +405,344 @@
<div id="formular-box" class="formular-boxen ring-0">
<Bereich
bereich="1"
title="Eingabe des Ansprechpartners -> {user.email}"
title="Eingabe des Ansprechpartners"
>
<Ansprechpartner bind:user /></Bereich
<div
id="ansprechpartner"
class="bereich-box grid
grid-cols-1 gap-x-4 gap-y-8
sm:grid-cols-2 sm:gap-x-6 sm:gap-y-8
xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
"
>
<!-- Vorname * -->
<div class="input-standard order-1 md:order-1 xl:order-1">
<InputLabel title="Vorname *"></InputLabel>
<input name="vorname" type="text" bind:value={$vorname} required />
<div class="help-label">
<HelpLabel>
Bitte Geben Sie hier den Vornamen des Ansprechpartners&nbsp;ein.
</HelpLabel>
</div>
</div>
<!-- Nachname * -->
<div class="input-standard order-2 md:order-2 xl:order-2">
<InputLabel title="Nachname *"></InputLabel>
<input name="name" type="text" bind:value={$name} required />
<div class="help-label">
<HelpLabel>
Bitte Geben Sie hier den Nachnamen des
Ansprechpartners&nbsp;ein.
</HelpLabel>
</div>
</div>
<!-- Telefon & Email * -->
<div class="input-standard order-3 md:order-3 xl:order-3">
<InputLabel title="Telefonnummer *"></InputLabel>
<input name="telefon" bind:value={$telefon} type="text" />
<div class="help-label">
<HelpLabel>
Bitte Geben Sie hier die Telefonnummer des
Ansprechpartners&nbsp;ein.
</HelpLabel>
</div>
</div>
</div>
</Bereich
>
<Bereich bereich="2" title="Rechnungsadresse">
<Rechnungsadresse bind:rechnung /></Bereich
<div
id="rechnungsadresse"
class="bereich-box grid
grid-cols-1 gap-x-4 gap-y-8
sm:grid-cols-2 sm:gap-x-6 sm:gap-y-8
xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
">
<!-- Empfänger * -->
<div class="input-standard order-1 md:order-1 xl:order-1">
<InputLabel title="Rechnungs-Empfänger *"></InputLabel>
<input
name="rechnung_empfaenger"
type="text"
bind:value={$empfaenger}
required
data-rule-maxlength="100"
data-msg-maxlength="max. 100 Zeichen"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie den Empfänger ein, auf den die Rechnung
ausgestellt wird.
</HelpLabel>
</div>
</div>
<!-- Straße, Hausnummer * -->
<div class="input-standard order-2 md:order-2 xl:order-2">
<InputLabel title="Straße, Hausnummer *"></InputLabel>
<input
name="rechnung_strasse"
bind:value={$strasse}
type="text"
required
data-rule-maxlength="40"
data-msg-maxlength="max. 40 Zeichen"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die Strasse und Hausnummer, so wie Sie auf der
Rechnung erscheinen soll, ein.
</HelpLabel>
</div>
</div>
<!-- PLZ / ORT -->
<div class="grid grid-cols-[2fr_4fr] gap-x-4 order-3 md:order-3 xl:order-3">
<div class="input-noHelp">
<InputLabel title="PLZ *"></InputLabel>
<PlzSuche
name="rechnung_plz"
bind:zip={$plz}
bind:city={$ort}
/>
</div>
<div class="input-standard">
<InputLabel title="Ort *"></InputLabel>
<input
name="rechnung_ort"
type="text"
required
bind:value={$ort}
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die PLZ des Ortes, so wie Sie auf der
Rechnung erscheinen soll, ein.
</HelpLabel>
</div>
</div>
</div>
<!-- Zusatzzeile -->
<div class="input-standard order-4 md:order-4 xl:order-4">
<InputLabel title="Zusatzzeile"></InputLabel>
<input
name="rechnung_zusatzzeile"
bind:value={$zusatzzeile}
type="text"
data-rule-maxlength="80"
data-msg-maxlength="max. 80 Zeichen"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie, falls erforderlich, zusätzliche nformationen
ein.
</HelpLabel>
</div>
</div>
<!-- E-mail -->
<div class="input-standard order-5 md:order-5 xl:order-5">
<InputLabel title="E-mail *"></InputLabel>
<input name="rechnung_email" bind:value={$email} type="email" />
<div class="help-label">
<HelpLabel>
Bitte geben Sie die E-Mail Adresse des Rechnungsempfängers ein.
</HelpLabel>
</div>
</div>
<div class="sm:col-span-3 order-7 md:order-7 xl:order-7">
<div
class="grid grid-cols-[25px_max-content] items-center justify-items-start"
>
<input
id="abweichende_versand_adresse"
class="w-[15px] h-[15px]"
type="checkbox"
name="abweichende_versand_adresse"
bind:checked={$abweichende_versand_adresse}
/>
<label for="abweichende_versand_adresse" class="cursor-pointer"
>abweichende Versandadresse</label
>
</div>
</div>
{#if $abweichende_versand_adresse}
<!-- Versand Empfänger * -->
<div class="input-standard order-8 md:order-8 xl:order-8">
<InputLabel title="Versand-Empfänger *"></InputLabel>
<input
name="versand_empfaenger"
type="text"
readonly={!$abweichende_versand_adresse}
bind:value={$versand_empfaenger}
required
data-rule-maxlength="100"
data-msg-maxlength="max. 100 Zeichen"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie den Namen des Versand-Empfängers ein.
</HelpLabel>
</div>
</div>
<!-- Versand Straße, Hausnummer * -->
<div class="input-standard order-9 md:order-9 xl:order-9">
<InputLabel title="Straße, Hausnummer *"></InputLabel>
<input
name="versand_strasse"
type="text"
readonly={!$abweichende_versand_adresse}
bind:value={$versand_strasse}
required
data-rule-maxlength="40"
data-msg-maxlength="max. 40 Zeichen"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die Versand-Empfänger Strasse und Hausnummer
ein, an die die Rechnung versandt wird.
</HelpLabel>
</div>
</div>
<!-- PLZ / ORT -->
<div
class="grid grid-cols-[2fr_4fr] gap-x-4 order-10 md:order-10 xl:order-10"
>
<div class="input-noHelp">
<InputLabel title="PLZ *"></InputLabel>
<PlzSuche
name="versand_plz"
readonly={!$abweichende_versand_adresse}
bind:zip={$versand_plz}
bind:city={$versand_ort}
/>
</div>
<div class="input-standard">
<InputLabel title="Ort *"></InputLabel>
<input
name="versand_ort"
type="text"
readonly
required
bind:value={$versand_ort}
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die Versand-Empfänger PLZ des Ortes ein,
an den die Rechnung versandt wird.
</HelpLabel>
</div>
</div>
</div>
<!-- Zusatzzeile -->
<div class="input-standard order-11 md:order-11 xl:order-11">
<InputLabel title="Zusatzzeile"></InputLabel>
<input
name="versand_zusatzzeile"
type="text"
readonly={!$abweichende_versand_adresse}
bind:value={$versand_zusatzzeile}
data-rule-maxlength="80"
data-msg-maxlength="max. 80 Zeichen"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie, falls erforderlich, zusätzliche
nformationen ein.
</HelpLabel>
</div>
</div>
<!-- E-mail -->
<div class="input-standard order-12 md:order-12 xl:order-12">
<InputLabel title="E-mail *"></InputLabel>
<input
name="versand_email"
bind:value={$versand_email}
type="email"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die E-Mail Adresse des Versand-Empfängers
ein.
</HelpLabel>
</div>
</div>
<!-- Telefon
<div class="input-standard order-[13] md:order-[13] xl:order-[13]">
<Inputlabel title="Telefon *"></Inputlabel>
<input
name="rechnung_telefon"
bind:value={rechnung.telefon}
type="text"
/>
<div class="help-label">
<HelpLabel>
Bitte geben Sie die Telefonnummer des Versand-Empfängers ein.
</HelpLabel>
</div>
</div>
-->
{/if}
</div>
</Bereich
>
{#if !gegAnfrage}
@@ -498,7 +863,7 @@ sm:grid-cols-[min-content_min-content_min-content] sm:justify-self-end sm:mt-8"
<button class="order-2 button" type="button">Speichern</button>
<button class="order-2 button" type="button" on:click={speichern}>Speichern</button>
{#if gegAnfrage}
<button
@@ -520,7 +885,7 @@ sm:grid-cols-[min-content_min-content_min-content] sm:justify-self-end sm:mt-8"
<Overlay bind:hidden={loginOverlayHidden}>
<div class="bg-white w-full max-w-screen-sm py-8">
<EmbeddedAuthFlowModule onLogin={loginAction} email={rechnung.email}></EmbeddedAuthFlowModule>
<EmbeddedAuthFlowModule onLogin={loginAction} email={$email}></EmbeddedAuthFlowModule>
</div>
</Overlay>

View File

@@ -146,6 +146,8 @@
const ausweisart = Enums.Ausweisart.VerbrauchsausweisWohnen
const anliegen = "Energieausweis erstellen";
let form: HTMLFormElement;
</script>
<Overlay bind:hidden={speichernOverlayHidden}>
@@ -179,7 +181,7 @@ const anliegen = "Energieausweis erstellen";
<div id="formInput-1" data-test="ausweis">
<form id="formInput-1" data-test="ausweis" bind:this={form}>
<div id="formular-box" class="formular-boxen ring-0">
<ButtonWeiterHilfe
@@ -190,6 +192,7 @@ const anliegen = "Energieausweis erstellen";
bind:aufnahme
ausweisart={Enums.Ausweisart.VerbrauchsausweisWohnen}
showWeiter={false}
{form}
>
</ButtonWeiterHilfe>
@@ -295,6 +298,7 @@ const anliegen = "Energieausweis erstellen";
bind:user
bind:objekt
bind:aufnahme
bind:form
ausweisart={Enums.Ausweisart.VerbrauchsausweisWohnen}
{ausweistyp}
>
@@ -303,7 +307,7 @@ const anliegen = "Energieausweis erstellen";
</div>
</form>
<RawNotificationWrapper class="fixed left-8 bottom-8 max-w-[400px] flex flex-col gap-4 z-50">
{#each Object.entries($notifications) as [uid, notification] (uid)}

View File

@@ -1,7 +1,7 @@
import { AufnahmeClient, OptionalNullable, UUidWithPrefix, ZodOverlap } from "#components/Ausweis/types.js";
import { exclude } from "#lib/exclude.js";
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
import { AufnahmeSchema, prisma } from "#lib/server/prisma";
import { AufnahmeSchema, prisma } from "#lib/server/prisma.js";
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { z } from "zod";
@@ -11,6 +11,7 @@ export const PATCH = defineApiRoute({
uid: true,
benutzer_id: true,
objekt_id: true,
erstellungsdatum: true
}),
output: z.void(),
middleware: authorizationMiddleware,

View File

@@ -1,7 +1,7 @@
import { ObjektClient, OptionalNullable, ZodOverlap } from "#components/Ausweis/types.js";
import { exclude } from "#lib/exclude.js";
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
import { ObjektSchema, prisma } from "#lib/server/prisma";
import { ObjektSchema, prisma } from "#lib/server/prisma.js";
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { z } from "zod";
@@ -9,7 +9,8 @@ export const PATCH = defineApiRoute({
input: ObjektSchema.omit({
uid: true,
id: true,
benutzer_id: true
benutzer_id: true,
erstellungsdatum: true
}),
output: z.void(),
headers: {

View File

@@ -1,7 +1,8 @@
import { UUidWithPrefix } from "#components/Ausweis/types.js";
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
import { ObjektSchema, prisma } from "#lib/server/prisma";
import { prisma } from "#lib/server/prisma.js";
import { defineApiRoute } from "astro-typesafe-api/server";
import { ObjektSchema } from "src/generated/zod/index.js";
import { z } from "zod";
export const PUT = defineApiRoute({

View File

@@ -1,7 +1,6 @@
import { z } from "zod";
import { TicketsSchema, prisma } from "#lib/server/prisma";
import { TicketsSchema, prisma } from "#lib/server/prisma.js";
import { defineApiRoute } from "astro-typesafe-api/server";
import { maybeAuthorizationMiddleware } from "#lib/middleware/authorization.js";
import { UUidWithPrefix } from "#components/Ausweis/types.js";
export const PUT = defineApiRoute({
@@ -26,37 +25,9 @@ export const PUT = defineApiRoute({
output: z.object({
uid: UUidWithPrefix,
}),
middleware: maybeAuthorizationMiddleware,
async fetch(input, ctx, user) {
if (user === null) {
// Der Benutzer ist nicht authentifiziert.
// Wir erstellen das Ticket anonym.
const ticket = await prisma.tickets.create({
data: {
beschreibung: input.beschreibung,
email: input.email,
titel: input.titel,
metadata: input.metadata,
},
select: {
uid: true,
},
});
return {
uid: ticket.uid,
};
}
// Der Benutzer ist authentifiziert.
// Wir verlinken den Benutzer und das Ticket.
async fetch(input, ctx) {
const ticket = await prisma.tickets.create({
data: {
benutzer: {
connect: {
id: user.id,
},
},
beschreibung: input.beschreibung,
email: input.email,
titel: input.titel,

View File

@@ -1,9 +1,44 @@
import { UUidWithPrefix } from "#components/Ausweis/types.js";
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
import { hashPassword } from "#lib/password.js";
import { prisma } from "#lib/server/prisma";
import { BenutzerSchema, prisma } from "#lib/server/prisma.js";
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { z } from "zod";
export const POST = defineApiRoute({
input: BenutzerSchema.omit({
id: true,
lex_office_id: true,
uid: true,
rolle: true
}),
middleware: authorizationMiddleware,
async fetch(input, context, user) {
if (user.email !== input.email) {
// TODO: Email wurde geändert, neue Bestätigunsmail schicken.
}
await prisma.benutzer.update({
where: {
id: user.id
},
data: {
adresse: input.adresse,
anrede: input.anrede,
email: input.email,
firma: input.firma,
name: input.name,
vorname: input.vorname,
ort: input.ort,
passwort: hashPassword(input.passwort),
plz: input.plz,
profilbild: input.profilbild,
telefon: input.telefon,
}
})
},
})
export const PUT = defineApiRoute({
input: z.object({
email: z.string().email(),

View File

@@ -1,7 +1,7 @@
import { OptionalNullable, UUidWithPrefix, VerbrauchsausweisWohnenClient, ZodOverlap } from "#components/Ausweis/types.js";
import { exclude } from "#lib/exclude.js";
import { authorizationHeaders, authorizationMiddleware } from "#lib/middleware/authorization.js";
import { prisma, VerbrauchsausweisWohnenSchema } from "#lib/server/prisma";
import { prisma, VerbrauchsausweisWohnenSchema } from "#lib/server/prisma.js";
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { z } from "zod";
@@ -10,8 +10,10 @@ export const PATCH = defineApiRoute({
uid: true,
id: true,
benutzer_id: true,
aufnahme_id: true,
}),
aufnahme_id: true
}).merge(z.object({
startdatum: z.coerce.date()
})),
output: z.void(),
headers: {
"Authorization": z.string()

View File

@@ -5,4 +5,5 @@ Astro.cookies.delete(API_ACCESS_TOKEN_COOKIE_NAME);
Astro.cookies.delete(API_REFRESH_TOKEN_COOKIE_NAME);
Astro.cookies.delete(API_UID_COOKIE_NAME);
return Astro.redirect("/auth/login");
---

View File

@@ -1,9 +1,7 @@
---
import { createCaller } from "../../../astro-typesafe-api-caller.js";
import { validateAccessTokenServer } from "#server/lib/validateAccessToken";
import DashboardModule from "#modules/Dashboard/DashboardModule.svelte";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
import Layout from "#layouts/Layout.astro";
import { prisma } from "#lib/server/prisma";
import UserLayout from "#layouts/DashboardLayout.astro";
import DashboardAufnahmeModule from "#modules/Dashboard/DashboardAufnahmeModule.svelte";

View File

@@ -4,7 +4,7 @@ import { getCurrentUser } from "#lib/server/user";
import DashboardEinstellungenModule from "#modules/Dashboard/DashboardEinstellungenModule.svelte";
import { validateAccessTokenServer } from "#server/lib/validateAccessToken";
const user = getCurrentUser(Astro)
const user = await getCurrentUser(Astro)
if (!user) {
return Astro.redirect("/auth/login", 302);

View File

@@ -46,5 +46,5 @@ const objekte = await prisma.objekt.findMany({
---
<DashboardLayout title="Dashboard" {user}>
<DashboardModule {user} {objekte} />
<DashboardModule {user} {objekte} client:load />
</DashboardLayout>

View File

@@ -34,6 +34,6 @@ try {
---
<AusweisLayout title="Kundendaten Aufnehmen - IBCornelsen">
<KundendatenModule {user} {ausweis} {objekt} {aufnahme} {bilder} {ausweisart} {ausweistyp} aktiveBezahlmethode={Enums.Bezahlmethoden.paypal} client:load></KundendatenModule>
<KundendatenModule {user} {ausweis} {objekt} {aufnahme} {bilder} {ausweisart} {ausweistyp} aktiveBezahlmethode={Enums.Bezahlmethoden.paypal} client:only></KundendatenModule>
</AusweisLayout>

View File

@@ -2,16 +2,16 @@ import { dialogs } from "svelte-dialogs";
dialogs.config({
global: {
confirmButtonClass: "btn btn-primary",
declineButtonClass: "btn btn-bordered",
dialogClass: "modal-box",
headerClass: "bg-base-100 text-center",
confirmButtonClass: "button",
declineButtonClass: "button",
dialogClass: "bg-white px-6 py-4 rounded-lg",
headerClass: "text-center",
titleClass: "text-base-content text-xl font-medium",
dividerClass: "hidden",
footerClass: "bg-base-100 justify-center gap-4 mt-4",
closeButtonClass: "btn btn-primary",
footerClass: "justify-center gap-4 mt-4",
closeButtonClass: "button",
closeButtonText: "Schließen",
dismissButtonClass: "btn btn-primary",
dismissButtonClass: "button",
dismissButtonText: "Schließen",
closeOnBg: true,
closeOnEsc: true