diff --git a/bun.lock b/bun.lock index b6a068d5..7c5db915 100644 --- a/bun.lock +++ b/bun.lock @@ -40,6 +40,7 @@ "sharp": "^0.33.5", "siema": "^1.5.1", "soap": "^1.1.8", + "sqids": "^0.3.0", "svelte": "^3.59.2", "svelte-dialogs": "^1.2.2", "svelte-preprocess": "^5.1.4", @@ -2190,6 +2191,8 @@ "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + "sqids": ["sqids@0.3.0", "", {}, "sha512-lOQK1ucVg+W6n3FhRwwSeUijxe93b51Bfz5PMRMihVf1iVkl82ePQG7V5vwrhzB11v0NtsR25PSZRGiSomJaJw=="], + "sshpk": ["sshpk@1.18.0", "", { "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", "dashdash": "^1.12.0", "ecc-jsbn": "~0.1.1", "getpass": "^0.1.1", "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" }, "bin": { "sshpk-conv": "bin/sshpk-conv", "sshpk-sign": "bin/sshpk-sign", "sshpk-verify": "bin/sshpk-verify" } }, "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ=="], "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], diff --git a/package.json b/package.json index 56e3bdc8..15b89b45 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "sharp": "^0.33.5", "siema": "^1.5.1", "soap": "^1.1.8", + "sqids": "^0.3.0", "svelte": "^3.59.2", "svelte-dialogs": "^1.2.2", "svelte-preprocess": "^5.1.4", diff --git a/src/astro-typesafe-api-caller.ts b/src/astro-typesafe-api-caller.ts index 01c6a1a1..0a8955a6 100644 --- a/src/astro-typesafe-api-caller.ts +++ b/src/astro-typesafe-api-caller.ts @@ -5,6 +5,9 @@ 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"), + "ausweise": await import("../src/pages/api/ausweise/index.ts"), + "bedarfsausweis-wohnen/[uid]": await import("../src/pages/api/bedarfsausweis-wohnen/[uid].ts"), + "bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/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"), @@ -13,25 +16,22 @@ export const createCaller = createCallerFactory({ "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"), + "bilder/[uid]": await import("../src/pages/api/bilder/[uid].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"), "auth/refresh-token": await import("../src/pages/api/auth/refresh-token.ts"), - "bedarfsausweis-wohnen/[uid]": await import("../src/pages/api/bedarfsausweis-wohnen/[uid].ts"), - "bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"), - "bilder/[uid]": await import("../src/pages/api/bilder/[uid].ts"), + "objekt": await import("../src/pages/api/objekt/index.ts"), "geg-nachweis-gewerbe/[uid]": await import("../src/pages/api/geg-nachweis-gewerbe/[uid].ts"), "geg-nachweis-gewerbe": await import("../src/pages/api/geg-nachweis-gewerbe/index.ts"), "geg-nachweis-wohnen/[uid]": await import("../src/pages/api/geg-nachweis-wohnen/[uid].ts"), "geg-nachweis-wohnen": await import("../src/pages/api/geg-nachweis-wohnen/index.ts"), - "objekt": await import("../src/pages/api/objekt/index.ts"), - "rechnung/anfordern": await import("../src/pages/api/rechnung/anfordern.ts"), - "rechnung": await import("../src/pages/api/rechnung/index.ts"), "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"), + "rechnung/anfordern": await import("../src/pages/api/rechnung/anfordern.ts"), + "rechnung": await import("../src/pages/api/rechnung/index.ts"), + "user": await import("../src/pages/api/user/index.ts"), + "user/self": await import("../src/pages/api/user/self.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"), diff --git a/src/client/lib/ausweisSpeichern.ts b/src/client/lib/ausweisSpeichern.ts index b85c4d62..c7c58165 100644 --- a/src/client/lib/ausweisSpeichern.ts +++ b/src/client/lib/ausweisSpeichern.ts @@ -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, diff --git a/src/client/lib/helpers.ts b/src/client/lib/helpers.ts index aef33eaf..e20fc762 100644 --- a/src/client/lib/helpers.ts +++ b/src/client/lib/helpers.ts @@ -1,7 +1,22 @@ import { writable, Writable } from "svelte/store"; import { ZodEffects, ZodNullable, ZodOptional, ZodType } from "zod"; -export function localStorageSync(initial: T, name: string, modifier: (stored: any) => T = (stored) => JSON.parse(stored), reverseModifier: (value: T) => string = (value) => JSON.stringify(value)): Writable { +/** + * Synchronisiert den ersten Writable in den zweiten. + */ +export function writableSync(source: Writable) { + let $val; + source.subscribe($ => $val = $)(); + const target = writable($val) + + source.subscribe((value) => { + target.set(value); + }) + + return target; +} + +export function localStorageSync(initial: T, name: string, modifier: (stored: any) => T = (stored) => stored, reverseModifier: (value: T) => string = (value) => value as string): Writable { const stored = localStorage.getItem(name) as T let value = initial; if (stored) { @@ -29,7 +44,7 @@ export function isZodInstanceOf>( 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; diff --git a/src/components/Ausweis/Ansprechpartner.svelte b/src/components/Ausweis/Ansprechpartner.svelte deleted file mode 100644 index 045a96a9..00000000 --- a/src/components/Ausweis/Ansprechpartner.svelte +++ /dev/null @@ -1,91 +0,0 @@ - - -
- - - - - - - -
- - - - -
- - Bitte Geben Sie hier den Vornamen des Ansprechpartners ein. - -
-
- - - - -
- - - - -
- - Bitte Geben Sie hier den Nachnamen des Ansprechpartners ein. - -
-
- - - -
- - - - -
- - Bitte Geben Sie hier die Telefonnummer des Ansprechpartners ein. - -
-
- - - - - -
\ No newline at end of file diff --git a/src/components/Ausweis/Ausweisart.svelte b/src/components/Ausweis/Ausweisart.svelte index befd65d1..b575bc2e 100644 --- a/src/components/Ausweis/Ausweisart.svelte +++ b/src/components/Ausweis/Ausweisart.svelte @@ -48,7 +48,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8 required data-cy="ausstellgrund" > - + {#each Object.entries(Enums.Ausstellgrund) as [name, ausstellgrund]} {/each} @@ -72,9 +72,9 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8 required bind:value={aufnahme.gebaeudetyp} > - + - {#if ausweisart==Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisWohnen} + {#if ausweisart==Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisWohnen || ausweisart === Enums.Ausweisart.BedarfsausweisWohnen} @@ -224,7 +224,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8 required bind:value={aufnahme.saniert} > - + diff --git a/src/components/Ausweis/ButtonWeiterHilfe.svelte b/src/components/Ausweis/ButtonWeiterHilfe.svelte index 68d78502..e74afd45 100644 --- a/src/components/Ausweis/ButtonWeiterHilfe.svelte +++ b/src/components/Ausweis/ButtonWeiterHilfe.svelte @@ -17,12 +17,20 @@ export let objekt: ObjektClient; export let aufnahme: AufnahmeClient; export let ausweisart: Enums.Ausweisart + export let form: HTMLFormElement; + export let skala: HTMLDivElement; export let showWeiter: boolean = true; export let ausweistyp: AusweisTyp = Enums.AusweisTyp.Standard; async function ausweisAbschicken() { + if (!form.checkValidity()) { + skala.classList.add("no-scroll") + form.reportValidity() + return + } + openWindowWithPost("/kundendaten", { ausweis, objekt, @@ -170,7 +178,7 @@ grid-cols-1 gap-x-2 gap-y-4
- + diff --git a/src/components/Tickets/TicketPopup.svelte b/src/components/Tickets/TicketPopup.svelte index f5544fb6..e3845ddc 100644 --- a/src/components/Tickets/TicketPopup.svelte +++ b/src/components/Tickets/TicketPopup.svelte @@ -44,9 +44,7 @@

Kategorie *

+
+ + +
+ + + + + \ No newline at end of file diff --git a/src/modules/KundendatenModule.svelte b/src/modules/KundendatenModule.svelte index 022b9a1c..2663e6e9 100644 --- a/src/modules/KundendatenModule.svelte +++ b/src/modules/KundendatenModule.svelte @@ -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; 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 = { - 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> | 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> | 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 @@
- + + + +
+ + + + +
+ + Bitte Geben Sie hier den Vornamen des Ansprechpartners ein. + +
+
+ + + +
+ + + + +
+ + Bitte Geben Sie hier den Nachnamen des + Ansprechpartners ein. + +
+
+ + + +
+ + + + +
+ + Bitte Geben Sie hier die Telefonnummer des + Ansprechpartners ein. + +
+
+
+ - + + +
+ + + + +
+ + Bitte geben Sie den Empfänger ein, auf den die Rechnung + ausgestellt wird. + +
+
+ + + +
+ + + + +
+ + Bitte geben Sie die Strasse und Hausnummer, so wie Sie auf der + Rechnung erscheinen soll, ein. + +
+
+ + + +
+
+ + +
+ +
+ + + + +
+ + Bitte geben Sie die PLZ des Ortes, so wie Sie auf der + Rechnung erscheinen soll, ein. + +
+
+
+ + + +
+ + + + +
+ + Bitte geben Sie, falls erforderlich, zusätzliche nformationen + ein. + +
+
+ + + +
+ + + + +
+ + Bitte geben Sie die E-Mail Adresse des Rechnungsempfängers ein. + +
+
+ + +
+
+ + + +
+
+ + {#if $abweichende_versand_adresse} + + +
+ + + + +
+ + Bitte geben Sie den Namen des Versand-Empfängers ein. + +
+
+ + + +
+ + + + +
+ + Bitte geben Sie die Versand-Empfänger Strasse und Hausnummer + ein, an die die Rechnung versandt wird. + +
+
+ + + +
+
+ + +
+ +
+ + + + +
+ + Bitte geben Sie die Versand-Empfänger PLZ des Ortes ein, + an den die Rechnung versandt wird. + +
+
+
+ + + +
+ + + + +
+ + Bitte geben Sie, falls erforderlich, zusätzliche + nformationen ein. + +
+
+ + + +
+ + + + +
+ + Bitte geben Sie die E-Mail Adresse des Versand-Empfängers + ein. + +
+
+ + + {/if} + + {#if !gegAnfrage} @@ -498,7 +863,7 @@ sm:grid-cols-[min-content_min-content_min-content] sm:justify-self-end sm:mt-8" - + {#if gegAnfrage}