diff --git a/bun.lock b/bun.lock index 6fe042a6..3da67b33 100644 --- a/bun.lock +++ b/bun.lock @@ -17,7 +17,7 @@ "@trpc/client": "^10.45.2", "@trpc/server": "^10.45.2", "astro": "^4.16.17", - "astro-typesafe-api": "^0.2.1", + "astro-typesafe-api": "^0.2.2", "body-scroll-lock": "^4.0.0-beta.0", "buffer": "^6.0.3", "bun": "^1.1.45", @@ -656,7 +656,7 @@ "astro": ["astro@4.16.18", "", { "dependencies": { "@astrojs/compiler": "^2.10.3", "@astrojs/internal-helpers": "0.4.1", "@astrojs/markdown-remark": "5.3.0", "@astrojs/telemetry": "3.1.0", "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx": "^7.25.9", "@babel/types": "^7.26.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.3", "@types/babel__core": "^7.20.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.1.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^0.7.2", "cssesc": "^3.0.0", "debug": "^4.3.7", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.5.4", "esbuild": "^0.21.5", "estree-walker": "^3.0.3", "fast-glob": "^3.3.2", "flattie": "^1.1.1", "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", "html-escaper": "^3.0.3", "http-cache-semantics": "^4.1.1", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.14", "magicast": "^0.3.5", "micromatch": "^4.0.8", "mrmime": "^2.0.0", "neotraverse": "^0.6.18", "ora": "^8.1.1", "p-limit": "^6.1.0", "p-queue": "^8.0.1", "preferred-pm": "^4.0.0", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.6.3", "shiki": "^1.23.1", "tinyexec": "^0.3.1", "tsconfck": "^3.1.4", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3", "vite": "^5.4.11", "vitefu": "^1.0.4", "which-pm": "^3.0.0", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-G7zfwJt9BDHEZwlaLNvjbInIw2hPryyD654314KV/XT34pJU6SfN1S+mWa8RAkALcZNJnJXCJmT3JXLQStD3Lw=="], - "astro-typesafe-api": ["astro-typesafe-api@0.2.1", "", { "dependencies": { "es-codec": "^0.5.0", "globby": "^14.0.2" }, "peerDependencies": { "astro": "^4.16.17", "typescript": "^5.0.0", "zod": "^3.24.1" }, "bin": { "astro-typesafe-api": "src/cli.ts" } }, "sha512-8f0McZj9fWIzT19njJ2z/1zETnbper3ejuba93t72Xvsy6aMTEDXaIGDG3xc9KWUQ9zEcNg+VS52JNWGfYm6CQ=="], + "astro-typesafe-api": ["astro-typesafe-api@0.2.2", "", { "dependencies": { "es-codec": "^0.5.0", "globby": "^14.0.2" }, "peerDependencies": { "astro": "^4.16.17", "typescript": "^5.0.0", "zod": "^3.24.1" }, "bin": { "astro-typesafe-api": "src/cli.ts" } }, "sha512-SEHV2iPyIrdpYdYb0mIN1WmcvC61bvsCQqb/X+R4EOcFjuozJ9fJhSiFGxJMvNoxJ9S3P3GKLyDnxXvFlKq0mw=="], "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], diff --git a/package.json b/package.json index 8c120117..71398a1b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@trpc/client": "^10.45.2", "@trpc/server": "^10.45.2", "astro": "^4.16.17", - "astro-typesafe-api": "^0.2.1", + "astro-typesafe-api": "^0.2.2", "body-scroll-lock": "^4.0.0-beta.0", "buffer": "^6.0.3", "bun": "^1.1.45", diff --git a/src/astro-typesafe-api-caller.ts b/src/astro-typesafe-api-caller.ts index 81eb88cc..edb1fa17 100644 --- a/src/astro-typesafe-api-caller.ts +++ b/src/astro-typesafe-api-caller.ts @@ -8,8 +8,9 @@ export const createCaller = createCallerFactory({ "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"), - "bilder/[uid]": await import("../src/pages/api/bilder/[uid].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"), "rechnung": await import("../src/pages/api/rechnung/index.ts"), "ticket": await import("../src/pages/api/ticket/index.ts"), diff --git a/src/components/Ausweis/AusweisPreviewContainer.svelte b/src/components/Ausweis/AusweisPreviewContainer.svelte index 86e65963..eb355ae0 100644 --- a/src/components/Ausweis/AusweisPreviewContainer.svelte +++ b/src/components/Ausweis/AusweisPreviewContainer.svelte @@ -24,7 +24,7 @@ export let ausweisart: Enums.Ausweisart; -
+
@@ -65,8 +65,6 @@
- -
diff --git a/src/components/Ausweis/ButtonWeiterHilfe.svelte b/src/components/Ausweis/ButtonWeiterHilfe.svelte index 357c5ccb..a25123aa 100644 --- a/src/components/Ausweis/ButtonWeiterHilfe.svelte +++ b/src/components/Ausweis/ButtonWeiterHilfe.svelte @@ -1,15 +1,11 @@
Später Weitermachen -
- +
+ +
+ +
+
+ +
diff --git a/src/components/Ausweis/DaemmungImage.svelte b/src/components/Ausweis/DaemmungImage.svelte index c648edd0..e1926614 100644 --- a/src/components/Ausweis/DaemmungImage.svelte +++ b/src/components/Ausweis/DaemmungImage.svelte @@ -8,17 +8,17 @@ export let objekt: ObjektClient; -
+
-
+
-

+

WICHTIG: Bild Upload - Wärmedämmung (2 bis 4 Bilder) -

+
-

+

Seit Mai 2021 wird das neue Gebäudeenergiegesetz (GEG) in Deutschland angewendet. Daher werden von nun an Bilder vom Gebäude zur Einschätzung der Modernisierungsempfehlungen benötigt. Hierfür @@ -26,23 +26,17 @@ Gebäudehülle und der Anlagentechnik (Wärmeerzeuger etc.) zur Verfügung gestellt werden. Diese Bilder erscheinen nicht auf Ihrem Energieausweis. -

+

  1. 1.Bild : Detailbild Dach bzw. des Dachinnenraums*
  2. -
  3. - 2.Bild : (weiteres Detailbild Dach bzw. des Dachinnenraums) -
  4. +
  5. 2.Bild :(weiteres Detailbild Dach bzw. des Dachinnenraums)
  6. 3.Bild : Detailbild der Außenwand*
  7. -
  8. - 4.Bild : (weiteres Detailbild der Außenwand) -
  9. +
  10. 4.Bild : (weiteres Detailbild der Außenwand)
-

* erforderliches Bild

+
* erforderliches Bild

-

+

Idealerweise sollte Dämmung oder nicht vorhandene Dämmung gut zu erkennen sein. Sollte aufgrund der Verkleidung bzw. Verschalung, der Dämmzustand nicht erkennbar sein, reicht ein Bild vom ausgebauten @@ -50,15 +44,15 @@ beim Detailbild der Außenwand aufgrund des Wandaufbaus die Dämmung nicht erkennbar sein, reicht ein normales Bild der Außenwand und/oder vom Dach-Wandanschluss. -

+
-
-

+

+
Diese Bilder erscheinen nicht auf Ihrem Energieausweis!
Bitte laden Sie hier mind. 2 Bilder hoch: -

+
-
+
-
+
-

+

WICHTIG: Bild Upload - Fenster/Dachfenster/Türen (1 bis 4 Bilder) -

+
-

+

Seit Mai 2021 wird das neue Gebäudeenergiegesetz (GEG) in Deutschland angewendet. Daher werden von nun an Bilder vom Gebäude zur Einschätzung der Modernisierungsempfehlungen benötigt. Hierfür @@ -25,36 +25,30 @@ Gebäudehülle und der Anlagentechnik (Wärmeerzeuger etc.) zur Verfügung gestellt werden. Diese Bilder erscheinen nicht auf Ihrem Energieausweis. -

-
+
+
  1. 1. Bild : Exemplarisches Bild eines Fensters*
  2. -
  3. - 2.Bild : (Im Baualter abweichendes Fenster) -
  4. -
  5. - 3.Bild : (Im Baualter abweichendes Fenster) -
  6. -
  7. - 4.Bild : (wenn möglich, Bild der Haustür) -
  8. +
  9. 2.Bild : (Im Baualter abweichendes Fenster)
  10. +
  11. 3.Bild : (Im Baualter abweichendes Fenster)
  12. +
  13. 4.Bild : (wenn möglich, Bild der Haustür)
-

* erforderliches Bild

+
* erforderliches Bild

-

+

Wenn eine Fensterart bzw. Fensterqualität verbaut wurde, reicht ein exemplarisches Bild, sonst pro Art ein Bild. Wenn möglich eine Großaufnahme des Fensters bzw. des Fensterfalzes. Idealerweise sollte der Datumsaufdruck am Verglasungsrahmen zu erkennen sein. -

+
-
-

+

+
Diese Bilder erscheinen nicht auf Ihrem Energieausweis!
Bitte laden Sie hier mind. 1 Bild hoch: -

+
-
+
-
+
- -

+

WICHTIG: Bild Upload - Heizungsanlage bzw. des Wärmeerzeugers (1 bis 4 Bilder) -

+
-

+

Seit Mai 2021 wird das neue Gebäudeenergiegesetz (GEG) in Deutschland angewendet. Daher werden von nun an Bilder vom Gebäude zur Einschätzung der Modernisierungsempfehlungen benötigt. Hierfür @@ -26,31 +25,31 @@ Gebäudehülle und der Anlagentechnik (Wärmeerzeuger etc.) zur Verfügung gestellt werden. Diese Bilder erscheinen nicht auf Ihrem Energieausweis. -

+

-
    +
    1. 1.Bild : Heizungsraum mit Heizkessel bzw. Heizungsanlage*
    2. 2.Bild : (wenn möglich, Warmwasserleitungen bzw. die Heizungsrohre)
    3. 3.Bild : (wenn möglich, Detailbild des Wärmeerzeugers bzw. Heizkessel)
    4. 4.Bild : (wenn möglich, Bild des Typenschildes der Heizung)

    -

    * erforderliches Bild

    +
    * erforderliches Bild

    -

    +

    Idealerweise sollte der Heizungsraum mit Heizkessel bzw. Heizungsanlage fotografiert werden. Die Warmwasserleitungen bzw. die Heizungsrohre sollten gut sichtbar sein und vorhandene bzw. nicht vorhandene Dämmung sollte erkennbar sein. -

    +
-
-

+

+
Diese Bilder erscheinen nicht auf Ihrem Energieausweis!
Bitte laden Sie hier mind. 1 Bild hoch: -

+
export let progress: number = 0; + export let step1: string; + export let step2: string; + export let step3: string; @@ -13,19 +16,18 @@
-
-
1
+
1
Gebäudedaten
-
2
+
2
Kundendaten
-
3
+
3
Kaufbestätigung
@@ -35,7 +37,8 @@ \ No newline at end of file diff --git a/src/components/Ausweis/SanierungszustandFensterTueren.svelte b/src/components/Ausweis/SanierungszustandFensterTueren.svelte index f2b7e534..8ccc2906 100644 --- a/src/components/Ausweis/SanierungszustandFensterTueren.svelte +++ b/src/components/Ausweis/SanierungszustandFensterTueren.svelte @@ -27,7 +27,7 @@ class="bereich-box grid grid-cols-1 gap-x-4 gap-y-2 - sm:grid-cols-2 sm:gap-x-6 sm:gap-y-8 + sm:grid-cols-1 sm:gap-x-6 sm:gap-y-1 md:grid-cols-2 md:gap-x-6 md:gap-y-8 xl:grid-cols-2 xl:gap-x-8 xl:gap-y-8 diff --git a/src/components/Ausweis/SanierungszustandHeizungsanlage.svelte b/src/components/Ausweis/SanierungszustandHeizungsanlage.svelte index 9726eef8..92d90bf6 100644 --- a/src/components/Ausweis/SanierungszustandHeizungsanlage.svelte +++ b/src/components/Ausweis/SanierungszustandHeizungsanlage.svelte @@ -25,9 +25,9 @@
-
- - Wir benötigen diese Angaben um den allgemeinen Modernisierungsstand - einschätzen zu können. Bitte setzen Sie den Haken wenn zutreffend. - Das seit Mai 2021 gültige GEG erfordert eine genauere Prüfung anhand - von Fotos. Bitte laden Sie ein oder mehrere Fotos der Außenwand und - des Dachbereiches hoch. - -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
- -
- - - -
- - - - - - --> - diff --git a/src/components/Ausweis/SanierungszustandWaermedammung.svelte b/src/components/Ausweis/SanierungszustandWaermedammung.svelte index 21107b01..8ea58bb9 100644 --- a/src/components/Ausweis/SanierungszustandWaermedammung.svelte +++ b/src/components/Ausweis/SanierungszustandWaermedammung.svelte @@ -1,6 +1,5 @@ -
{ +
{ hideZipDropdown = true; }}> @@ -65,13 +65,19 @@ maxlength="5" /> - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/src/components/design/content/WidgetCardTemplate.svelte b/src/components/design/content/WidgetCardTemplate.svelte index b277ed68..2ac3ad3b 100644 --- a/src/components/design/content/WidgetCardTemplate.svelte +++ b/src/components/design/content/WidgetCardTemplate.svelte @@ -8,6 +8,7 @@ export let src: string; export let alt: string; export let empfehlung: string; + export let cta: string; @@ -35,7 +36,7 @@
jetzt online erstellen{cta} -
-
+
+
{bereich}
{title}
{ open = !open }} @@ -26,7 +26,7 @@
-
-
+ +
\ No newline at end of file diff --git a/src/components/labels/BereichLabel.svelte b/src/components/labels/BereichLabel.svelte index 8c61093b..cd360bde 100644 --- a/src/components/labels/BereichLabel.svelte +++ b/src/components/labels/BereichLabel.svelte @@ -12,7 +12,7 @@ collapse.style.overflow = "hidden"; } else { //y.classList.add('hidden'); - collapse.style.maxHeight = "1500px"; + collapse.style.maxHeight = "1800px"; collapse.style.overflow = "visible"; } @@ -23,13 +23,13 @@
{bereich}
{#if open} diff --git a/src/components/labels/InputLabel.svelte b/src/components/labels/InputLabel.svelte index 2ae38d24..2cc68382 100644 --- a/src/components/labels/InputLabel.svelte +++ b/src/components/labels/InputLabel.svelte @@ -4,7 +4,7 @@ -
{title}
+
{title}
diff --git a/src/components/widgets/WelcherAusweisWidget_1.svelte b/src/components/widgets/WelcherAusweisWidget_1.svelte index 2d44f11a..67b3c65f 100644 --- a/src/components/widgets/WelcherAusweisWidget_1.svelte +++ b/src/components/widgets/WelcherAusweisWidget_1.svelte @@ -210,6 +210,7 @@ threeBOX = ((ausnahme === true) && (gebaeudetyp === "Mischgebäude") && (twoBo alt="Wohnhaus Verbrauchsausweis" variant="einfach" empfehlung="nein" + cta="jetzt online erstellen" services={[ ["3 Jahresverbräuche der Heizung benötigt.", true], ["Zulässig bei Vermietung oder Verkauf.", true], @@ -235,6 +236,7 @@ threeBOX = ((ausnahme === true) && (gebaeudetyp === "Mischgebäude") && (twoBo alt="Wohnhaus Bedarfsausweis" variant="fundiert" empfehlung="ja" + cta="jetzt online erstellen" services={[ ["Erfassung der Gebäudegeometrie.", true], ["Für Vermietung, Verkauf und Finanzierung.", true], @@ -259,7 +261,8 @@ threeBOX = ((ausnahme === true) && (gebaeudetyp === "Mischgebäude") && (twoBo src="/images/immowelt/gewerbegebaeude_immowelt.svg" alt="Gewerbe Verbrauchsausweis" variant="einfach" - empfehlung="nein" + empfehlung="nein" + cta="jetzt online erstellen" services={[ ["3 Jahresverbräuche von Heizung Gebäudestrom nötig.", true], @@ -287,6 +290,7 @@ threeBOX = ((ausnahme === true) && (gebaeudetyp === "Mischgebäude") && (twoBo alt="Gewerbe Bedarfsausweis" variant="fundiert" empfehlung="ja" + cta="Angebot anfragen" services={[ ["Mehrzonenmodell nach DIN 18599.", true], @@ -313,6 +317,7 @@ threeBOX = ((ausnahme === true) && (gebaeudetyp === "Mischgebäude") && (twoBo alt="Gewerbe Bedarfsausweis" variant="Bauvorlage" empfehlung="ja" + cta="Angebot anfragen" services={[ ["Nachweis fürs Bauamt bei Neubau oder Modernisierung.", true], @@ -341,6 +346,7 @@ threeBOX = ((ausnahme === true) && (gebaeudetyp === "Mischgebäude") && (twoBo alt="Gewerbe Bedarfsausweis" variant="Bauvorlage" empfehlung="ja" + cta="Angebot anfragen" services={[ ["Nachweis fürs Bauamt bei Neubau oder Modernisierung.", true], diff --git a/src/layouts/AusweisLayoutDaten_immowelt.astro b/src/layouts/AusweisLayoutDaten_immowelt.astro index c1c6e2a7..a15e4c28 100644 --- a/src/layouts/AusweisLayoutDaten_immowelt.astro +++ b/src/layouts/AusweisLayoutDaten_immowelt.astro @@ -85,7 +85,7 @@ window.addEventListener("scroll", (event) => { -
+
diff --git a/src/layouts/UserLayout.astro b/src/layouts/UserLayout.astro index 6c5f8b1f..f4eb6b98 100644 --- a/src/layouts/UserLayout.astro +++ b/src/layouts/UserLayout.astro @@ -6,6 +6,8 @@ import "svelte-ripple-action/ripple.css" import DashboardSidebar from "../components/Dashboard/DashboardSidebar.svelte" import { validateAccessTokenServer } from "#server/lib/validateAccessToken"; import { createCaller } from "src/astro-typesafe-api-caller"; +import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants"; +import { BenutzerClient } from "#components/Ausweis/types"; const valid = validateAccessTokenServer(Astro) @@ -16,7 +18,11 @@ if (!valid) { const caller = createCaller(Astro) -const benutzer = await caller.v1.benutzer.self() +const benutzer = (await caller.user.self.GET.fetch(undefined, { + headers: { + Authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}` + } +})) || {} as BenutzerClient; export interface Props { title: string; @@ -101,7 +107,7 @@ let lightTheme = Astro.cookies.get("theme")?.value === "light"; - +
diff --git a/src/lib/pdf/pdfVerbrauchsausweisWohnen.ts b/src/lib/pdf/pdfVerbrauchsausweisWohnen.ts index 5b7d8240..0f3f2bd0 100644 --- a/src/lib/pdf/pdfVerbrauchsausweisWohnen.ts +++ b/src/lib/pdf/pdfVerbrauchsausweisWohnen.ts @@ -4,7 +4,7 @@ import { getEmpfehlungen } from "#lib/XML/getEmpfehlungen.js"; import { Enums } from "@ibcornelsen/database/server"; import * as fs from "fs" import moment from "moment"; -import { PDFDocument, PDFFont, PDFName, PDFNumber, PDFPage, StandardFonts, TextAlignment } from "pdf-lib"; +import { PDFDocument, PDFFont, PDFPage, RotationTypes, StandardFonts, TextAlignment } from "pdf-lib"; /* -------------------------------- Pdf Tools ------------------------------- */ @@ -82,7 +82,6 @@ export async function pdfVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohne /* -------------------------------- Seite 2 -------------------------------- */ - const co2Emissionen = fillFormField("co2emissionen", berechnungen?.co2EmissionenGesamt.toString(), 8, TextAlignment.Center) const addEnergieverbrauchSkalaPfeile = async (page: PDFPage) => { const pfeilNachUnten = await pdf.embedPng(fs.readFileSync(new URL("../../../public/images/pfeil-nach-unten.png", import.meta.url), "base64")) @@ -172,11 +171,7 @@ export async function pdfVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohne } } - addEnergieverbrauchSkalaPfeile(pages[1]) - addEnergieverbrauchSkalaPfeile(pages[2]) - - const primaerenergiebedarfIst = fillFormField("primaerenergiebedarf_ist", berechnungen?.primaerEnergieVerbrauchGesamt.toString()) - + addEnergieverbrauchSkalaPfeile(pages[2]) /* -------------------------------- Seite 3 -------------------------------- */ @@ -369,6 +364,22 @@ export async function pdfVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohne } + function addAnsichtsausweisLabel(page: PDFPage, font: PDFFont) { + page.drawText("Ansichtsausweis", { + x: page.getWidth() / 2 - font.heightAtSize(112) * 2.2, y: page.getHeight() - font.heightAtSize(112) / 2, + size: 112, + font, + rotate: { + type: RotationTypes.Degrees, + angle: -60 + }, + opacity: 0.3 + }) + } + + for (const page of pages) { + addAnsichtsausweisLabel(page, font) + } // pdf.getForm().flatten() diff --git a/src/modules/Dashboard/DashboardModule.svelte b/src/modules/Dashboard/DashboardModule.svelte index 4f929f45..33ee0fb1 100644 --- a/src/modules/Dashboard/DashboardModule.svelte +++ b/src/modules/Dashboard/DashboardModule.svelte @@ -3,6 +3,9 @@ export let user: BenutzerClient; export let objekte: ObjektClient[]; + + console.log(objekte); +

Willkommen zurück, {user.vorname}!

@@ -12,7 +15,7 @@

Gebäude

- {#each objekte as objekt} +
\ No newline at end of file diff --git a/src/modules/KundendatenModule.svelte b/src/modules/KundendatenModule.svelte index 7c2f666a..4177c481 100644 --- a/src/modules/KundendatenModule.svelte +++ b/src/modules/KundendatenModule.svelte @@ -1,6 +1,6 @@ - - -
- -
-
- - \ No newline at end of file diff --git a/src/modules/VerbrauchsausweisWohnen/VerbrauchsausweisWohnenModule.svelte b/src/modules/VerbrauchsausweisWohnen/VerbrauchsausweisWohnenModule.svelte index 4877670d..5de3ea27 100644 --- a/src/modules/VerbrauchsausweisWohnen/VerbrauchsausweisWohnenModule.svelte +++ b/src/modules/VerbrauchsausweisWohnen/VerbrauchsausweisWohnenModule.svelte @@ -193,7 +193,7 @@ lg:grid-cols-2 lg:gap-x-6

Energiesausweis erstellen

{ausweisart} {PRICES.VerbrauchsausweisWohnen[0]} €

- +
diff --git a/src/pages/api/bedarfsausweis-wohnen/[uid].ts b/src/pages/api/bedarfsausweis-wohnen/[uid].ts new file mode 100644 index 00000000..32f1d29d --- /dev/null +++ b/src/pages/api/bedarfsausweis-wohnen/[uid].ts @@ -0,0 +1,217 @@ +import { BedarfsausweisWohnenClient, OptionalNullable, UUidWithPrefix, ZodOverlap } from "#components/Ausweis/types.js"; +import { exclude } from "#lib/exclude.js"; +import { authorizationHeaders, authorizationMiddleware } from "#lib/middleware/authorization.js"; +import { BedarfsausweisWohnenSchema, prisma } from "@ibcornelsen/database/server"; +import { APIError, defineApiRoute } from "astro-typesafe-api/server"; +import { z } from "zod"; + +export const PATCH = defineApiRoute({ + input: BedarfsausweisWohnenSchema.omit({ + uid: true, + id: true, + benutzer_id: true, + aufnahme_id: true, + }), + output: z.void(), + headers: { + "Authorization": z.string() + }, + middleware: authorizationMiddleware, + async fetch(input, ctx, user) { + const objekt = await prisma.bedarfsausweisWohnen.findUnique({ + where: { + uid: ctx.params.uid, + benutzer: { + id: user.id + } + } + }) + + if (!objekt) { + throw new APIError({ + code: "NOT_FOUND", + message: "Ausweis konnte nicht gefunden werden oder gehört einem anderen Benutzer." + }) + } + + await prisma.bedarfsausweisWohnen.update({ + where: { + uid: ctx.params.uid + }, + data: input + }) + }, +}) + +export const DELETE = defineApiRoute({ + meta: { + description: "Storniert einen Ausweis" + }, + headers: authorizationHeaders, + middleware: authorizationMiddleware, + async fetch(input, ctx, user) { + const { uid } = ctx.params; + + if (!UUidWithPrefix.safeParse(uid).success) { + throw new APIError({ + code: "BAD_REQUEST", + message: "UID konnte nicht verifiziert werden." + }) + } + + // Wir holen uns den Bedarfsausweis + // Dieser MUSS mit dem Nutzer verknüpft sein. + const ausweis = await prisma.bedarfsausweisWohnen.findUnique({ + where: { + uid, + }, + include: { + aufnahme: { + select: { + storniert: true + } + } + } + }); + + if (!ausweis) { + // Falls wir den Ausweis nicht finden können, werfen wir einen Fehler + throw new APIError({ + code: "NOT_FOUND", + message: "Ausweis konnte nicht gefunden werden.", + }); + } + + // Wir dürfen den Ausweis nur stornieren, wenn er noch nicht ausgestellt wurde + // Außerdem müssen wir schauen, ob wir Admin oder der Besitzer des Ausweises sind. + if ((ausweis.benutzer_id !== user.id) && user.rolle !== "ADMIN") { + // Falls der Ausweis nicht dem Nutzer gehört, werfen wir einen Fehler + throw new APIError({ + code: "FORBIDDEN", + message: "Ausweis gehört nicht dem Nutzer.", + }); + } + + // if (ausweis.erledigt) { + // // Falls der Ausweis bereits ausgestellt wurde, werfen wir einen Fehler + // throw new TRPCError({ + // code: "BAD_REQUEST", + // message: "Ausweis wurde bereits ausgestellt.", + // }); + // } + + if (ausweis.aufnahme.storniert) { + // Falls der Ausweis bereits storniert ist, werfen wir einen Fehler + throw new APIError({ + code: "BAD_REQUEST", + message: "Ausweis wurde bereits storniert.", + }); + } + + await prisma.aufnahme.update({ + where: { + id: ausweis.aufnahme_id + }, + data: { + storniert: true + } + }) + + // Wir erstellen ein Event, dass der Ausweis storniert wurde + // Dann können wir das in der Historie anzeigen + await prisma.event.create({ + data: { + title: "Ausweis storniert", + description: ((user.rolle === "ADMIN") && (ausweis.benutzer_id !== user.id)) ? "Ausweis wurde von einem Administrator storniert." : "Ausweis wurde vom Besitzer storniert.", + benutzer: { + connect: { + id: user.id + } + }, + aufnahme: { + connect: { + id: ausweis.aufnahme_id + } + } + } + }) + }, +}) + +export const GET = defineApiRoute({ + meta: { + description: "Gibt ein spezifisches Gebäude des Benutzers zurück.", + tags: ["Gebäude"], + headers: { + "Authorization": { + description: "Ein gültiger Authentifizierungstoken", + required: true, + allowEmptyValue: false, + examples: { + Bearer: { + value: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } + } + } + } + }, + output: ZodOverlap>(BedarfsausweisWohnenSchema.merge(z.object({ + uid_aufnahme: UUidWithPrefix, + uid_objekt: UUidWithPrefix, + uid_benutzer: UUidWithPrefix.optional() + })).omit({ + id: true, + aufnahme_id: true, + benutzer_id: true + })), + middleware: authorizationMiddleware, + async fetch(input, context, user) { + const { uid } = context.params; + + if (!uid) { + throw new APIError({ + code: "BAD_REQUEST", + message: "Missing uid in request params" + }) + } + + const ausweis = await prisma.bedarfsausweisWohnen.findUnique({ + where: { + uid, + benutzer_id: user.id + }, + include: { + benutzer: { + select: { + uid: true + } + }, + aufnahme: { + select: { + uid: true, + objekt: { + select: { + uid: true + } + } + } + } + } + }); + + if (!ausweis) { + // Falls wir den Ausweis nicht finden können, werfen wir einen Fehler + throw new APIError({ + code: "NOT_FOUND", + message: "Ausweis konnte nicht gefunden werden.", + }); + } + + return { + uid_aufnahme: ausweis.aufnahme.uid, + uid_objekt: ausweis.aufnahme.objekt.uid, + uid_benutzer: ausweis.benutzer?.uid, + ...exclude(ausweis, ["id", "aufnahme_id", "benutzer_id", "aufnahme"]) + } + }, +}); diff --git a/src/pages/api/bedarfsausweis-wohnen/index.ts b/src/pages/api/bedarfsausweis-wohnen/index.ts index e69de29b..8aca276c 100644 --- a/src/pages/api/bedarfsausweis-wohnen/index.ts +++ b/src/pages/api/bedarfsausweis-wohnen/index.ts @@ -0,0 +1,146 @@ +import { UUidWithPrefix } from "#components/Ausweis/types.js"; +import { authorizationHeaders, authorizationMiddleware } from "#lib/middleware/authorization.js"; +import { BedarfsausweisWohnenSchema, prisma } from "@ibcornelsen/database/server"; +import { APIError, defineApiRoute } from "astro-typesafe-api/server"; +import { z } from "zod"; + +export const PUT = defineApiRoute({ + meta: { + contentTypes: ["application/json"], + description: + "Erstellt einen neuen Bedarfsausweis für Wohngebäude nach dem Schema der EnEV von 2016. Als Input wird ein bestehendes Gebäude benötigt. Falls keine UID einer bestehenden Gebäudeaufnahme mitgegeben wird, wird automatisch eine erstellt.", + tags: ["Bedarfsausweis Wohnen"], + }, + input: z.object({ + ausweis: BedarfsausweisWohnenSchema.omit({ + id: true, + benutzer_id: true, + uid: true, + aufnahme_id: true + }), + uid_aufnahme: UUidWithPrefix + }), + output: z.object({ + uid: UUidWithPrefix, + objekt_uid: UUidWithPrefix, + aufnahme_uid: UUidWithPrefix, + }), + headers: authorizationHeaders, + middleware: authorizationMiddleware, + async fetch(input, ctx, user) { + const aufnahme = await prisma.aufnahme.findUnique({ + where: { + uid: input.uid_aufnahme + } + }) + + if (!aufnahme || aufnahme.benutzer_id !== user.id) { + throw new APIError({ + code: "FORBIDDEN", + message: "Aufnahme konnte nicht gefunden werden oder gehört nicht zu diesem Benutzer." + }) + } + + const createdAusweis = await prisma.bedarfsausweisWohnen.create({ + data: { + ...input.ausweis, + benutzer: { + connect: { + id: user.id, + }, + }, + aufnahme: { + connect: { + uid: aufnahme.uid, + }, + }, + }, + select: { + uid: true, + aufnahme: { + select: { + uid: true, + objekt: { + select: { + uid: true, + }, + }, + }, + }, + }, + }); + + return { + uid: createdAusweis.uid, + objekt_uid: createdAusweis.aufnahme.objekt.uid, + aufnahme_uid: createdAusweis.aufnahme.uid, + }; + }, +}); + +export const GET = defineApiRoute({ + meta: { + description: "Gibt ein spezifisches Gebäude des Benutzers zurück.", + tags: ["Gebäude"], + headers: { + Authorization: { + description: "Ein gültiger Authentifizierungstoken", + required: true, + allowEmptyValue: false, + examples: { + Bearer: { + value: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + }, + }, + }, + }, + }, + middleware: authorizationMiddleware, + async fetch(input, context, user) { + const { uid } = context.params; + + const ausweis = await prisma.bedarfsausweisWohnen.findUnique({ + where: { + uid, + }, + include: { + benutzer: true, + aufnahme: { + include: { + objekt: { + include: { + gebaeude_bilder: true, + }, + }, + rechnungen: true, + events: { + include: { + benutzer: { + select: { + uid: true, + }, + }, + }, + orderBy: { + date: "asc", + }, + }, + }, + }, + }, + }); + + if ( + !ausweis || + (ausweis.benutzer_id !== null && ausweis.benutzer_id !== user.id) + ) { + // Falls wir den Ausweis nicht finden können, werfen wir einen Fehler + throw new APIError({ + code: "NOT_FOUND", + message: "Ausweis konnte nicht gefunden werden.", + }); + } + + return ausweis; + }, +}); diff --git a/src/pages/api/objekt/[uid]/index.ts b/src/pages/api/objekt/[uid]/index.ts index 30097039..c05f6e4d 100644 --- a/src/pages/api/objekt/[uid]/index.ts +++ b/src/pages/api/objekt/[uid]/index.ts @@ -59,10 +59,10 @@ export const GET = defineApiRoute({ } } }, - output: ZodOverlap>(ObjektSchema.omit({ + output: ObjektSchema.omit({ benutzer_id: true, id: true - })), + }), middleware: authorizationMiddleware, async fetch(input, ctx, user) { const { uid } = ctx.params; diff --git a/src/pages/kundendaten.astro b/src/pages/kundendaten.astro index f3e77353..220dadae 100644 --- a/src/pages/kundendaten.astro +++ b/src/pages/kundendaten.astro @@ -88,6 +88,6 @@ if (!ausweis || !user) { --- - + diff --git a/src/style/global.css b/src/style/global.css index c101f536..5f5f3a8b 100644 --- a/src/style/global.css +++ b/src/style/global.css @@ -62,13 +62,13 @@ input[type="number"], input[type="password"], input[type="file"], select{ -@apply min-h-[38px] ring-1 ring-black/15 rounded-sm} +@apply p-1 min-h-[38px] ring-1 ring-black/15 rounded-sm} input[type="file"]{@apply pt-[4px]} input[type="checkbox"],input[type="radio"]{@apply inline-block accent-secondary h-[13px]} -input:disabled, input:read-only, select:disabled { +input:disabled, input:read-only { @apply bg-gray-200 border-gray-500/15; }