Verbesserungen

This commit is contained in:
Moritz Utcke
2025-03-09 06:34:01 -03:00
parent c3c7fd313f
commit a1e5945d73
14 changed files with 144 additions and 136 deletions

View File

@@ -19,7 +19,7 @@
export let ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient;
export let aufnahme: AufnahmeKomplettClient;
export let objekt: Objekt;
export let progress: number;
const progress = ausweis.ausgestellt ? 100 : ausweis.bestellt ? 66 : 33;
const ausweisart = getAusweisartFromUUID(ausweis.uid);
@@ -39,6 +39,7 @@
});
if (result === true) {
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
await api["verbrauchsausweis-wohnen"]._uid.DELETE.fetch(undefined, {
params: {
uid: ausweis.uid,
@@ -47,6 +48,16 @@
Authorization: `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`,
},
});
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
await api["verbrauchsausweis-gewerbe"]._uid.DELETE.fetch(undefined, {
params: {
uid: ausweis.uid,
},
headers: {
Authorization: `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`,
},
});
}
ausweis.storniert
ausweis = ausweis;
@@ -138,8 +149,9 @@
</div>
<div class="badge badge-accent font-semibold text-black text-m">{objekt.adresse}</div>
<div class="mb-4 flex flex-row items-center gap-4">
<progress class="progress w-full" value={progress} max="100"
></progress>
<div class="w-full border rounded-lg my-2">
<div class="bg-green-600 h-4 rounded-lg" class:bg-red-600={progress == 33} class:bg-primary={progress == 66} style="width: {progress}%;"></div>
</div>
<!-- TODO: Metrics für den Fortschritt festlegen -->
<span class="text-sm font-semibold text-base-content"
>{progress}%</span
@@ -202,12 +214,19 @@
{/if}
{#if !ausweis.ausgestellt}
{#if !ausweis.ausgestellt && !ausweis.bestellt}
<!-- TODO -->
{#if ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen}
<a
class="button text-sm"
href="/energieausweis-erstellen/verbrauchsausweis-wohngebaeude?uid={ausweis.uid}"
>Bearbeiten</a>
{:else if ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe}
<a
class="button text-sm"
href="/energieausweis-erstellen/verbrauchsausweis-gewerbe?uid={ausweis.uid}"
>Bearbeiten</a>
{/if}
{/if}
<a

View File

@@ -32,10 +32,10 @@
<a href="/dashboard/aufnahme/{aufnahme.uid}" class="rounded-lg p-2 hover:bg-gray-100 transition-all"><OpenInNewWindow size={20}></OpenInNewWindow></a>
</div>
<div class="flex flex-row gap-2">
{#if aufnahme.verbrauchsausweise_wohnen}
{#if aufnahme.verbrauchsausweise_wohnen.length > 0}
<a href="/dashboard/aufnahme/{aufnahme.uid}" class="rounded-lg p-2 hover:bg-gray-100 transition-all"><File size={20}></File></a>
{/if}
{#if aufnahme.verbrauchsausweis_gewerbe}
{#if aufnahme.verbrauchsausweise_gewerbe.length > 0}
<a href="/dashboard/aufnahme/{aufnahme.uid}" class="rounded-lg p-2 hover:bg-gray-100 transition-all"><File size={20}></File></a>
{/if}
</div>

View File

@@ -1,8 +1,11 @@
<script lang="ts">
import UploadImages from "./UploadImages.svelte";
import type { Enums } from "#lib/client/prisma";
import { BedarfsausweisWohnenClient, BildClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "./Ausweis/types.js";
import type { Enums } from "#lib/client/prisma.js";
import { BedarfsausweisWohnenClient, BildClient, ObjektClient, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "./Ausweis/types.js";
import { RotateCounterClockwise, Trash, Upload } from "radix-svelte-icons";
import { api } from "astro-typesafe-api/client";
import Cookies from "js-cookie";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
export let images: BildClient[] = [];
export let max: number = Infinity;
@@ -12,24 +15,16 @@
export let objekt: ObjektClient;
export let kategorie: Enums.BilderKategorie
async function rotateImage(image: UploadedGebaeudeBild): Promise<UploadedGebaeudeBild> {
return new Promise((resolve, reject) => {
let img = new Image();
img.src = image.data ? image.data : `/bilder/${image.uid}.jpg`;
img.onload = () => {
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = img.height;
canvas.height = img.width;
ctx?.translate(img.height / 2, img.width / 2);
ctx?.rotate((-90 * Math.PI) / 180);
ctx?.drawImage(img, -img.width / 2, -img.height / 2);
const clone = Object.assign({}, image)
clone.data = canvas.toDataURL("image/jpeg");
clone.update = true;
resolve(clone)
};
async function deleteImage(image: BildClient) {
await api.bild.DELETE.fetch({
uid: image.uid
}, {
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
images = images.filter((x) => x.uid !== image.uid);
}
let upload: () => void;
@@ -51,13 +46,12 @@
type="button"
class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]"
on:click={() => {
delete images[i];
images = images.filter((x) => x);
deleteImage(images[i])
}}
>
<Trash size={20} color="#fff"></Trash>
</button>
<button
<!-- <button
type="button"
class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]"
on:click={async () => {
@@ -67,7 +61,7 @@
}}
>
<RotateCounterClockwise size={20} color="#fff"></RotateCounterClockwise>
</button>
</button> -->
</div>
</div>
{/if}

View File

@@ -4,7 +4,7 @@ import * as fs from "fs"
import { PDFDocument, rgb, StandardFonts, TextAlignment } from "pdf-lib";
import { xml2pdf } from "./elements/xml2pdf.js";
import moment from "moment";
import { Heizungsstatus } from "#lib/server/prisma.js";
import { Enums, Heizungsstatus } from "#lib/server/prisma.js";
import { endEnergieVerbrauchVerbrauchsausweisGewerbe_2016 } from "#lib/Berechnungen/VerbrauchsausweisGewerbe/VerbrauchsausweisGewerbe_2016.js";
import { fileURLToPath } from "url";
import { copyPage } from "./utils/copyPage.js";
@@ -359,8 +359,12 @@ export async function pdfDatenblattVerbrauchsausweisGewerbe(ausweis: Verbrauchsa
let badge: string[];
let image: string = "";
if (bild.kategorie === Enums.BilderKategorie.Gebaeude) {
continue;
}
if (bild.uid) {
image = `<img src="${fileURLToPath(new URL(`${PERSISTENT_DIR}/images/${bilder[0].uid}.jpg`, import.meta.url))}" width="${(pages[2].getHeight() - 120) / 3.1}" />`
image = `<img src="${fileURLToPath(new URL(`${PERSISTENT_DIR}/images/${bild.uid}.jpg`, import.meta.url))}" width="${(pages[2].getWidth() - 120) / 3.1}" height="${(pages[2].getHeight() - marginY * 2) / 4}" />`
} else if (bild.data) {
image = `<img data="${bild.data}" width="${(pages[2].getWidth() - 120) / 3.1}" height="180" />`
}
@@ -380,7 +384,7 @@ export async function pdfDatenblattVerbrauchsausweisGewerbe(ausweis: Verbrauchsa
}
const layoutPage3 = xml2pdf(`<layout height="${pages[2].getHeight()}" width="${pages[2].getWidth()}" marginTop="150" marginLeft="60" marginRight="60">
${images.map(badge => `<flex direction="row" justify="space-between" width="${pages[2].getWidth() - 120}" height="180" marginTop="15">${badge.join("")}</flex>`).join("")}
${images.map(badge => `<flex direction="row" justify="space-between" width="${pages[2].getWidth() - 120}" height="${(pages[2].getHeight() - marginY * 2) / 4}" marginTop="15">${badge.join("")}</flex>`).join("")}
</layout>`, { "default": font })
layout.draw(pages[0], 0, pages[0].getHeight())

View File

@@ -5,7 +5,7 @@ import { PDFDocument, rgb, StandardFonts, TextAlignment } from "pdf-lib";
import { checkbox, flex, text } from "./elements/index.js";
import { xml2pdf } from "./elements/xml2pdf.js";
import moment from "moment";
import { BilderKategorie, Heizungsstatus } from "#lib/server/prisma.js";
import { BilderKategorie, Enums, Heizungsstatus } from "#lib/server/prisma.js";
import { fileURLToPath } from "url";
import { copyPage } from "./utils/copyPage.js";
import { PERSISTENT_DIR } from "#lib/server/constants.js";
@@ -298,40 +298,37 @@ export async function pdfDatenblattVerbrauchsausweisWohnen(ausweis: Verbrauchsau
const images: string[][] = []
let individualHeight = (pages[2].getHeight() - 370) / 4;
for (const bild of bilder) {
let badge: string[];
let image: string = "";
const sortedImages = bilder.reduce((acc, c) => {
let img: string;
if (c.uid) {
img = `<img src="${fileURLToPath(new URL(`${PERSISTENT_DIR}/images/${bilder[0].uid}.jpg`, import.meta.url))}" width="${(pages[2].getHeight() - 120) / 4.1}" height="${individualHeight}" />`
} else {
img = `<img data="${c.data}" width="${(pages[2].getWidth() - 120) / 4.1}" height="${individualHeight}" />`
if (bild.kategorie === Enums.BilderKategorie.Gebaeude) {
continue;
}
if (c.kategorie in acc) {
acc[c.kategorie].push(img)
} else {
acc[c.kategorie] = [img];
if (bild.uid) {
image = `<img src="${fileURLToPath(new URL(`${PERSISTENT_DIR}/images/${bild.uid}.jpg`, import.meta.url))}" width="${(pages[2].getWidth() - 120) / 3.1}" height="${(pages[2].getHeight() - marginY * 2) / 4}" />`
} else if (bild.data) {
image = `<img data="${bild.data}" width="${(pages[2].getWidth() - 120) / 3.1}" height="180" />`
}
return acc;
}, {} as Record<BilderKategorie, string[]>)
let text = ""
for (const [kategorie, images] of Object.entries(sortedImages)) {
text += `<flex direction="column" gap="4" width="${pages[2].getWidth() - 120}" height="${individualHeight}" marginTop="25">
<text font="bold" size="14">${kategorie}</text>
<flex direction="row" justify="space-between" width="${pages[2].getWidth() - 120}" height="${individualHeight}">
${images.join("")}
</flex>
</flex>`
if (images.length > 0) {
let badge = images[images.length - 1]
if (badge.length == 3) {
badge = [image]
images.push(badge)
} else {
badge.push(image)
}
} else {
badge = [image]
images.push(badge)
}
}
const layoutPage3 = xml2pdf(`<layout height="${pages[2].getHeight()}" width="${pages[2].getWidth()}" marginTop="150" marginLeft="60" marginRight="60">
${text}
</layout>`, { "default": font, bold })
${images.map(badge => `<flex direction="row" justify="space-between" width="${pages[2].getWidth() - 120}" height="${(pages[2].getHeight() - marginY * 2) / 4}" marginTop="15">${badge.join("")}</flex>`).join("")}
</layout>`, { "default": font })
layout.draw(pages[0], 0, pages[0].getHeight())
layoutPage2.draw(pages[1], 0, pages[1].getHeight())

View File

@@ -140,12 +140,17 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
const gebaeudeBild = bilder.find(image => image.kategorie === Enums.BilderKategorie.Gebaeude);
if (gebaeudeBild) {
let image: PDFImage;
let image: PDFImage | null;
try {
image = await pdf.embedJpg(gebaeudeBild?.data)
image = await pdf.embedJpg(gebaeudeBild.data)
} catch(e) {
image = await pdf.embedPng(gebaeudeBild?.data)
try {
image = await pdf.embedPng(gebaeudeBild.data)
} catch(e) {
image = null;
}
}
if (image) {
pages[0].drawImage(image, {
x: 460.5,
y: height - 289,
@@ -153,6 +158,7 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
height: 138
})
}
}
// Checkmark Angabe energetische Qualität des Gebäudes.
addCheckMark(pages[0], 40, height - 550)

View File

@@ -3,7 +3,7 @@ import { pdfDatenblattVerbrauchsausweisGewerbe } from "#lib/pdf/pdfDatenblattVer
import { pdfDatenblattVerbrauchsausweisWohnen } from "#lib/pdf/pdfDatenblattVerbrauchsausweisWohnen.js";
import { pdfVerbrauchsausweisGewerbe } from "#lib/pdf/pdfVerbrauchsausweisGewerbe.js";
import { pdfVerbrauchsausweisWohnen } from "#lib/pdf/pdfVerbrauchsausweisWohnen.js";
import { Enums, prisma } from "#lib/server/prisma";
import { Enums, prisma } from "#lib/server/prisma.js";
/**
* Gibt den richtigen Prisma Adapter für die Ausweisart basierend auf der UID zurück, oder null bei einer falschen UID.

View File

@@ -17,8 +17,6 @@ export async function sendInvoiceMail(
rechnung: Rechnung,
user: Benutzer
) {
console.log(ausweis);
const aufnahme = await prisma.aufnahme.findUnique({
where: {
id: ausweis.aufnahme_id,

View File

@@ -63,12 +63,12 @@
<div class="my-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
{#each aufnahme.verbrauchsausweise_wohnen as ausweis}
<DashboardAusweis {ausweis} {aufnahme} {objekt} progress={0}></DashboardAusweis>
<DashboardAusweis {ausweis} {aufnahme} {objekt}></DashboardAusweis>
{/each}
{#each aufnahme.bedarfsausweise_wohnen as ausweis}
<DashboardAusweis {ausweis} {aufnahme} {objekt} progress={0}></DashboardAusweis>
<DashboardAusweis {ausweis} {aufnahme} {objekt}></DashboardAusweis>
{/each}
{#each aufnahme.verbrauchsausweise_gewerbe as ausweis}
<DashboardAusweis {ausweis} {aufnahme} {objekt} progress={0}></DashboardAusweis>
<DashboardAusweis {ausweis} {aufnahme} {objekt}></DashboardAusweis>
{/each}
</div>

View File

@@ -2,7 +2,7 @@ import { UUidWithPrefix } from "#components/Ausweis/types.js";
import { adminMiddleware } from "#lib/middleware/authorization.js";
import { mollieClient } from "#lib/mollie.js";
import { getPrismaAusweisAdapter } from "#lib/server/ausweis.js";
import { Prisma, prisma } from "#lib/server/prisma";
import { Prisma, prisma } from "#lib/server/prisma.js";
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { z } from "zod";

View File

@@ -1,8 +1,9 @@
import { OptionalNullable, UUidWithPrefix, VerbrauchsausweisGewerbeClient, ZodOverlap } from "#components/Ausweis/types.js";
import { exclude } from "#lib/exclude.js";
import { authorizationHeaders, authorizationMiddleware } from "#lib/middleware/authorization.js";
import { prisma, VerbrauchsausweisGewerbeSchema } from "#lib/server/prisma";
import { prisma } from "#lib/server/prisma.js";
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { VerbrauchsausweisGewerbeSchema } from "src/generated/zod/verbrauchsausweisgewerbe.js";
import { z } from "zod";
export const PATCH = defineApiRoute({
@@ -64,13 +65,6 @@ export const DELETE = defineApiRoute({
const ausweis = await prisma.verbrauchsausweisGewerbe.findUnique({
where: {
uid,
},
include: {
aufnahme: {
select: {
storniert: true
}
}
}
});
@@ -100,7 +94,7 @@ export const DELETE = defineApiRoute({
// });
// }
if (ausweis.aufnahme.storniert) {
if (ausweis.storniert) {
// Falls der Ausweis bereits storniert ist, werfen wir einen Fehler
throw new APIError({
code: "BAD_REQUEST",
@@ -108,9 +102,9 @@ export const DELETE = defineApiRoute({
});
}
await prisma.aufnahme.update({
await prisma.verbrauchsausweisGewerbe.update({
where: {
id: ausweis.aufnahme_id
uid
},
data: {
storniert: true

View File

@@ -1,8 +1,9 @@
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.js";
import { prisma } from "#lib/server/prisma.js";
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { VerbrauchsausweisWohnenSchema } from "src/generated/zod/verbrauchsausweiswohnen.js";
import { z } from "zod";
export const PATCH = defineApiRoute({
@@ -66,13 +67,6 @@ export const DELETE = defineApiRoute({
const ausweis = await prisma.verbrauchsausweisWohnen.findUnique({
where: {
uid,
},
include: {
aufnahme: {
select: {
storniert: true
}
}
}
});
@@ -102,7 +96,7 @@ export const DELETE = defineApiRoute({
// });
// }
if (ausweis.aufnahme.storniert) {
if (ausweis.storniert) {
// Falls der Ausweis bereits storniert ist, werfen wir einen Fehler
throw new APIError({
code: "BAD_REQUEST",
@@ -110,9 +104,9 @@ export const DELETE = defineApiRoute({
});
}
await prisma.aufnahme.update({
await prisma.verbrauchsausweisWohnen.update({
where: {
id: ausweis.aufnahme_id
uid
},
data: {
storniert: true

View File

@@ -31,6 +31,9 @@ const objekte = await prisma.objekt.findMany({
}
},
take: 10,
orderBy: {
erstellungsdatum: "desc"
},
include: {
aufnahmen: {
include: {

View File

@@ -2,7 +2,6 @@
import AusweisLayout from "#layouts/AusweisLayoutDaten.astro";
import { AufnahmeClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisGewerbeClient } from "#components/Ausweis/types";
import { createCaller } from "src/astro-typesafe-api-caller";
import { inferOutput } from "astro-typesafe-api/client";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants";
import { validateAccessTokenServer } from "#server/lib/validateAccessToken";
import VerbrauchsausweisGewerbeModule from "#modules/VerbrauchsausweisGewerbe/VerbrauchsausweisGewerbeModule.svelte";