PDF Designer
This commit is contained in:
@@ -244,12 +244,15 @@ describe("Verbrauchsausweis erstellen Schritt 1", () => {
|
|||||||
// Der Ausweis sollte jetzt schon erstellt worden sein und wir sollten auf die kundendaten seite weitergeleitet 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");
|
cy.url().should("contain", "/kundendaten");
|
||||||
|
|
||||||
|
cy.wait(1000)
|
||||||
|
|
||||||
// Wir füllen jetzt die Kundendaten aus.
|
// Wir füllen jetzt die Kundendaten aus.
|
||||||
|
cy.get("select[name='anrede']").select(Math.random() > 0.5 ? "Herr" : "Frau");
|
||||||
cy.get("input[name='vorname']").should("contain.value", vorname);
|
cy.get("input[name='vorname']").should("contain.value", vorname);
|
||||||
cy.get("input[name='name']").should("contain.value", nachname);
|
cy.get("input[name='name']").should("contain.value", nachname);
|
||||||
cy.get("input[name='email']").should("contain.value", email);
|
cy.get("input[name='email']").should("contain.value", email);
|
||||||
cy.get("input[name='telefon']").type(faker.phone.number());
|
cy.get("input[name='telefon']").type(faker.phone.number());
|
||||||
|
|
||||||
cy.get("input[name='rechnung_empfaenger']").type(`${vorname} ${nachname}`);
|
cy.get("input[name='rechnung_empfaenger']").type(`${vorname} ${nachname}`);
|
||||||
cy.get("input[name='rechnung_strasse']").type(faker.location.streetAddress());
|
cy.get("input[name='rechnung_strasse']").type(faker.location.streetAddress());
|
||||||
// TODO: Random Plz generieren, allerdings muss die auch in der Datenbank vorhanden sein...
|
// TODO: Random Plz generieren, allerdings muss die auch in der Datenbank vorhanden sein...
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
"@ibcornelsen/database": "link:@ibcornelsen/database",
|
"@ibcornelsen/database": "link:@ibcornelsen/database",
|
||||||
"@ibcornelsen/ui": "^0.0.2",
|
"@ibcornelsen/ui": "^0.0.2",
|
||||||
"@mollie/api-client": "^3.7.0",
|
"@mollie/api-client": "^3.7.0",
|
||||||
|
"@pdfme/common": "^3.2.3",
|
||||||
|
"@pdfme/generator": "^3.2.3",
|
||||||
|
"@pdfme/ui": "^3.2.3",
|
||||||
"@trpc/client": "^10.45.0",
|
"@trpc/client": "^10.45.0",
|
||||||
"@trpc/server": "^10.45.0",
|
"@trpc/server": "^10.45.0",
|
||||||
"astro": "^2.5.1",
|
"astro": "^2.5.1",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
import { API_ACCESS_TOKEN_COOKIE_NAME, API_REFRESH_TOKEN_COOKIE_NAME } from "../../lib/constants";
|
import { API_ACCESS_TOKEN_COOKIE_NAME, API_REFRESH_TOKEN_COOKIE_NAME, API_UID_COOKIE_NAME } from "../../lib/constants";
|
||||||
import { client } from "src/trpc";
|
import { client } from "src/trpc";
|
||||||
|
import moment from "moment";
|
||||||
|
|
||||||
|
|
||||||
export async function validateAccessTokenClient() {
|
export async function validateAccessTokenClient() {
|
||||||
@@ -8,7 +9,11 @@ export async function validateAccessTokenClient() {
|
|||||||
const refreshToken = Cookies.get(API_REFRESH_TOKEN_COOKIE_NAME);
|
const refreshToken = Cookies.get(API_REFRESH_TOKEN_COOKIE_NAME);
|
||||||
|
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
return true;
|
const { valid } = await client.v1.benutzer.validateAccessToken.query({accessToken})
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
Cookies.remove(API_ACCESS_TOKEN_COOKIE_NAME);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wir haben keinen Access Token mehr, vielleicht ist dieser ausgelaufen.
|
// Wir haben keinen Access Token mehr, vielleicht ist dieser ausgelaufen.
|
||||||
@@ -16,6 +21,9 @@ export async function validateAccessTokenClient() {
|
|||||||
|
|
||||||
if (!refreshToken) {
|
if (!refreshToken) {
|
||||||
// Wir haben keinen Refresh Token, also müssen wir uns neu anmelden.
|
// Wir haben keinen Refresh Token, also müssen wir uns neu anmelden.
|
||||||
|
Cookies.remove(API_ACCESS_TOKEN_COOKIE_NAME);
|
||||||
|
Cookies.remove(API_REFRESH_TOKEN_COOKIE_NAME);
|
||||||
|
Cookies.remove(API_UID_COOKIE_NAME)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,21 +31,25 @@ export async function validateAccessTokenClient() {
|
|||||||
// Wenn das klappt, dann haben wir auch einen neuen Access Token.
|
// Wenn das klappt, dann haben wir auch einen neuen Access Token.
|
||||||
// Wenn das nicht klappt, dann müssen wir uns neu anmelden.
|
// Wenn das nicht klappt, dann müssen wir uns neu anmelden.
|
||||||
try {
|
try {
|
||||||
const { accessToken: newAccessToken, exp } = await client.v1.benutzer.getAccessToken.query({
|
const { accessToken: newAccessToken, accessTokenExpiry, refreshToken: newRefreshToken, refreshTokenExpiry } = await client.v1.benutzer.getAccessToken.query({
|
||||||
refreshToken
|
refreshToken
|
||||||
})
|
})
|
||||||
|
|
||||||
const options = {
|
Cookies.set(API_ACCESS_TOKEN_COOKIE_NAME, newAccessToken, {
|
||||||
domain: `.${window.location.hostname}`,
|
domain: `.${window.location.hostname}`,
|
||||||
path: "/",
|
path: "/",
|
||||||
expires: exp
|
expires: moment.unix(accessTokenExpiry).toDate()
|
||||||
}
|
});
|
||||||
|
Cookies.set(API_REFRESH_TOKEN_COOKIE_NAME, newRefreshToken, {
|
||||||
Cookies.set(API_ACCESS_TOKEN_COOKIE_NAME, newAccessToken, options);
|
domain: `.${window.location.hostname}`,
|
||||||
|
path: "/",
|
||||||
|
expires: moment.unix(refreshTokenExpiry).toDate()
|
||||||
|
})
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Cookies.remove(API_ACCESS_TOKEN_COOKIE_NAME);
|
Cookies.remove(API_ACCESS_TOKEN_COOKIE_NAME);
|
||||||
Cookies.remove(API_REFRESH_TOKEN_COOKIE_NAME);
|
Cookies.remove(API_REFRESH_TOKEN_COOKIE_NAME);
|
||||||
|
Cookies.remove(API_UID_COOKIE_NAME)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1394
src/components/Dashboard/Admin/DashboardAdminAusweis.svelte
Normal file
1394
src/components/Dashboard/Admin/DashboardAdminAusweis.svelte
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ripple } from "svelte-ripple-action";
|
import { ripple } from "svelte-ripple-action";
|
||||||
import type { RippleOptions } from "svelte-ripple-action/dist/constants";
|
import type { RippleOptions } from "svelte-ripple-action/dist/constants";
|
||||||
import { Home, Reader, EnvelopeClosed, Cube, Sun, Moon, Bell, Gear } from "radix-svelte-icons"
|
import { Home, Reader, EnvelopeClosed, Cube, Sun, Moon, Bell, Gear, LockClosed } from "radix-svelte-icons"
|
||||||
import NotificationProvider from "#components/NotificationProvider/NotificationProvider.svelte";
|
import NotificationProvider from "#components/NotificationProvider/NotificationProvider.svelte";
|
||||||
import DashboardNotification from "./DashboardNotification.svelte";
|
import DashboardNotification from "./DashboardNotification.svelte";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
const rippleOptions: RippleOptions = {
|
const rippleOptions: RippleOptions = {
|
||||||
center: false,
|
center: false,
|
||||||
color: "rgba(113, 128, 150, 0.1)",
|
color: lightTheme ? "rgba(233,233,233,0.1)" : "rgba(113, 128, 150, 0.1)",
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
@@ -66,10 +66,27 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</details></li>
|
</details></li>
|
||||||
|
<li><details>
|
||||||
|
<summary class="button-tab w-full outline-0 hover:outline-0">
|
||||||
|
<LockClosed width={22} height={22} />
|
||||||
|
Admin</summary>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a use:ripple={rippleOptions} class="button-tab" href="/dashboard/admin/ausweise-pruefen">
|
||||||
|
Ausweise Prüfen
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a use:ripple={rippleOptions} class="button-tab" href="/dashboard/admin/pdf-designer">
|
||||||
|
PDF Erstellen
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</details></li>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-auto flex flex-col gap-4 px-8">
|
<div class="mt-auto flex flex-col gap-4 px-8">
|
||||||
<div class="flex flex-row justify-between items-center">
|
<div class="flex flex-row justify-between items-center">
|
||||||
<ThemeController {lightTheme}></ThemeController>
|
<ThemeController bind:lightTheme></ThemeController>
|
||||||
<div class="dropdown dropdown-top">
|
<div class="dropdown dropdown-top">
|
||||||
<div class="indicator">
|
<div class="indicator">
|
||||||
{#if Object.keys($notifications).length > 0}
|
{#if Object.keys($notifications).length > 0}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import { VerbrauchsausweisWohnenClient, GebaeudeClient } from "#components/Ausweis/types";
|
import { VerbrauchsausweisWohnenClient, GebaeudeClient } from "#components/Ausweis/types";
|
||||||
import { getKlimafaktoren } from "#lib/Klimafaktoren";
|
import { getKlimafaktoren } from "#lib/Klimafaktoren";
|
||||||
import { getHeizwertfaktor } from "#lib/server/Heizwertfaktor";
|
import { getHeizwertfaktor } from "#lib/server/Heizwertfaktor";
|
||||||
import type {
|
|
||||||
GebaeudeStammdaten,
|
|
||||||
VerbrauchsausweisWohnen,
|
|
||||||
} from "@ibcornelsen/database/client";
|
|
||||||
|
|
||||||
export function energetischeNutzflaecheVerbrauchsausweisWohnen_2016(
|
export function energetischeNutzflaecheVerbrauchsausweisWohnen_2016(
|
||||||
ausweis: VerbrauchsausweisWohnenClient & {
|
ausweis: VerbrauchsausweisWohnenClient & {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { BenutzerClient, GebaeudeClient, UploadedGebaeudeBild, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types";
|
import { BenutzerClient, GebaeudeClient, UploadedGebaeudeBild, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types";
|
||||||
import { addNotification, updateNotification } from "@ibcornelsen/ui";
|
import { addNotification, updateNotification } from "@ibcornelsen/ui";
|
||||||
import { validateAccessTokenClient } from "#client/lib/validateAccessToken";
|
import { validateAccessTokenClient } from "../../client/lib/validateAccessToken";
|
||||||
import { client } from "src/trpc";
|
import { client } from "src/trpc";
|
||||||
import EmbeddedAuthFlowModule from "#modules/EmbeddedAuthFlowModule.svelte";
|
import EmbeddedAuthFlowModule from "#modules/EmbeddedAuthFlowModule.svelte";
|
||||||
import Overlay from "#components/Overlay.svelte";
|
import Overlay from "#components/Overlay.svelte";
|
||||||
|
|||||||
34
src/modules/Dashboard/DashboardAusweisePruefenModule.svelte
Normal file
34
src/modules/Dashboard/DashboardAusweisePruefenModule.svelte
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import DashboardAdminAusweis from "#components/Dashboard/Admin/DashboardAdminAusweis.svelte";
|
||||||
|
import DashboardAusweis from "#components/Dashboard/DashboardAusweis.svelte";
|
||||||
|
import DashboardAusweisSkeleton from "#components/Dashboard/DashboardAusweisSkeleton.svelte";
|
||||||
|
import { client } from "src/trpc";
|
||||||
|
|
||||||
|
let ausweisRequest = client.v1.benutzer.getAusweise.query({
|
||||||
|
limit: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1 class="text-4xl font-medium my-8">Ausweise</h1>
|
||||||
|
|
||||||
|
<div class="grid xl:grid-cols-2 grid-cols-1 gap-4">
|
||||||
|
|
||||||
|
{#await ausweisRequest}
|
||||||
|
<DashboardAusweisSkeleton></DashboardAusweisSkeleton>
|
||||||
|
<DashboardAusweisSkeleton></DashboardAusweisSkeleton>
|
||||||
|
|
||||||
|
{:then ausweise}
|
||||||
|
{#if ausweise.length == 0}
|
||||||
|
<div class="">
|
||||||
|
<h1 class="text-2xl">Es konnten keine Ausweise gefunden werden.</h1>
|
||||||
|
<p>Erstellen sie einen Verbrauchsausweis für ihr Wohngebäude <a href="/energieausweis-erstellen/verbrauchsausweis-wohnen">hier</a></p>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
{#each ausweise as ausweis}
|
||||||
|
<DashboardAdminAusweis {ausweis}></DashboardAdminAusweis>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
{/await}
|
||||||
|
|
||||||
|
</div>
|
||||||
98
src/modules/Dashboard/DashboardPDFDesignerModule.svelte
Normal file
98
src/modules/Dashboard/DashboardPDFDesignerModule.svelte
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Designer } from "@pdfme/ui";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
import { Template, BLANK_PDF } from "@pdfme/common";
|
||||||
|
|
||||||
|
let template: Template = {
|
||||||
|
basePdf: BLANK_PDF,
|
||||||
|
schemas: [
|
||||||
|
{
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let container: HTMLDivElement;
|
||||||
|
|
||||||
|
let designer: Designer;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
designer = new Designer({ domContainer: container, template });
|
||||||
|
});
|
||||||
|
|
||||||
|
function loadBasePDF() {
|
||||||
|
if (!loadTemplateInput.files) return;
|
||||||
|
|
||||||
|
const file = loadTemplateInput.files[0];
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = function (e) {
|
||||||
|
if (!e.target) return;
|
||||||
|
|
||||||
|
const basePdf = e.target.result as string;
|
||||||
|
const newTemplate = { ...template, basePdf };
|
||||||
|
designer = new Designer({ domContainer: container, template: newTemplate });
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addNewField() {
|
||||||
|
template = designer.getTemplate();
|
||||||
|
|
||||||
|
template.schemas[0]["new-field"] = {
|
||||||
|
type: "text",
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
width: 10,
|
||||||
|
height: 10
|
||||||
|
};
|
||||||
|
|
||||||
|
designer.updateTemplate(template);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportTemplate() {
|
||||||
|
const template = designer.getTemplate();
|
||||||
|
const blob = new Blob([JSON.stringify(template)], { type: "application/json" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = "template.json";
|
||||||
|
a.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTemplate() {
|
||||||
|
const input = document.createElement("input");
|
||||||
|
input.type = "file";
|
||||||
|
input.accept = ".json";
|
||||||
|
input.onchange = function (e) {
|
||||||
|
if (!input.files) return;
|
||||||
|
|
||||||
|
const file = input.files[0];
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = function (e) {
|
||||||
|
if (!e.target) return;
|
||||||
|
|
||||||
|
const template = JSON.parse(e.target.result as string) as Template;
|
||||||
|
designer = new Designer({ domContainer: container, template });
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsText(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
input.click()
|
||||||
|
}
|
||||||
|
|
||||||
|
let loadTemplateInput: HTMLInputElement;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<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>
|
||||||
|
<button class="btn btn-secondary" on:click={exportTemplate}>Export</button>
|
||||||
|
<button class="btn btn-secondary" on:click={loadTemplate}>Load Template</button>
|
||||||
|
<a class="btn btn-secondary" href="/dashboard/admin/pdf-viewer">Test in Viewer</a>
|
||||||
|
<input type="file" hidden bind:this={loadTemplateInput} on:change={loadBasePDF}>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div bind:this={container}></div>
|
||||||
95
src/modules/Dashboard/DashboardPDFViewerModule.svelte
Normal file
95
src/modules/Dashboard/DashboardPDFViewerModule.svelte
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Benutzer, GebaeudeStammdaten, Rechnungen, VerbrauchsausweisWohnen } from "@ibcornelsen/database/client";
|
||||||
|
import { Template } from "@pdfme/common";
|
||||||
|
import { Viewer } from "@pdfme/ui";
|
||||||
|
|
||||||
|
type AusweisData = VerbrauchsausweisWohnen & { benutzer: Benutzer, gebaeude_stammdaten: GebaeudeStammdaten, rechnungen: Rechnungen }
|
||||||
|
|
||||||
|
export let ausweise: AusweisData[];
|
||||||
|
|
||||||
|
let pdfInputs: AusweisData;
|
||||||
|
let template: Template;
|
||||||
|
let viewer: Viewer
|
||||||
|
|
||||||
|
function convertAusweisData(inputs: AusweisData): Record<string, string> {
|
||||||
|
return {
|
||||||
|
"gebaeude_stammdaten.adresse": inputs.gebaeude_stammdaten.adresse || "",
|
||||||
|
"gebaeude_stammdaten.gebaeudetyp": inputs.gebaeude_stammdaten.gebaeudetyp || "",
|
||||||
|
"gebaeude_stammdaten.baujahr_gebaeude": inputs.gebaeude_stammdaten.baujahr_gebaeude.join(", ") || "",
|
||||||
|
"gebaeude_stammdaten.baujahr_heizung": inputs.gebaeude_stammdaten.baujahr_heizung.join(", ") || "",
|
||||||
|
"gebaeude_stammdaten.plz": inputs.gebaeude_stammdaten.plz || "",
|
||||||
|
"gebaeude_stammdaten.ort": inputs.gebaeude_stammdaten.ort || "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function loadTemplate() {
|
||||||
|
const input = document.createElement("input");
|
||||||
|
input.type = "file";
|
||||||
|
input.accept = ".json";
|
||||||
|
|
||||||
|
input.onchange = function (e) {
|
||||||
|
if (!input.files) return;
|
||||||
|
|
||||||
|
const file = input.files[0];
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = async function (e) {
|
||||||
|
if (!e.target) return;
|
||||||
|
|
||||||
|
template = JSON.parse(e.target.result as string) as Template;
|
||||||
|
|
||||||
|
if (!pdfInputs) {
|
||||||
|
alert("Bitte wählen Sie einen Ausweis aus, um diesen als Basis zu verwenden.")
|
||||||
|
return
|
||||||
|
};
|
||||||
|
|
||||||
|
viewer = new Viewer({
|
||||||
|
domContainer: pdfViewerContainer,
|
||||||
|
template,
|
||||||
|
inputs: [convertAusweisData(pdfInputs)]
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsText(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
input.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeInputs(inputs: AusweisData) {
|
||||||
|
pdfInputs = inputs;
|
||||||
|
if (!template) {
|
||||||
|
alert("Bitte laden Sie zuerst ein Template.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer = new Viewer({
|
||||||
|
domContainer: pdfViewerContainer,
|
||||||
|
template,
|
||||||
|
inputs: [convertAusweisData(pdfInputs)]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let pdfViewerContainer: HTMLDivElement;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-secondary" on:click={loadTemplate}>Load Template</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<main class="grid grid-cols-[1fr_4fr]">
|
||||||
|
<div>
|
||||||
|
<h1>Ausweise</h1>
|
||||||
|
{#each ausweise as ausweis}
|
||||||
|
<div>
|
||||||
|
<h2 class="text-black">{ausweis.gebaeude_stammdaten.adresse}</h2>
|
||||||
|
<button class="btn btn-secondary" on:click={() => {
|
||||||
|
changeInputs(ausweis)
|
||||||
|
}}>Diesen Ausweis als Basis nehmen</button>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<div bind:this={pdfViewerContainer}></div>
|
||||||
|
</main>
|
||||||
15
src/pages/dashboard/admin/ausweise-pruefen.astro
Normal file
15
src/pages/dashboard/admin/ausweise-pruefen.astro
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
import UserLayout from "../../../layouts/UserLayout.astro";
|
||||||
|
import { validateAccessTokenServer } from "src/server/lib/validateAccessToken";
|
||||||
|
import DashboardAusweisePruefenModule from "#modules/Dashboard/DashboardAusweisePruefenModule.svelte";
|
||||||
|
|
||||||
|
const accessTokenValid = await validateAccessTokenServer(Astro);
|
||||||
|
|
||||||
|
if (!accessTokenValid) {
|
||||||
|
return Astro.redirect("/auth/login")
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
<UserLayout title="Dashboard">
|
||||||
|
<DashboardAusweisePruefenModule client:load></DashboardAusweisePruefenModule>
|
||||||
|
</UserLayout>
|
||||||
10
src/pages/dashboard/admin/pdf-designer.astro
Normal file
10
src/pages/dashboard/admin/pdf-designer.astro
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
import UserLayout from "../../../layouts/UserLayout.astro";
|
||||||
|
import DashboardPDFDesignerModule from "../../../modules/Dashboard/DashboardPDFDesignerModule.svelte";
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<UserLayout title="PDF Designer">
|
||||||
|
<DashboardPDFDesignerModule client:only></DashboardPDFDesignerModule>
|
||||||
|
</UserLayout>
|
||||||
18
src/pages/dashboard/admin/pdf-viewer.astro
Normal file
18
src/pages/dashboard/admin/pdf-viewer.astro
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
---
|
||||||
|
|
||||||
|
<UserLayout title="PDF Viewer">
|
||||||
|
<DashboardPDFViewerModule ausweise={ausweise} client:only></DashboardPDFViewerModule>
|
||||||
|
</UserLayout>
|
||||||
@@ -1,22 +1,10 @@
|
|||||||
---
|
---
|
||||||
import UserLayout from "../../layouts/UserLayout.astro";
|
import UserLayout from "../../layouts/UserLayout.astro";
|
||||||
import { API_UID_COOKIE_NAME } from "../../lib/constants";
|
import { validateAccessTokenServer } from "src/server/lib/validateAccessToken";
|
||||||
import UserModule from "#modules/UserModule.svelte";
|
|
||||||
import { prisma } from "@ibcornelsen/database/server";
|
|
||||||
|
|
||||||
const uid = Astro.cookies.get(API_UID_COOKIE_NAME).value
|
const accessTokenValid = await validateAccessTokenServer(Astro);
|
||||||
|
|
||||||
if (!uid) {
|
if (!accessTokenValid) {
|
||||||
return Astro.redirect("/auth/login")
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await prisma.benutzer.findUnique({
|
|
||||||
where: {
|
|
||||||
uid
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return Astro.redirect("/auth/login")
|
return Astro.redirect("/auth/login")
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,42 +1,57 @@
|
|||||||
import { createCaller } from "#lib/caller";
|
import { createCaller } from "#lib/caller";
|
||||||
import { API_ACCESS_TOKEN_COOKIE_NAME, API_REFRESH_TOKEN_COOKIE_NAME } from "#lib/constants";
|
import { API_ACCESS_TOKEN_COOKIE_NAME, API_REFRESH_TOKEN_COOKIE_NAME, API_UID_COOKIE_NAME } from "#lib/constants";
|
||||||
import type { AstroGlobal } from "astro";
|
import type { AstroGlobal } from "astro";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
|
||||||
|
|
||||||
export async function validateAccessTokenServer(astro: Readonly<AstroGlobal<Record<string, any>>>) {
|
export async function validateAccessTokenServer(astro: AstroGlobal) {
|
||||||
const accessToken = astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME).value;
|
const accessToken = astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME).value;
|
||||||
const refreshToken = astro.cookies.get(API_REFRESH_TOKEN_COOKIE_NAME).value;
|
const refreshToken = astro.cookies.get(API_REFRESH_TOKEN_COOKIE_NAME).value;
|
||||||
|
|
||||||
if (accessToken) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wir haben keinen Access Token mehr, vielleicht ist dieser ausgelaufen.
|
|
||||||
// Schauen wir mal, ob wir einen Refresh Token haben.
|
|
||||||
|
|
||||||
if (!refreshToken) {
|
if (!refreshToken) {
|
||||||
// Wir haben keinen Refresh Token, also müssen wir uns neu anmelden.
|
// Wir haben keinen Refresh Token, also müssen wir uns neu anmelden.
|
||||||
|
astro.cookies.delete(API_ACCESS_TOKEN_COOKIE_NAME);
|
||||||
|
astro.cookies.delete(API_REFRESH_TOKEN_COOKIE_NAME);
|
||||||
|
astro.cookies.delete(API_UID_COOKIE_NAME)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (accessToken) {
|
||||||
|
const { valid } = await createCaller(astro).v1.benutzer.validateAccessToken({accessToken})
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
astro.cookies.delete(API_ACCESS_TOKEN_COOKIE_NAME);
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wir haben keinen Access Token mehr, vielleicht ist dieser ausgelaufen.
|
||||||
// Wir haben einen Refresh Token, also versuchen wir uns damit anzumelden.
|
// Wir haben einen Refresh Token, also versuchen wir uns damit anzumelden.
|
||||||
// Wenn das klappt, dann haben wir auch einen neuen Access Token.
|
// Wenn das klappt, dann haben wir auch einen neuen Access Token.
|
||||||
// Wenn das nicht klappt, dann müssen wir uns neu anmelden.
|
// Wenn das nicht klappt, dann müssen wir uns neu anmelden.
|
||||||
|
// TODO: Schlägt fehl! Ich habe keine Ahnung warum. Ich habe das Gefühl, dass das an der Astro-Implementierung liegt. Der Refresh Token updated sich nicht richtig....
|
||||||
try {
|
try {
|
||||||
const { accessToken: newAccessToken, exp } = await createCaller(astro).v1.benutzer.getAccessToken({
|
const { accessToken: newAccessToken, accessTokenExpiry, refreshToken: newRefreshToken, refreshTokenExpiry } = await createCaller(astro).v1.benutzer.getAccessToken({
|
||||||
refreshToken
|
refreshToken
|
||||||
})
|
})
|
||||||
|
|
||||||
astro.cookies.set(API_ACCESS_TOKEN_COOKIE_NAME, newAccessToken, {
|
astro.cookies.set(API_ACCESS_TOKEN_COOKIE_NAME, newAccessToken, {
|
||||||
domain: `.${astro.url.host}`,
|
domain: `.${astro.url.host}`,
|
||||||
path: "/",
|
path: "/",
|
||||||
expires: moment.unix(exp).toDate()
|
expires: moment.unix(accessTokenExpiry).toDate()
|
||||||
});
|
});
|
||||||
|
astro.cookies.set(API_REFRESH_TOKEN_COOKIE_NAME, newRefreshToken, {
|
||||||
|
domain: `.${astro.url.host}`,
|
||||||
|
path: "/",
|
||||||
|
expires: moment.unix(refreshTokenExpiry).toDate()
|
||||||
|
})
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
astro.cookies.delete(API_ACCESS_TOKEN_COOKIE_NAME);
|
astro.cookies.delete(API_ACCESS_TOKEN_COOKIE_NAME);
|
||||||
astro.cookies.delete(API_REFRESH_TOKEN_COOKIE_NAME);
|
astro.cookies.delete(API_REFRESH_TOKEN_COOKIE_NAME);
|
||||||
|
astro.cookies.delete(API_UID_COOKIE_NAME)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,14 @@ module.exports = {
|
|||||||
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}", "./node_modules/@ibcornelsen/ui/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
|
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}", "./node_modules/@ibcornelsen/ui/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
|
||||||
darkMode: "class",
|
darkMode: "class",
|
||||||
plugins: [require("daisyui"), require("@tailwindcss/typography")],
|
plugins: [require("daisyui"), require("@tailwindcss/typography")],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
"pdf-yellow-bright": "#f3cb00",
|
||||||
|
"pdf-yellow-light": "#fff6ca",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
daisyui: {
|
daisyui: {
|
||||||
themes: [{
|
themes: [{
|
||||||
light: {
|
light: {
|
||||||
@@ -43,9 +51,7 @@ module.exports = {
|
|||||||
|
|
||||||
'--btn-text-case': 'normal',
|
'--btn-text-case': 'normal',
|
||||||
'--navbar-padding': '.5rem',
|
'--navbar-padding': '.5rem',
|
||||||
'--border-btn': '1px',
|
'--border-btn': '1px'
|
||||||
"pdf-yellow-bright": "#f3cb00",
|
|
||||||
"pdf-yellow-light": "#fff6ca",
|
|
||||||
},
|
},
|
||||||
'dark': {
|
'dark': {
|
||||||
'primary': '#ff7d26',
|
'primary': '#ff7d26',
|
||||||
|
|||||||
Reference in New Issue
Block a user