Build getestet + MDX Erstmal entfernt

This commit is contained in:
Moritz Utcke
2024-03-04 14:11:47 +07:00
parent 26058e3205
commit 4f479b9c6c
22 changed files with 158 additions and 110 deletions

View File

@@ -12,7 +12,7 @@ import mdx from "@astrojs/mdx";
// https://astro.build/config
export default defineConfig({
integrations: [/*astroI18next(),*/svelte(), tailwind(), mdx()],
integrations: [svelte(), tailwind(), mdx()],
outDir: "./dist",
output: "server",
vite: {

View File

@@ -25,15 +25,6 @@ docker stop $APP_NAME
docker rm $APP_NAME
docker build -t $APP_NAME .
# SECTION: Startup jobs zu crontab hinzufügen.
# Erstmal den cronfile leeren.
crontab -r;
# Alle builds schlagen fehl wenn die Datenbank nicht da ist, also muss der Container zuerst gebaut werden.
(crontab -l ; echo "@reboot sudo ~/database/build.sh &") | crontab -;
(crontab -l ; echo "@reboot sudo ~/apps/online-energieausweis/build.sh &") | crontab -;
(crontab -l ; echo "@reboot sudo ~/apps/layout-tool/build.sh &") | crontab -;
# Wir legen ein persistent directory an
PERSISTENT_DIR="${HOME}/persistent/online-energieausweis";
mkdir -p $PERSISTENT_DIR;
@@ -48,9 +39,6 @@ echo "CERTIFICATE=$(cat /etc/letsencrypt/live/ibcornelsen.de/fullchain.pem | bas
docker run -d --name $APP_NAME --link $DB_CONTAINER_NAME \
-v "${PERSISTENT_DIR}:/persistent" \
-p "${APP_PORT}:80" \
-e DB_CONNECTION=postgresql://${DB_USER}:${DB_PASSWORD}@${DB_CONTAINER_NAME}:${DB_PORT}/${DB_NAME} \
-e DB_PORT=${DB_PORT} \
--env-file ~/apps/${APP_NAME}/.env \
$APP_NAME;
# Crontab Updaten

15
install.sh Normal file
View File

@@ -0,0 +1,15 @@
git clone https://github.com/IBCornelsen/online-energieausweis
git clone https://github.com/IBCornelsen/database
git clone https://github.com/IBCornelsen/api
cd ./database
bun link
bun install
cd ../api
bun link
bun install
cd ../online-energieausweis
bun link
bun install

View File

@@ -1,5 +1,5 @@
---
layout: ../layouts/Layout.astro
layout; ../layouts/Layout.astro
title: AGB - online-energieausweis.org
---

View File

@@ -3,6 +3,7 @@ layout: ../layouts/Layout.astro
title: Energieausweis EnEV/GEG
---
# EnEV Zusammenfassung (Archiv - Seit 1. Mai 2021 abgelöst durch GEG)

View File

@@ -1,6 +1,6 @@
---
layout: ../layouts/Layout.astro
title: Welcher Energieausweis?
title: "Welcher Energieausweis?"
---
import { BoxWithHeading } from "@ibcornelsen/ui";

View File

@@ -17,7 +17,7 @@
},
"private": true,
"dependencies": {
"@astrojs/mdx": "^0.18.4",
"@astrojs/mdx": "^2.1.1",
"@astrojs/node": "^5.1.4",
"@astrojs/svelte": "^2.2.0",
"@astrojs/tailwind": "^3.1.3",
@@ -54,6 +54,7 @@
"moment-timezone": "^0.5.45",
"pg": "^8.11.0",
"radix-svelte-icons": "^1.0.0",
"remark-frontmatter": "^5.0.0",
"sass": "^1.62.1",
"svelte": "^3.59.1",
"svelte-dialogs": "^1.2.2",

View File

@@ -76,7 +76,7 @@
<DotsVertical size={15} />
</button>
<ul
tabindex="0"
tabindex="-1"
class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-64 gap-2"
>
<li>
@@ -95,15 +95,15 @@
</ul>
</div>
<div class="flex flex-row flex-wrap gap-2">
{#if ausweis.ausweisart == "VerbrauchsausweisWohnen"}
{#if ausweis.gebaeude_aufnahme_allgemein.ausweisart == "VerbrauchsausweisWohnen"}
<div class="badge badge-accent font-semibold">
Verbrauchsausweis Wohnen
</div>
{:else if ausweis.ausweisart == "BedarfsausweisWohnen"}
{:else if ausweis.gebaeude_aufnahme_allgemein.ausweisart == "BedarfsausweisWohnen"}
<div class="badge badge-accent font-semibold">
Bedarfsausweis Wohnen
</div>
{:else if ausweis.ausweisart == "VerbrauchsausweisGewerbe"}
{:else if ausweis.gebaeude_aufnahme_allgemein.ausweisart == "VerbrauchsausweisGewerbe"}
<div class="badge badge-accent font-semibold">
Verbrauchsausweis Gewerbe
</div>

View File

@@ -8,7 +8,6 @@
import ThemeController from "#components/ThemeController.svelte";
import { BenutzerClient } from "#components/Ausweis/types";
import Cross1 from "radix-svelte-icons/src/lib/icons/Cross1.svelte";
import { slide } from "svelte/transition";
export let lightTheme: boolean;
export let benutzer: BenutzerClient;

View File

@@ -1,20 +1,42 @@
import { VerbrauchsausweisWohnenClient } from "#components/Ausweis/types";
import { z } from "zod";
import { verbrauchsausweisWohnenPDFValidator } from "./validators/verbrauchsausweis-wohnen-pdf-validator";
export function convertAusweisData(
inputs: Partial<VerbrauchsausweisWohnenClient>
): Record<string, string> {
let gebaeude_aufnahme_allgemein = inputs.gebaeude_aufnahme_allgemein || {};
let gebaeude_stammdaten = inputs.gebaeude_aufnahme_allgemein?.gebaeude_stammdaten || {};
// Wir wollen alle Werte zu einem Flachen Objekt umwandeln, sodass wir dass später benutzen können.
// Dazu kommen noch einige wichtige Eigenschaften die man im PDF brauchen könnte.
let pdfInputs: z.infer<typeof verbrauchsausweisWohnenPDFValidator> = {
...inputs,
pdf: {
"brennstoff": [inputs.gebaeude_aufnahme_allgemein?.brennstoff_1, inputs.gebaeude_aufnahme_allgemein?.brennstoff_2].filter(x => x).join(", ")
}
}
let result = recursiveFlatten(inputs, "");
// Außerdem müssen wir alle Werte zu strings umwandeln.
for (const key in result) {
result[key] = String(result[key]);
}
return {
"gebaeude_stammdaten.adresse": gebaeude_stammdaten.adresse || "",
"gebaeude_stammdaten.gebaeudetyp":
gebaeude_stammdaten.gebaeudetyp || "",
"gebaeude_stammdaten.baujahr_gebaeude":
gebaeude_stammdaten.gebaeude_aufnahme_allgemein?.join(", ") || "",
"gebaeude_stammdaten.baujahr_heizung":
gebaeude_stammdaten.gebaeude_aufnahme_allgemein?.join(", ") || "",
"gebaeude_stammdaten.plz": gebaeude_stammdaten.plz || "",
"gebaeude_stammdaten.ort": gebaeude_stammdaten.ort || "",
};
return result;
}
function recursiveFlatten(obj: any, parentKey = ""): Record<string, string> {
const result: Record<string, string> = {};
for (const key in obj) {
const value = obj[key];
const newKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof value === "object") {
Object.assign(result, recursiveFlatten(value, newKey));
} else {
result[newKey] = value;
}
}
return result;
}

25
src/lib/helpers/zod.ts Normal file
View File

@@ -0,0 +1,25 @@
import { z } from "zod";
// get zod object keys recursively
export const zodGetKeys = <T extends z.ZodTypeAny>(schema: T): string[] => {
// make sure schema is not null or undefined
if (schema === null || schema === undefined) return [];
// check if schema is nullable or optional
if (schema instanceof z.ZodNullable || schema instanceof z.ZodOptional) return zodGetKeys(schema.unwrap());
// check if schema is an array
if (schema instanceof z.ZodArray) return zodGetKeys(schema.element);
// check if schema is an object
if (schema instanceof z.ZodObject) {
// get key/value pairs from schema
const entries = Object.entries(schema.shape);
// loop through key/value pairs
return entries.flatMap(([key, value]) => {
// get nested keys
const nested = value instanceof z.ZodType ? zodGetKeys(value).map(subKey => `${key}.${subKey}`) : [];
// return nested keys
return nested.length ? nested : key;
});
}
// return empty array
return [];
};

View File

@@ -1,14 +1,12 @@
import {
ZOOM,
Plugin,
Schema,
PropPanel,
DEFAULT_FONT_NAME,
getFallbackFontName,
PropPanelSchema,
PropPanelWidgetProps,
} from "@pdfme/common";
import { image, text } from "@pdfme/schemas";
import { text } from "@pdfme/schemas";
import type { TextSchema } from "@pdfme/schemas/dist/types/src/text/types";
import {
@@ -34,8 +32,10 @@ import {
import {
GebaeudeStammdaten,
Rechnungen,
VerbrauchsausweisWohnen,
} from "@ibcornelsen/database/client";
import { VerbrauchsausweisWohnenClient } from "#components/Ausweis/types";
import { zodGetKeys } from "#lib/helpers/zod";
import { verbrauchsausweisWohnenPDFValidator } from "#lib/validators/verbrauchsausweis-wohnen-pdf-validator";
const UseDynamicFontSize = (props: PropPanelWidgetProps) => {
const { rootElement, changeSchemas, activeSchema, i18n } = props;
@@ -62,51 +62,17 @@ const UseDynamicFontSize = (props: PropPanelWidgetProps) => {
rootElement.appendChild(label);
};
type AusweisIndex = keyof VerbrauchsausweisWohnen
| `gebaeude_stammdaten.${keyof GebaeudeStammdaten}`
| `rechnung.${keyof Rechnungen}`;
type AusweisIndex = keyof Omit<Omit<VerbrauchsausweisWohnenClient, "gebaeude_aufnahme_allgemein">, "rechnungen">
| `gebaeude_aufnahme_allgemein.${keyof VerbrauchsausweisWohnenClient["gebaeude_aufnahme_allgemein"]}`
| `gebaeude_aufnahme_allgemein.gebaeude_stammdaten.${keyof VerbrauchsausweisWohnenClient["gebaeude_aufnahme_allgemein"]["gebaeude_stammdaten"]}`
| `rechnungen.${keyof Rechnungen}`;
const sampleData: Partial<Record<AusweisIndex , string>> = {
"gebaeude_stammdaten.adresse": "Musterstraße 123",
"gebaeude_stammdaten.plz": "12345",
"gebaeude_stammdaten.ort": "Musterstadt",
"gebaeude_stammdaten.baujahr_gebaeude": "1990",
"gebaeude_stammdaten.baujahr_heizung": "2000",
}
const ausweisKeys = zodGetKeys(verbrauchsausweisWohnenPDFValidator);
const variableOptions: {
label: string;
value: AusweisIndex;
}[] = [
{
label: "Gebäude -> Adresse",
value: "gebaeude_stammdaten.adresse",
},
{
label: "Gebäude -> PLZ",
value: "gebaeude_stammdaten.plz",
},
{
label: "Gebaeude -> Ort",
value: "gebaeude_stammdaten.ort",
},
{
label: "Gebäude -> Baujahr Gebäude",
value: "gebaeude_stammdaten.baujahr_gebaeude",
},
{
label: "Gebäude -> Baujahr Heizung",
value: "gebaeude_stammdaten.baujahr_heizung",
},
{
label: "Gebäude -> Fläche",
value: "gebaeude_stammdaten.flaeche"
},
{
label: "Gebäude -> Einheiten",
value: "gebaeude_stammdaten.einheiten"
}
];
}[] = ausweisKeys.map((key: AusweisIndex) => ({ label: key as string, value: key }));
interface VariableSchema extends TextSchema {
variable: AusweisIndex | undefined
@@ -297,14 +263,17 @@ const propPanel: PropPanel<VariableSchema> = {
};
export const variable: Plugin<VariableSchema> = {
ui: (props) => {
if (props.schema.variable) {
props.value = sampleData[props.schema.variable] as string;
ui: function(props) {
// Wir binden die inputs auf dieses Element, damit wir die Werte später auslesen können.
if (props.schema.variable && this) {
props.value = (this as unknown as Record<string, string>)[props.schema.variable] as string;
}
return text.ui(props);
},
pdf: (props) => {
props.value = props.schema.variable as string;
if (props.schema.variable && this) {
props.value = (this as unknown as Record<string, string>)[props.schema.variable] as string;
}
text.pdf(props);
},
propPanel,

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -0,0 +1,8 @@
import { VerbrauchsausweisWohnenValidator } from "@ibcornelsen/api/src/validators";
import { z } from "zod";
export const verbrauchsausweisWohnenPDFValidator = VerbrauchsausweisWohnenValidator.merge(z.object({
pdf: z.object({
brennstoff: z.string()
})
}))

View File

@@ -42,7 +42,9 @@
return;
}
const imagesToUpload = images.filter(image => !image.uid);
// Wenn Bilder hochgeladen werden konvertieren wir sie zu base64, das heißt, dass die base64 Eigenschaft bei diesen Bildern
// existiert. Das müssen wir TypeScript nur wissen lassen, damit es uns in Ruhe lässt.
const imagesToUpload = images.filter(image => !image.uid) as unknown as { base64: string, kategorie: string, uid?: string }[];
if (imagesToUpload.length == 0) {
return;
@@ -104,7 +106,7 @@
try {
const gebaeudeBilderEntfernt = exclude(gebaeude, ["gebaeude_bilder"])
const gebaeudeAufnahmeGeneratedFieldsEntfernt = exclude(gebaeude_aufnahme_allgemein, ["erstellungsdatum", "events"])
const ausweisGeneratedFieldsEntfernt = exclude(ausweis, ["ausweisart", "rechnungen", "erstellungsdatum"])
const ausweisGeneratedFieldsEntfernt = exclude(ausweis, ["rechnungen", "erstellungsdatum"])
await client.v1.verbrauchsausweisWohnen[2016].speichern.mutate({
...ausweisGeneratedFieldsEntfernt,
@@ -192,9 +194,9 @@
ausweis.einheit_1 = "kWh";
ausweis.anteil_warmwasser_1 = 18;
ausweis.startdatum = moment("01.01.2019").toDate();
gebaeude.plz = "21039";
gebaeude.ort = "Hamburg";
gebaeude.adresse = "Curslacker Deich 170";
gebaeude_aufnahme_allgemein.plz = "21039";
gebaeude_aufnahme_allgemein.ort = "Hamburg";
gebaeude_aufnahme_allgemein.adresse = "Curslacker Deich 170";
gebaeude_aufnahme_allgemein.gebaeudeteil = "Gesamtgebäude";
gebaeude = gebaeude;
@@ -281,7 +283,7 @@
required
data-msg-minlength="min. 5 Zeichen"
data-msg-maxlength="max. 40 Zeichen"
bind:value={gebaeude.adresse}
bind:value={gebaeude_aufnahme_allgemein.adresse}
/>
</div>
</div>
@@ -289,8 +291,8 @@
<!-- PLZ -->
<div class="form-group col-md-4 PLZ">
<ZipSearch
bind:zip={gebaeude.plz}
bind:city={gebaeude.ort}
bind:zip={gebaeude_aufnahme_allgemein.plz}
bind:city={gebaeude_aufnahme_allgemein.ort}
name="plz" data-test="plz"
/>
</div>
@@ -303,7 +305,7 @@
<input
name="ort" data-test="ort"
readonly={true}
bind:value={gebaeude.ort}
bind:value={gebaeude_aufnahme_allgemein.ort}
type="text"
/>
</div>

View File

@@ -87,9 +87,18 @@
input.click()
}
function onKeydown(e: KeyboardEvent) {
if (e.ctrlKey && e.key === "s") {
e.preventDefault();
exportTemplate();
}
}
let loadTemplateInput: HTMLInputElement;
</script>
<svelte:window on:keydown={onKeydown}></svelte:window>
<header class="mb-4">
<button class="btn btn-secondary" on:click={() => loadTemplateInput.click()}>Change base PDF</button>
<button class="btn btn-secondary" on:click={addNewField}>Add new Field</button>
@@ -99,4 +108,4 @@
<input type="file" hidden bind:this={loadTemplateInput} on:change={loadBasePDF}>
</header>
<div bind:this={container}></div>
<div bind:this={container} class="h-[80vh]"></div>

View File

@@ -5,13 +5,14 @@
import { Viewer } from "@pdfme/ui";
import { Check } from "radix-svelte-icons";
import { image, text } from "@pdfme/schemas";
import { AusweisData, convertAusweisData } from "#lib/AusweisData";
import { VerbrauchsausweisWohnenClient } from "#components/Ausweis/types";
import { convertAusweisData } from "#lib/AusweisData";
export let ausweise: AusweisData[];
export let ausweise: VerbrauchsausweisWohnenClient[];
let pdfInputs: AusweisData;
let pdfInputs: VerbrauchsausweisWohnenClient;
let template: Template;
let viewer: Viewer
@@ -37,11 +38,13 @@
return
};
const convertedInputs = convertAusweisData(pdfInputs);
viewer = new Viewer({
domContainer: pdfViewerContainer,
template,
inputs: [convertAusweisData(pdfInputs)],
plugins: { text, image, variable}
inputs: [convertedInputs],
plugins: { text, image, variable: { ...variable, pdf: variable.pdf.bind(convertedInputs), ui: variable.ui.bind(convertedInputs) } }
})
};
@@ -51,8 +54,9 @@
input.click();
}
function changeInputs(inputs: AusweisData) {
function changeInputs(inputs: VerbrauchsausweisWohnenClient) {
pdfInputs = inputs;
if (!template) {
alert("Bitte laden Sie zuerst ein Template.")
return
@@ -60,11 +64,13 @@
if (viewer) viewer.destroy();
const convertedInputs = convertAusweisData(pdfInputs);
viewer = new Viewer({
domContainer: pdfViewerContainer,
template,
inputs: [convertAusweisData(pdfInputs)],
plugins: { text, image, variable}
inputs: [convertedInputs],
plugins: { text, image, variable: { ...variable, pdf: variable.pdf.bind(convertedInputs), ui: variable.ui.bind(convertedInputs) } }
})
}
@@ -81,7 +87,7 @@
<div class="flex flex-col gap-4">
{#each ausweise as ausweis}
<div class="rounded-lg border p-2 flex flex-row items-center justify-between">
<h2 class="text-black">{ausweis.gebaeude_stammdaten.adresse}</h2>
<h2 class="text-black">{ausweis.gebaeude_aufnahme_allgemein.gebaeude_stammdaten.adresse}</h2>
<button class="btn btn-square btn-ghost p-1.5" on:click={() => {
changeInputs(ausweis)
}}><Check size={20}/></button>

View File

@@ -41,7 +41,11 @@
import { PRICES } from "#lib/constants";
import { BenutzerClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types";
const prices = PRICES[ausweis.ausweisart];
let prices: number[] = []
if (ausweis.gebaeude_aufnahme_allgemein.ausweisart) {
prices = PRICES[ausweis.gebaeude_aufnahme_allgemein.ausweisart]
}
let basePrice: number = prices[0];

View File

@@ -1,16 +1,14 @@
---
import { createCaller } from "#lib/caller";
import UserLayout from "../../../layouts/UserLayout.astro";
import DashboardPDFViewerModule from "../../../modules/Dashboard/DashboardPDFViewerModule.svelte";
import { prisma } from "@ibcornelsen/database/server";
const ausweise = await prisma.verbrauchsausweisWohnen.findMany({
take: 10,
include: {
benutzer: true,
gebaeude_stammdaten: true,
rechnungen: true
}
})
const caller = createCaller(Astro);
const ausweise = await caller.v1.verbrauchsausweisWohnen.getMany({
limit: 10
});
---
<UserLayout title="PDF Viewer">