diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7832952e..81757e03 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,7 +6,7 @@ { "label": "Docker Container Starten", "type": "shell", - "command": "docker-compose up" + "command": "docker compose up" } ] } \ No newline at end of file diff --git a/cypress/e2e/VerbrauchsausweisWohnen/erstellen.cy.ts b/cypress/e2e/VerbrauchsausweisWohnen/erstellen.cy.ts index f7425691..cf10d396 100644 --- a/cypress/e2e/VerbrauchsausweisWohnen/erstellen.cy.ts +++ b/cypress/e2e/VerbrauchsausweisWohnen/erstellen.cy.ts @@ -241,6 +241,34 @@ describe("Verbrauchsausweis erstellen Schritt 1", () => { cy.get("form[name='login'] button[type='submit']").click(); - // Der Ausweis sollte jetzt schon erstellt worden sein. + // Der Ausweis sollte jetzt schon erstellt worden sein und wir sollten auf die kundendaten seite weitergeleitet worden sein. + cy.url().should("contain", "/kundendaten"); + + // Wir füllen jetzt die Kundendaten aus. + cy.get("input[name='vorname']").should("contain.value", vorname); + cy.get("input[name='name']").should("contain.value", nachname); + cy.get("input[name='email']").should("contain.value", email); + cy.get("input[name='telefon']").type(faker.phone.number()); + + cy.get("input[name='rechnung_empfaenger']").type(`${vorname} ${nachname}`); + cy.get("input[name='rechnung_strasse']").type(faker.location.streetAddress()); + // TODO: Random Plz generieren, allerdings muss die auch in der Datenbank vorhanden sein... + cy.get("input[name='rechnung_plz']").type("2103"); + // Jetzt sollte der PLZ Container erscheinen, dort klicken wir einfach das erste Element an. + cy.get("div[data-test='plz-container']").children().first().click(); + cy.get("input[name='rechnung_telefon']").type(faker.phone.number()); + cy.get("input[name='rechnung_email']").type(faker.internet.email()); + cy.get("button[data-test='paypal']").click(); + + // Datenschutz und AGB akzeptieren, dann schicken wir das Formular ab. + cy.get("input[name='agb-akzeptieren']").check() + cy.get("input[name='datenschutz-akzeptieren']").check() + cy.get("button[type='submit']").click(); + + cy.origin('https://www.mollie.com', () => { + // Jetzt sind wir auf der Mollie Seite, dort wählen wir den "paid" status aus + cy.get("input[type='radio'][name='final_state'][value='paid']").check(); + // Da wird unser Test fehlschlagen, da die localhost domain von Mollie aus nicht erreichbar ist. + }) }); }); diff --git a/package.json b/package.json index 0efb7660..2e8044cf 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "pg": "^8.11.0", "sass": "^1.62.1", "svelte": "^3.59.1", + "svelte-dialogs": "^1.2.2", "svelte-preprocess": "^5.0.3", "tailwindcss": "^3.3.2", "trpc-openapi": "^1.2.0", diff --git a/src/client/lib/spawnLoginPrompt.ts b/src/client/lib/spawnLoginPrompt.ts new file mode 100644 index 00000000..c18e9381 --- /dev/null +++ b/src/client/lib/spawnLoginPrompt.ts @@ -0,0 +1,49 @@ +import { dialogs } from "../../../svelte-dialogs.config"; +import { loginClient } from "#lib/login"; +import { addNotification } from "#components/Notifications/shared"; + +export async function spawnLoginPrompt() { + const result = await dialogs.prompt( + [ + { + label: "Email", + type: "email", + required: true, + placeholder: "Email", + name: "email" + }, + { + label: "Passwort", + type: "password", + name: "passwort", + required: true, + placeholder: "********", + }, + ], + { + title: "Login", + submitButtonText: "Einloggen", + cancelButtonText: "Abbrechen", + } + ); + + if (!result) return false; + + const [email, passwort] = result; + + const loginResult = await loginClient(email, passwort); + + if (loginResult === null) { + addNotification({ + type: "error", + message: "Einloggen fehlgeschlagen", + dismissable: true, + subtext: "Bitte überprüfen Sie ihre Eingaben und versuchen es erneut.", + timeout: 5000, + }) + + return false + } + + return true +} \ No newline at end of file diff --git a/src/client/lib/spawnSignupPrompt.ts b/src/client/lib/spawnSignupPrompt.ts new file mode 100644 index 00000000..f8ba08e6 --- /dev/null +++ b/src/client/lib/spawnSignupPrompt.ts @@ -0,0 +1,67 @@ +import { dialogs } from "../../../svelte-dialogs.config"; +import { addNotification } from "#components/Notifications/shared"; +import { client } from "src/trpc"; + +export async function spawnSignupPrompt() { + const result = await dialogs.prompt( + [ + { + label: "Vorname", + type: "text", + required: true, + placeholder: "Vorname", + name: "vorname" + }, + { + label: "Name", + type: "text", + required: true, + placeholder: "Name", + name: "name" + }, + { + label: "Email", + type: "email", + required: true, + placeholder: "Email", + name: "email" + }, + { + label: "Passwort", + type: "password", + name: "passwort", + required: true, + placeholder: "********", + }, + ], + { + title: "Registrieren", + submitButtonText: "Registrieren", + cancelButtonText: "Abbrechen", + } + ); + + if (!result) return false; + + const [vorname, name, email, passwort] = result; + + try { + const response = await client.v1.benutzer.erstellen.mutate({ + email, + passwort, + vorname, + name, + }); + + return true; + } catch(e) { + addNotification({ + type: "error", + message: "Registrieren fehlgeschlagen", + dismissable: true, + subtext: "Ein Fehler ist aufgetreten, vielleicht wird die angegebene Email bereits verwendet.", + timeout: 5000, + }) + return false; + } +} \ No newline at end of file diff --git a/src/client/tickets/createTicket.ts b/src/client/tickets/createTicket.ts new file mode 100644 index 00000000..aec6754f --- /dev/null +++ b/src/client/tickets/createTicket.ts @@ -0,0 +1,7 @@ +import { AppRouter } from "@ibcornelsen/api"; +import { inferProcedureInput } from "@trpc/server"; +import { client } from "src/trpc"; + +export async function createTicket(info: inferProcedureInput) { + return await client.v1.tickets.erstellen.mutate(info) +} \ No newline at end of file diff --git a/src/client/tickets/index.ts b/src/client/tickets/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/components/Ausweis/types.ts b/src/components/Ausweis/types.ts index d9960b26..cd8e84df 100644 --- a/src/components/Ausweis/types.ts +++ b/src/components/Ausweis/types.ts @@ -1,6 +1,6 @@ import { AppRouter } from "@ibcornelsen/api"; -import { GebaeudeBilder } from "@ibcornelsen/database/client"; -import { inferProcedureInput } from "@trpc/server"; +import { Benutzer, GebaeudeBilder } from "@ibcornelsen/database/client"; +import { inferProcedureInput, inferProcedureOutput } from "@trpc/server"; export type UploadedGebaeudeBild = Omit< GebaeudeBilder, @@ -65,4 +65,6 @@ export type BedarfsausweisWohnenClient = inferProcedureInput< */ export type GebaeudeClient = inferProcedureInput< AppRouter["v1"]["verbrauchsausweisWohnen"]["2016"]["speichern"] ->["gebaeude"]; \ No newline at end of file +>["gebaeude"]; + +export type BenutzerClient = inferProcedureOutput \ No newline at end of file diff --git a/src/components/PaymentOption.svelte b/src/components/PaymentOption.svelte index 236580eb..3e6a3ca5 100644 --- a/src/components/PaymentOption.svelte +++ b/src/components/PaymentOption.svelte @@ -7,7 +7,7 @@ export let selectedPaymentType: Bezahlmethoden; - \ No newline at end of file diff --git a/src/modules/Ausweise/VerbrauchsausweisWohnenModule.svelte b/src/modules/Ausweise/VerbrauchsausweisWohnenModule.svelte index 3489270b..8e05c17a 100644 --- a/src/modules/Ausweise/VerbrauchsausweisWohnenModule.svelte +++ b/src/modules/Ausweise/VerbrauchsausweisWohnenModule.svelte @@ -20,14 +20,22 @@ import EmbeddedAuthFlowModule from "#modules/EmbeddedAuthFlowModule.svelte"; import AusweisGespeichertModule from "./AusweisGespeichertModule.svelte"; import { validateAccessTokenClient } from "src/client/lib/validateAccessToken"; - import { UploadedGebaeudeBild, VerbrauchsausweisWohnenClient, GebaeudeClient } from "#components/Ausweis/types"; - + import { UploadedGebaeudeBild, VerbrauchsausweisWohnenClient, GebaeudeClient, BenutzerClient } from "#components/Ausweis/types"; + export let gebaeude: GebaeudeClient = {} as GebaeudeClient; export let images: UploadedGebaeudeBild[] = []; export let ausweis: VerbrauchsausweisWohnenClient = {} as VerbrauchsausweisWohnenClient; - - async function uploadImages() { + export let user: BenutzerClient = {} as BenutzerClient; + + async function bilderHochladen() { // Alle Bilder hochladen + const notification = addNotification({ + dismissable: false, + message: "Bilder hochladen.", + subtext: `${images.length} Bilder werden hochgeladen, bitte haben sie Geduld.`, + timeout: 0, + type: "info" + }) for (let i = 0; i < images.length; i++) { const image = images[i]; if (image.uid) { @@ -35,14 +43,6 @@ continue } - const notification = addNotification({ - dismissable: false, - message: "Bilder hochladen.", - subtext: `Bild ${i + 1}/${images.length} wird hochgeladen, bitte haben sie Geduld.`, - timeout: 0, - type: "info" - }) - try { const response = await client.v1.bilder.upload.mutate({ base64: image.base64, @@ -55,7 +55,7 @@ updateNotification(notification, { dismissable: true, message: "Bild hochgeladen.", - subtext: `Bild ${i + 1}/${images.length} wurde erfolgreich hochgeladen.`, + subtext: `${i + 1}/${images.length} Bildern wurden erfolgreich hochgeladen, bitte haben sie Geduld.`, timeout: 4000 }) } catch (e) { @@ -74,6 +74,8 @@ // 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. + //await spawnLoginPrompt(); loginOverlayHidden = false; return } @@ -89,23 +91,14 @@ gebaeude }) - await uploadImages(); + await bilderHochladen(); return true; } catch (e) { - addNotification({ - dismissable: false, - message: "Ausweis konnte nicht gespeichert werden, bitte versuchen sie es erneut.", - subtext: "Sollte das Problem weiterhin bestehen, kontaktieren sie bitte den Support.", - timeout: 6000, - type: "error" - }) - return false + // TODO: Ticket mit Fehldermeldung abschicken. } } else { // Wir speichern den Ausweis ab und leiten auf die "ausweis-gespeichert" Seite weiter. - console.log(ausweis, gebaeude); - try { const response = await client.v1.verbrauchsausweisWohnen[2016].erstellen.mutate({ ausweis, @@ -114,20 +107,31 @@ ausweis.uid = response.uid; gebaeude.uid = response.gebaeude_uid; - await uploadImages(); + await bilderHochladen(); return true; - } catch (e) { - addNotification({ - dismissable: false, - message: "Ausweis konnte nicht gespeichert werden, bitte versuchen sie es erneut.", - subtext: "Sollte das Problem weiterhin bestehen, kontaktieren sie bitte den Support.", - timeout: 6000, - type: "error" + } catch (e: any) { + await client.v1.tickets.erstellen.mutate({ + titel: "Ausweis konnte nicht gespeichert werden", + beschreibung: e.stack, + email: user.email ?? "", + metadata: JSON.stringify({ + ausweis, + gebaeude + }) }) - return false; + // TODO: Ticket mit Fehldermeldung abschicken. } } + + addNotification({ + dismissable: false, + message: "Ausweis konnte nicht gespeichert werden, bitte versuchen sie es erneut.", + subtext: "Sollte das Problem weiterhin bestehen, kontaktieren sie bitte den Support.", + timeout: 6000, + type: "error" + }) + return false } async function spaeterWeitermachen() { @@ -165,7 +169,7 @@ async function ausweisAbschicken(e: SubmitEvent) { - e.preventDefault() + if (e && e.preventDefault) e.preventDefault(); const result = await ausweisSpeichern(); if (result === true) { @@ -180,7 +184,7 @@
- +
diff --git a/src/modules/EmbeddedAuthFlowModule.svelte b/src/modules/EmbeddedAuthFlowModule.svelte index df59f86a..e0827bba 100644 --- a/src/modules/EmbeddedAuthFlowModule.svelte +++ b/src/modules/EmbeddedAuthFlowModule.svelte @@ -1,8 +1,9 @@
@@ -74,7 +68,7 @@
-
+
@@ -101,7 +95,7 @@
- +
@@ -130,7 +124,7 @@
@@ -187,13 +182,13 @@
- +
- +
@@ -207,7 +202,7 @@ @@ -217,7 +212,7 @@

- - - - - + + + + +
{selectedPaymentType}
- {#if selectedPaymentType == Enums.Bezahlmethoden.RECHNUNG} + {#if selectedPaymentType == Enums.Bezahlmethoden.rechnung}

Sobald sie AGB und Datenschutzerklärung gelesen und akzeptiert haben können sie den @@ -322,8 +317,8 @@ {/if}

- -
diff --git a/src/modules/LoginModule.svelte b/src/modules/LoginModule.svelte index b883d503..2e1a33f1 100644 --- a/src/modules/LoginModule.svelte +++ b/src/modules/LoginModule.svelte @@ -8,21 +8,22 @@ export let redirect: string | null = null; async function login(e: SubmitEvent) { - e.preventDefault() - const response = await loginClient(email, passwort) + e.preventDefault(); + const response = await loginClient(email, passwort); if (response === null) { addNotification({ message: "Ups...", - subtext: "Das hat leider nicht geklappt, haben sie ihr Passwort und ihre Email Adresse richtig eingegeben?", + subtext: + "Das hat leider nicht geklappt, haben sie ihr Passwort und ihre Email Adresse richtig eingegeben?", type: "error", timeout: 6000, - dismissable: true - }) + dismissable: true, + }); } else { if (redirect) { - window.location.href = redirect - return + window.location.href = redirect; + return; } window.location.href = "/user"; @@ -55,10 +56,12 @@ required />
- +
diff --git a/src/pages/energieausweis-erstellen/verbrauchsausweis-wohnen/index.astro b/src/pages/energieausweis-erstellen/verbrauchsausweis-wohnen/index.astro index 8cd9eafa..737f8136 100644 --- a/src/pages/energieausweis-erstellen/verbrauchsausweis-wohnen/index.astro +++ b/src/pages/energieausweis-erstellen/verbrauchsausweis-wohnen/index.astro @@ -4,8 +4,9 @@ import AusweisLayout from "#layouts/AusweisLayout.astro"; import VerbrauchsausweisWohnenModule from "#modules/Ausweise/VerbrauchsausweisWohnenModule.svelte"; import { prisma } from "@ibcornelsen/database/server"; import { exclude } from "#lib/exclude"; -import { UploadedGebaeudeBild, VerbrauchsausweisWohnenClient, GebaeudeClient } from "#components/Ausweis/types"; +import { UploadedGebaeudeBild, VerbrauchsausweisWohnenClient, GebaeudeClient, BenutzerClient } from "#components/Ausweis/types"; import { createCaller } from "#lib/caller"; +import { API_UID_COOKIE_NAME } from "#lib/constants"; const uid = Astro.url.searchParams.get("uid"); let ausweis: VerbrauchsausweisWohnenClient = {} as VerbrauchsausweisWohnenClient; @@ -44,6 +45,14 @@ if (uid) { } } +const benutzerUid = Astro.cookies.get(API_UID_COOKIE_NAME).value +let user: BenutzerClient = {} as BenutzerClient; +if (benutzerUid) { + user = await createCaller(Astro).v1.benutzer.fromPublicId({ + uid: benutzerUid + }) +} + --- diff --git a/svelte-dialogs.config.ts b/svelte-dialogs.config.ts new file mode 100644 index 00000000..f3bf5fd5 --- /dev/null +++ b/svelte-dialogs.config.ts @@ -0,0 +1,20 @@ +import { dialogs } from "svelte-dialogs"; + +dialogs.config({ + global: { + inputClass: "px-2.5 py-1.5 rounded-lg border bg-white", + inputLabelClass: "text-gray-500 bg-white ml-2 w-fit", + bodyClass: "bg-white flex flex-col gap-4", + dialogClass: "bg-white rounded-md px-8 py-8", + cancelButtonClass: "button", + footerClass: "flex justify-end bg-white mt-4 gap-4", + resetButton: false, + submitButtonClass: "button", + titleClass: "text-2xl font-bold mb-4", + dividerClass: "border-b border-gray-200", + closeOnBg: true, + closeOnEsc: true + } +}); + +export { dialogs } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index a40ff826..6308706c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,6 +25,8 @@ "#components/*": ["./src/components/*"], "#layouts/*": ["./src/layouts/*"], "#modules/*": ["./src/modules/*"], + "#client/*": ["./src/modules/*"], + "#server/*": ["./src/modules/*"], }, "types": ["cypress", "cypress-file-upload", "bun-types"] }