Dashboard

This commit is contained in:
Moritz Utcke
2025-02-21 23:57:46 +11:00
parent d6e137d50f
commit 6a51b0b02f
35 changed files with 648 additions and 274 deletions

View File

@@ -5,23 +5,25 @@ export const createCaller = createCallerFactory({
"klimafaktoren": await import("../src/pages/api/klimafaktoren.ts"),
"postleitzahlen": await import("../src/pages/api/postleitzahlen.ts"),
"unterlage": await import("../src/pages/api/unterlage.ts"),
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"),
"admin/ausstellen": await import("../src/pages/api/admin/ausstellen.ts"),
"admin/bestellbestaetigung": await import("../src/pages/api/admin/bestellbestaetigung.ts"),
"admin/erinnern": await import("../src/pages/api/admin/erinnern.ts"),
"admin/nicht-ausstellen": await import("../src/pages/api/admin/nicht-ausstellen.ts"),
"admin/post-ausstellen": await import("../src/pages/api/admin/post-ausstellen.ts"),
"admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"),
"admin/stornieren": await import("../src/pages/api/admin/stornieren.ts"),
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"),
"ausweise": await import("../src/pages/api/ausweise/index.ts"),
"bedarfsausweis-wohnen/[uid]": await import("../src/pages/api/bedarfsausweis-wohnen/[uid].ts"),
"bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"),
"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"),
"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"),
"geg-nachweis-gewerbe/[uid]": await import("../src/pages/api/geg-nachweis-gewerbe/[uid].ts"),
"geg-nachweis-gewerbe": await import("../src/pages/api/geg-nachweis-gewerbe/index.ts"),
"geg-nachweis-wohnen/[uid]": await import("../src/pages/api/geg-nachweis-wohnen/[uid].ts"),
"geg-nachweis-wohnen": await import("../src/pages/api/geg-nachweis-wohnen/index.ts"),
"geg-nachweis-gewerbe/[uid]": await import("../src/pages/api/geg-nachweis-gewerbe/[uid].ts"),
"geg-nachweis-gewerbe": await import("../src/pages/api/geg-nachweis-gewerbe/index.ts"),
"objekt": await import("../src/pages/api/objekt/index.ts"),
"rechnung/anfordern": await import("../src/pages/api/rechnung/anfordern.ts"),
"rechnung": await import("../src/pages/api/rechnung/index.ts"),

View File

@@ -1,4 +1,5 @@
import { writable, Writable } from "svelte/store";
import { ZodEffects, ZodNullable, ZodOptional, ZodType } from "zod";
export function localStorageSync<T>(initial: T, name: string, modifier: (stored: any) => T = (stored) => JSON.parse(stored), reverseModifier: (value: T) => string = (value) => JSON.stringify(value)): Writable<T> {
const stored = localStorage.getItem(name) as T
@@ -15,4 +16,30 @@ export function localStorageSync<T>(initial: T, name: string, modifier: (stored:
})
return writableStore
}
export function isZodInstanceOf<T extends ZodType<any>>(
schema: ZodType<any>,
targetType: new (...args: any) => T
): schema is T {
if (schema instanceof targetType) {
return true;
}
if (schema instanceof ZodOptional || schema instanceof ZodNullable) {
return isZodInstanceOf(schema._def.innerType, targetType);
}else if (schema instanceof ZodEffects) {
return getZodBaseType(schema._def.schema)
}
return false;
}
export function getZodBaseType(schema: ZodType<any>): ZodType<any> {
if (schema instanceof ZodOptional || schema instanceof ZodNullable) {
return getZodBaseType(schema._def.innerType);
} else if (schema instanceof ZodEffects) {
return getZodBaseType(schema._def.schema)
}
return schema;
}

View File

@@ -48,7 +48,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required
data-cy="ausstellgrund"
>
<option disabled selected>Bitte auswählen</option>
<option disabled selected value={null}>Bitte auswählen</option>
{#each Object.entries(Enums.Ausstellgrund) as [name, ausstellgrund]}
<option value={ausstellgrund}>{name}</option>
{/each}
@@ -72,7 +72,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required
bind:value={aufnahme.gebaeudetyp}
>
<option disabled selected>Bitte auswählen</option>
<option disabled selected value={null}>Bitte auswählen</option>
{#if ausweisart==Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisWohnen}
<option value="Einfamilienhaus">Einfamilienhaus</option>
@@ -224,7 +224,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required
bind:value={aufnahme.saniert}
>
<option disabled selected>Bitte auswählen</option>
<option disabled selected value={null}>Bitte auswählen</option>
<option value={true}>saniert</option>
<option value={false}>unsaniert</option>
</select>

View File

@@ -121,7 +121,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
bind:value={aufnahme.dachgeschoss}
required
>
<option disabled selected>Bitte auswählen</option>
<option disabled selected value={null}>Bitte auswählen</option>
<option value={Enums.Heizungsstatus.NICHT_VORHANDEN}>nicht vorhanden</option>
<option value={Enums.Heizungsstatus.UNBEHEIZT}>unbeheizt</option>
<option value={Enums.Heizungsstatus.BEHEIZT}>beheizt</option>

View File

@@ -29,7 +29,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
bind:value={aufnahme.gebaeudeteil}
required
>
<option disabled selected>Bitte auswählen</option>
<option disabled selected value={null}>Bitte auswählen</option>
<option value="Gesamtgebäude">Gesamtgebäude</option>
<option value="Wohnen">Wohnen</option>
</select>
@@ -79,7 +79,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required
bind:value={aufnahme.lueftung}
>
<option disabled selected>Bitte auswählen</option>
<option disabled selected value={null}>Bitte auswählen</option>
<option value="Fensterlueftung">Fensterlüftung</option>
<option value="Schachtlueftung">Schachtlüftung</option>
<option value="LueftungsanlageOhneWaermerueckgewinnung"
@@ -112,7 +112,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required
bind:value={aufnahme.kuehlung}
>
<option disabled selected>Bitte auswählen</option>
<option disabled selected value={null}>Bitte auswählen</option>
<option value="1">vorhanden</option>
<option value="0">nicht vorhanden</option>
</select>

View File

@@ -223,7 +223,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
bind:value={aufnahme.brennstoff_1}
required
>
<option disabled selected>Bitte auswählen</option>
<option disabled selected value={null}>Bitte auswählen</option>
{#each Object.keys(fuelMap) as fuel}
<option value={fuel}>{fuel}</option>
{/each}

View File

@@ -165,9 +165,10 @@ export type ObjektKomplettClient = ObjektClient & {
export type AufnahmeKomplettClient = AufnahmeClient & {
bilder: BildClient[],
unterlagen: UnterlageClient[],
bedarfsausweis_wohnen?: BedarfsausweisWohnenClient,
verbrauchsausweis_wohnen?: VerbrauchsausweisWohnenClient,
verbrauchsausweis_gewerbe?: VerbrauchsausweisGewerbeClient
bedarfsausweise_wohnen: BedarfsausweisWohnenClient[],
verbrauchsausweise_wohnen: VerbrauchsausweisWohnenClient[],
verbrauchsausweise_gewerbe: VerbrauchsausweisGewerbeClient[],
geg_nachweise_wohnen: GEGNachweisWohnenClient[]
}
export type GEGNachweisWohnenClient = Omit<GEGNachweisWohnen, "id" | "aufnahme_id" | "benutzer_id"> & {

View File

@@ -9,7 +9,7 @@
VerbrauchsausweisWohnenClient,
} from "./Ausweis/types.js";
import AusweisPruefenTooltip from "./AusweisPruefenTooltip.svelte";
import { addNotification } from "./NotificationProvider/shared.js";
import { addNotification } from "#components/Notifications/shared.js";
import { CheckCircled, CrossCircled, Image } from "radix-svelte-icons";
import ChevronDown from "radix-svelte-icons/src/lib/icons/ChevronDown.svelte";
import { Event } from "@ibcornelsen/database/client";
@@ -31,16 +31,16 @@
async function ausweisAusstellen(uid: string) {
try {
await api.admin.ausstellen.GET.fetch({
uid
}, {
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
uid_ausweis: uid
}, {
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
} catch(e) {
addNotification({
title: "Das hat nicht geklappt.",
description: e.cause.statusText,
message: "Das hat nicht geklappt.",
subtext: e as string,
timeout: 3000,
type: "error",
})
@@ -49,7 +49,7 @@
const ausweisArt = getAusweisartFromUUID(ausweis.uid) // TODO: Das ist ein Platzhalter, hier muss die Ausweisart aus dem Ausweisobjekt kommen
const ausweisArt = getAusweisartFromUUID(ausweis.uid)
let verbrauchWWGesamt_1 = "";
let verbrauchWWGesamt_2 = "";
@@ -124,12 +124,12 @@
let Abgeschlossen: any;
if (aufnahme.erledigt) {
if (ausweis.ausgestellt) {
Ausweisbild = "/images/dashboard/ausweishaken.jpg";
DatenBlattBild = "/images/dashboard/datenblatthaken.jpg";
StatusIcon = "/images/dashboard/erledigt.svg";
Abgeschlossen = 0;
} else if (aufnahme.bestellt) {
} else if (ausweis.bestellt) {
Ausweisbild = "/images/dashboard/ausweis.jpg";
DatenBlattBild = "/images/dashboard/datenblatt.jpg";
StatusIcon = "/images/dashboard/bestellt.svg";
@@ -147,7 +147,7 @@
symbolPruefung = "/images/dashboard/kreiskreuz.png";
}
if (aufnahme.zurueckgestellt) {
if (ausweis.zurueckgestellt) {
zurueckGestellt =
"<img src='/images/dashboard/zurueckGestellt.svg' alt='Status' width=\"25\" height=\"25\"></img>";
} else {
@@ -456,16 +456,33 @@
// }
async function stornieren(ausweis: VerbrauchsausweisWohnenClient) {
addNotification({
title: "Ausweis wurde storniert",
type: "success",
dismissable: true,
timeout: 3000,
})
try {
const response = await api.admin.stornieren.PUT.fetch({
uid_ausweis: ausweis.uid
}, {
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
ausweis.aufnahme.storniert = true;
addNotification({
message: "Ausweis wurde storniert",
type: "success",
dismissable: true,
timeout: 3000,
})
ausweis = ausweis;
ausweis.storniert = true;
ausweis = ausweis;
} catch(e) {
addNotification({
message: "Ausweis konnte nicht storniert werden.",
subtext: e as string,
type: "error",
dismissable: true,
timeout: 3000,
})
}
}
let bilderModal: HTMLDialogElement;
@@ -473,13 +490,23 @@
async function registriernummerAnfordern(uid: string) {
const result = await api.admin.registriernummer.GET.fetch({
uid
}, {
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
try {
const result = await api.admin.registriernummer.GET.fetch({
uid
}, {
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
} catch(e) {
addNotification({
message: "Registriernummer anfordern fehlgeschlagen.",
subtext: e as string,
type: "error",
dismissable: true,
timeout: 3000,
})
}
}
</script>
@@ -909,4 +936,4 @@
</div>
</div>
<NotificationWrapper></NotificationWrapper>
<NotificationWrapper />

View File

@@ -0,0 +1,68 @@
<script lang="ts">
import { getZodBaseType } from "#client/lib/helpers.js";
import { filterAusweise } from "#lib/filters.js";
import { ZodTypeAny } from "astro:schema";
import { Cross1 } from "radix-svelte-icons";
import z, { ZodBoolean, ZodNativeEnum, ZodNumber } from "zod"
export let filters: { name: keyof z.infer<typeof filterAusweise>, type: ZodTypeAny, value: any }[] = []
</script>
{#each filters as filter, i}
{@const type = getZodBaseType(filter.type)}
<div class="flex flex-row bg-white gap-4 px-2 py-2 rounded-lg">
{#if i === 0}
<span class="badge">where</span>
{:else}
<span class="badge">and</span>
{/if}
<select on:change={function(e) {
delete filters[filter.name]
filter.name = e.target.value;
filter.type = filterAusweise._def.shape()[filter.name]
filters = filters.filter(Boolean);
}}>
<option value={filter.name} selected>{filter.name}</option>
{#each Object.keys(filterAusweise._def.shape()) as n}
{#if !filters.find(filter => filter.name === n)}
<option value={n}>{n}</option>
{/if}
{/each}
</select>
<span class="badge">equals</span>
{#if type instanceof ZodNumber}
<input type="number" bind:value={filter.value}>
{:else if type instanceof ZodBoolean}
<select bind:value={filter.value}>
<option value={true}>true</option>
<option value={false}>false</option>
</select>
{:else if type instanceof ZodNativeEnum}
<select bind:value={filter.value}>
{#each Object.entries(type._def.values) as [key, value]}
<option {value}>{key}</option>
{/each}
</select>
{:else}
<input type="text" bind:value={filter.value}>
{/if}
<Cross1 size={24} class="cursor-pointer"></Cross1>
</div>
{/each}
<button on:click={() => {
const entry = Object.entries(filterAusweise._def.shape())[0]
filters.push({
name: entry[0],
type: entry[1],
value: null
})
filters = filters
}}>Filter Hinzufügen</button>
<style>
.badge {
@apply rounded-lg px-2 py-1 bg-gray-500 text-white;
}
</style>

View File

@@ -13,11 +13,11 @@
import { api } from "astro-typesafe-api/client";
import Cookies from "js-cookie";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
import { Enums } from "@ibcornelsen/database/client";
import { Enums, Objekt } from "@ibcornelsen/database/client";
export let ausweis: VerbrauchsausweisWohnenClient;
export let aufnahme: AufnahmeKomplettClient;
export let objekt: ObjektKomplettClient;
export let objekt: Objekt;
export let progress: number;
const ausweisart = getAusweisartFromUUID(ausweis.uid);
@@ -188,14 +188,14 @@
</div>
</div>
{/await}
<div class="card-actions justify-end mt-8">
<div class="flex flex-row justify-end gap-4 mt-4">
<a
class="btn btn-primary"
class="button text-sm"
href="/energieausweis-erstellen/verbrauchsausweis-wohngebaeude?uid={ausweis.uid}"
>Bearbeiten</a
>
<a
class="btn btn-ghost"
class="p-2 rounded-lg hover:bg-gray-200"
title="PDF Herunterladen"
target="_blank"
href="/pdf/ansichtsausweis?uid={ausweis.uid}"

View File

@@ -1,3 +1,5 @@
<script lang="ts"></script>
<div class="card lg:card-side bg-base-200 card-bordered border-base-300">
<figure class="lg:w-1/2 min-h-72 skeleton"></figure>
<div class="card-body lg:w-1/2">

View File

@@ -2,12 +2,12 @@
import { ObjektKomplettClient } from "#components/Ausweis/types.js";
import { Enums } from "@ibcornelsen/database/server";
import moment from "moment";
import { OpenInNewWindow } from "radix-svelte-icons";
import { File, OpenInNewWindow } from "radix-svelte-icons";
export let objekt: ObjektKomplettClient;
</script>
<div class="border rounded-lg border-base-300 bg-white">
<div class="border rounded-lg border-base-300 bg-white break-inside-avoid-column mb-4">
{#if objekt.aufnahmen.length > 0}
{@const bild = objekt.aufnahmen[0].bilder.find(bild => bild.kategorie === Enums.BilderKategorie.Gebaeude)}
@@ -17,13 +17,28 @@
{/if}
<div class="p-4">
<div class="flex flex-row justify-between">
<h3 class="text-lg font-medium">{objekt.adresse}</h3>
<div class="flex flex-row justify-between items-center">
<h3 class="text-lg font-medium">{objekt.adresse}, {objekt.plz} {objekt.ort}</h3>
<span class="text-sm opacity-70 font-medium">{moment(objekt.erstellungsdatum).format("DD.MM.YYYY")}</span>
</div>
<div class="flex flex-row justify-end">
<a href="/dashboard/objekt/{objekt.uid}" class="rounded-lg p-2.5 hover:bg-gray-200" target="_blank"><OpenInNewWindow size={20}></OpenInNewWindow></a>
<div class="flex flex-col gap-2 mt-4">
{#each objekt.aufnahmen as aufnahme}
<div class="border rounded-lg px-4 py-2">
<div class="flex flex-row justify-between items-center">
<span>Sanierungsstand vom {moment(aufnahme.erstellungsdatum).format("DD.MM.YYYY")}</span>
<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}
<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}
<a href="/dashboard/aufnahme/{aufnahme.uid}" class="rounded-lg p-2 hover:bg-gray-100 transition-all"><File size={20}></File></a>
{/if}
</div>
</div>
{/each}
</div>
</div>
</div>

View File

@@ -46,16 +46,12 @@
/></a
>
<div class="menu flex flex-col gap-2 mt-0 md:mt-12 px-0">
<div class="flex flex-col gap-2 mt-0 md:mt-12 px-0">
<a use:ripple={rippleOptions} class="button-tab" href="/dashboard">
<Home width={22} height={22} />
Home
</a>
<a use:ripple={rippleOptions} class="button-tab" href="/dashboard/ausweise">
<Reader width={22} height={22} />
Ausweise
Objekte
</a>
<button use:ripple={rippleOptions} class="button-tab">
<!-- <button use:ripple={rippleOptions} class="button-tab">
<EnvelopeClosed width={22} height={22} />
Kontakt
</button>
@@ -75,7 +71,7 @@
</button>
</li>
</ul>
</details></li>
</details></li> -->
{#if benutzer.rolle === "ADMIN"}
<li><details class="[&_.caret]:open:rotate-180">
<summary class="button-tab w-full outline-0 hover:outline-0 cursor-pointer">

View File

@@ -9,11 +9,8 @@
import TagInput from "../TagInput.svelte";
import { Enums } from "@ibcornelsen/database/client";
import {
BedarfsausweisWohnenClient,
AufnahmeClient,
AufnahmeClient,
ObjektClient,
VerbrauchsausweisGewerbeClient,
VerbrauchsausweisWohnenClient,
GEGNachweisWohnenClient,
} from "../Ausweis/types.js";
@@ -45,7 +42,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required
data-cy="ausstellgrund"
>
<option disabled selected>Bitte auswählen</option>
<option disabled selected value={null}>Bitte auswählen</option>
<option value={Enums.Ausstellgrund.Neubau}>Neubau</option>
<option value={Enums.Ausstellgrund.Modernisierung}>Modernisierung</option>
</select>
@@ -68,7 +65,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required
bind:value={aufnahme.gebaeudetyp}
>
<option disabled selected>Bitte auswählen</option>
<option disabled selected value={null}>Bitte auswählen</option>
{#if ausweisart === Enums.Ausweisart.GEGNachweisWohnen}
<option value="Einfamilienhaus">Einfamilienhaus</option>

View File

@@ -0,0 +1,33 @@
<script lang="ts">
import { ChevronLeft, ChevronRight } from "radix-svelte-icons";
export let pages: number;
export let current: number
export let next: string;
export let prev: string;
</script>
<div class='flex items-center justify-center gap-2 my-4'>
<a href={prev}
class:disabled={current === 1 ? true : false}
aria-label="left arrow icon"
aria-describedby="prev"
class="flex p-2 hover:bg-gray-200 rounded-lg">
<ChevronLeft></ChevronLeft>
</a>
<p>{current} of {pages}</p>
<a href={next}
class:disabled={current === pages ? true : false}
aria-label="right arrow icon"
aria-describedby="next"
class="flex p-2 hover:bg-gray-200 rounded-lg">
<ChevronRight></ChevronRight>
</a>
</div>
<style>
.disabled {
@apply pointer-events-none cursor-default text-gray-500;
}
</style>

View File

@@ -43,7 +43,7 @@
<div>
<h4>Kategorie *</h4>
<select class="select select-bordered" bind:value={category}>
<option value="" disabled selected>Bitte Auswählen</option>
<option value={null} disabled selected>Bitte Auswählen</option>
<option value="Verständnisproblem">Verständnisproblem</option>
<option value="Technischer Fehler">Technischer Fehler</option>
<option value="Feature anfordern">Feature anfordern</option>

11
src/lib/filters.ts Normal file
View File

@@ -0,0 +1,11 @@
import { UUidWithPrefix } from "#components/Ausweis/types.js";
import { Enums } from "@ibcornelsen/database/client";
import { z } from "zod";
export const filterAusweise = z.object({
uid: UUidWithPrefix.optional(),
ausgestellt: z.boolean().optional(),
ausstellgrund: z.nativeEnum(Enums.Ausstellgrund).optional(),
bestellt: z.boolean().optional(),
zurueckgestellt: z.boolean().optional()
})

View File

@@ -8,16 +8,16 @@ export async function getObjektKomplettClient(uid: string): Promise<ObjektKomple
uid
},
include: {
bilder: true,
aufnahmen: {
include: {
bedarfsausweis_wohnen: true,
verbrauchsausweis_gewerbe: true,
verbrauchsausweis_wohnen: true,
bilder: true,
unterlagen: true,
bedarfsausweise_wohnen: true,
verbrauchsausweise_gewerbe: true,
verbrauchsausweise_wohnen: true,
events: true
}
},
unterlagen: true
}
})
@@ -26,27 +26,29 @@ export async function getObjektKomplettClient(uid: string): Promise<ObjektKomple
}
return {
...omit(objekt, ["benutzer_id", "id", "aufnahmen", "bilder"]),
...omit(objekt, ["benutzer_id", "id"]),
aufnahmen: objekt.aufnahmen.map(aufnahme => ({
...omit(aufnahme, ["id", "objekt_id", "benutzer_id", "bedarfsausweis_wohnen", "verbrauchsausweis_gewerbe", "verbrauchsausweis_wohnen"]),
bedarfsausweis_wohnen: (aufnahme.bedarfsausweis_wohnen && {
...omit(aufnahme.bedarfsausweis_wohnen, ["id", "aufnahme_id", "benutzer_id"]),
...omit(aufnahme, ["id", "objekt_id", "benutzer_id", "bedarfsausweise_wohnen", "verbrauchsausweise_gewerbe", "verbrauchsausweise_wohnen"]),
bedarfsausweise_wohnen: aufnahme.bedarfsausweise_wohnen.map(ausweis => ({
...omit(ausweis, ["id", "aufnahme_id", "benutzer_id"]),
uid_aufnahme: aufnahme.uid,
uid_objekt: objekt.uid
}) || undefined,
verbrauchsausweis_wohnen: (aufnahme.verbrauchsausweis_wohnen && {
...omit(aufnahme.verbrauchsausweis_wohnen, ["id", "aufnahme_id", "benutzer_id"]),
})),
verbrauchsausweise_wohnen:
aufnahme.verbrauchsausweise_wohnen.map(ausweis => ({
...omit(ausweis, ["id", "aufnahme_id", "benutzer_id"]),
uid_aufnahme: aufnahme.uid,
uid_objekt: objekt.uid
}) || undefined,
verbrauchsausweis_gewerbe: (aufnahme.verbrauchsausweis_gewerbe && {
...omit(aufnahme.verbrauchsausweis_gewerbe, ["id", "aufnahme_id", "benutzer_id"]),
})),
verbrauchsausweise_gewerbe:
aufnahme.verbrauchsausweise_gewerbe.map(ausweis => ({
...omit(ausweis, ["id", "aufnahme_id", "benutzer_id"]),
uid_aufnahme: aufnahme.uid,
uid_objekt: objekt.uid
}) || undefined,
})),
uid_objekt: objekt.uid,
})),
bilder: objekt.bilder.map(bild => omit(bild, ["id", "objekt_id"])),
unterlagen: objekt.unterlagen.map(unterlage => omit(unterlage, ["id", "objekt_id"]))
bilder: aufnahme.bilder.map(bild => omit(bild, ["id", "aufnahme_id"])),
unterlagen: aufnahme.unterlagen.map(unterlage => omit(unterlage, ["id", "aufnahme_id"]))
}))
}
}

View File

@@ -0,0 +1,44 @@
<script lang="ts">
import { AufnahmeKomplettClient, BenutzerClient } from "#components/Ausweis/types.js";
import Carousel from "#components/Carousel.svelte";
import DashboardAusweis from "#components/Dashboard/DashboardAusweis.svelte";
import { Objekt } from "@ibcornelsen/database/client";
import { ChevronLeft, ChevronRight, Plus } from "radix-svelte-icons";
export let user: BenutzerClient;
export let aufnahme: AufnahmeKomplettClient;
export let objekt: Objekt;
</script>
<h1 class="text-4xl font-medium mb-8">{objekt.adresse}, {objekt.plz} {objekt.ort}</h1>
<div class="bg-white rounded-lg">
{#if aufnahme.bilder.length > 0}
<Carousel perPage={1}>
{#each aufnahme.bilder as bild, i (i)}
<img src="/bilder/{bild.uid}.webp" alt={bild.kategorie} class="max-h-[60vh] h-full w-full object-contain">
{/each}
<span slot="left-control" class="p-2.5 bg-opacity-50 bg-white block rounded-full"><ChevronLeft size={24}></ChevronLeft></span>
<span slot="right-control" class="p-2.5 bg-opacity-50 bg-white block rounded-full"><ChevronRight size={24}></ChevronRight></span>
</Carousel>
{/if}
</div>
<div class="flex flex-row gap-4">
<button class="button flex flex-row rounded-lg gap-2"><Plus size={20}></Plus> Verbrauchsausweis Wohnen Erstellen</button>
<button class="button flex flex-row rounded-lg gap-2"><Plus size={20}></Plus> Verbrauchsausweis Gewerbe Erstellen</button>
<button class="button flex flex-row rounded-lg gap-2"><Plus size={20}></Plus> Bedarfsausweis Erstellen</button>
</div>
<div class="my-8 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>
{/each}
{#each aufnahme.bedarfsausweise_wohnen as ausweis}
<DashboardAusweis {ausweis} {aufnahme} {objekt} progress={0}></DashboardAusweis>
{/each}
{#each aufnahme.verbrauchsausweise_gewerbe as ausweis}
<DashboardAusweis {ausweis} {aufnahme} {objekt} progress={0}></DashboardAusweis>
{/each}
</div>

View File

@@ -1,114 +1,126 @@
<script lang="ts">
import {
AufnahmeClient,
ObjektClient,
UploadedGebaeudeBild,
VerbrauchsausweisWohnenClient,
} from "#components/Ausweis/types.js";
import AusweisPruefenBox from "#components/AusweisPruefenBox.svelte";
import NotificationProvider from "#components/NotificationProvider/NotificationProvider.svelte";
import { endEnergieVerbrauchVerbrauchsausweis_2016 } from "#lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016.js";
import AusweisPruefenNotification from "#components/AusweisPruefenNotification.svelte";
import DashboardAusweisSkeleton from "#components/Dashboard/DashboardAusweisSkeleton.svelte"
import { Event } from "@ibcornelsen/database/client";
import Pagination from "#components/Pagination.svelte";
import { api } from "astro-typesafe-api/client";
import Cookies from "js-cookie"
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js"
import AusweisePruefenFilter from "#components/Dashboard/AusweisePruefenFilter.svelte";
import { z, ZodTypeAny } from "zod";
import { filterAusweise } from "#lib/filters.js";
export let ausweise: {
ausweis: VerbrauchsausweisWohnenClient,
aufnahme: AufnahmeClient,
objekt: ObjektClient,
bilder: UploadedGebaeudeBild[],
events: Event[]
}[];
export let page: number;
export let totalPages: number;
let filters: { name: keyof z.infer<typeof filterAusweise>, type: ZodTypeAny, value: any }[] = []
</script>
<div class="gap-4 flex flex-col">
{#each ausweise as { ausweis, objekt, aufnahme, bilder, events }}
{#await endEnergieVerbrauchVerbrauchsausweis_2016(ausweis, aufnahme, objekt)}
<div class="rounded-lg border w-full h-20 p-2.5 gap-4 flex flex-row items-center">
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-8 h-8 rounded-full"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
</div>
{:then calculations}
<AusweisPruefenBox {ausweis} {aufnahme} {objekt} {bilder} {events} {calculations}></AusweisPruefenBox>
{/await}
{/each}
<div class="flex flex-col mb-4">
<AusweisePruefenFilter bind:filters={filters}></AusweisePruefenFilter>
</div>
<div class="flex items-center justify-center mt-12">
<div class="join">
<button class="join-item btn btn-ghost shadow-none">1</button>
<button class="join-item btn btn-ghost shadow-none">2</button>
<button class="join-item btn btn-ghost shadow-none">3</button>
<button class="join-item btn btn-ghost shadow-none">4</button>
</div>
<div class="gap-4 flex flex-col">
{#await api.ausweise.GET.fetch({
limit: 10,
skip: (page - 1) * 10,
filters: filters.reduce((acc, filter) => {
acc[filter.name] = filter.value
return acc
}, {})
}, {
headers: {
Authorization: `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})}
<DashboardAusweisSkeleton></DashboardAusweisSkeleton>
{:then ausweise}
{#each ausweise as { ausweis, objekt, aufnahme, bilder, events }}
{#await endEnergieVerbrauchVerbrauchsausweis_2016(ausweis, aufnahme, objekt)}
<div class="rounded-lg border w-full h-20 p-2.5 gap-4 flex flex-row items-center">
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-8 h-8 rounded-full"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="w-1/12 h-full flex flex-col gap-2">
<div class="skeleton w-full h-4"></div>
<div class="skeleton w-full h-4"></div>
</div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
<div class="skeleton w-4 h-4"></div>
</div>
{:then calculations}
<AusweisPruefenBox {ausweis} {aufnahme} {objekt} {bilder} {events} {calculations}></AusweisPruefenBox>
{/await}
{/each}
{/await}
</div>
<Pagination pages={totalPages} current={page} prev="/dashboard/admin/ausweise-pruefen/{page - 1}" next="/dashboard/admin/ausweise-pruefen/{page + 1}"></Pagination>
<div class="fixed bottom-8 right-8 flex flex-col gap-4">
<NotificationProvider component={AusweisPruefenNotification}></NotificationProvider>
</div>

View File

@@ -161,7 +161,7 @@
<style>
:global(.tab-list) {
@apply menu flex flex-col gap-2 px-0 bg-base-200 rounded-lg border border-base-300;
@apply flex flex-col gap-2 px-0 bg-base-200 rounded-lg border;
}
@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,400&display=swap");
@@ -171,15 +171,15 @@
}
:global(.tab.selected) {
@apply bg-base-300;
@apply bg-gray-200;
}
:global(.tab) {
@apply btn btn-primary btn-ghost rounded-none px-8 justify-start outline-0 gap-4 items-center text-base font-normal text-base-content no-animation;
@apply rounded-none px-8 justify-start outline-0 gap-4 items-center text-base font-normal text-base-content;
}
:global(.tab:hover) {
@apply bg-base-300 outline-0;
@apply bg-gray-200 outline-0;
}
:global(.tab:focus) {

View File

@@ -13,7 +13,7 @@
</p>
<h1 class="text-4xl font-medium my-8">Gebäude</h1>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<div class="columns columns-1 md:columns-2 lg:columns-3 gap-4">
{#each objekte as objekt}
<DashboardObjekt {objekt}></DashboardObjekt>
{/each}

View File

@@ -1,33 +0,0 @@
<script lang="ts">
import { BenutzerClient, ObjektKomplettClient } from "#components/Ausweis/types.js";
import Carousel from "#components/Carousel.svelte";
import DashboardAusweis from "#components/Dashboard/DashboardAusweis.svelte";
import { ChevronLeft, ChevronRight } from "radix-svelte-icons";
export let user: BenutzerClient;
export let objekt: ObjektKomplettClient;
</script>
<h1 class="text-4xl font-medium mb-8">{objekt.adresse}</h1>
<div class="bg-white rounded-lg">
<Carousel perPage={1}>
{#each objekt.bilder as bild, i (i)}
<img src="/bilder/{bild.uid}.webp" alt={bild.kategorie} class="max-h-[60vh] h-full w-full object-contain">
{/each}
<span slot="left-control" class="p-2.5 bg-opacity-50 bg-white block rounded-full"><ChevronLeft size={24}></ChevronLeft></span>
<span slot="right-control" class="p-2.5 bg-opacity-50 bg-white block rounded-full"><ChevronRight size={24}></ChevronRight></span>
</Carousel>
</div>
<div class="my-8 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
{#each objekt.aufnahmen as aufnahme}
{@const ausweis = aufnahme.verbrauchsausweis_wohnen ?? aufnahme.verbrauchsausweis_gewerbe ?? aufnahme.bedarfsausweis_wohnen}
{#if !ausweis}
<p>Diese Aufnahme hat noch keinen Ausweis.</p>
{:else}
<DashboardAusweis {ausweis} {aufnahme} {objekt} progress={0}></DashboardAusweis>
{/if}
{/each}
</div>

View File

@@ -56,7 +56,7 @@
},
];
export let selectedPaymentType: Bezahlmethoden =
export let aktiveBezahlmethode: Bezahlmethoden =
Enums.Bezahlmethoden.paypal;
async function createPayment(e: SubmitEvent) {
@@ -67,13 +67,13 @@
...rechnung,
ausweisart: Enums.Ausweisart.VerbrauchsausweisWohnen,
ausweis_uid: ausweis.uid,
bezahlmethode: selectedPaymentType,
bezahlmethode: aktiveBezahlmethode,
services: services
.filter((service) => service.selected)
.map((service) => service.id),
});
if (selectedPaymentType === Enums.Bezahlmethoden.rechnung) {
if (aktiveBezahlmethode === Enums.Bezahlmethoden.rechnung) {
window.location.href = `/payment/success?r=${response.uid}&a=${ausweis.uid}`
} else {
window.location.href = response.checkout_url as string;
@@ -315,32 +315,32 @@
class="rounded-lg border p-4 border-base-300 bg-base-100 flex flex-row gap-4 justify-between"
>
<PaymentOption
paymentType={Enums.Bezahlmethoden.paypal}
bind:selectedPaymentType
bezahlmethode={Enums.Bezahlmethoden.paypal}
bind:aktiveBezahlmethode
name={"PayPal"}
icon={"/images/paypal.png"}
></PaymentOption>
<PaymentOption
paymentType={Enums.Bezahlmethoden.sofort}
bind:selectedPaymentType
bezahlmethode={Enums.Bezahlmethoden.sofort}
bind:aktiveBezahlmethode
name={"Sofort"}
icon={"/images/sofort.png"}
></PaymentOption>
<PaymentOption
paymentType={Enums.Bezahlmethoden.giropay}
bind:selectedPaymentType
bezahlmethode={Enums.Bezahlmethoden.giropay}
bind:aktiveBezahlmethode
name={"Giropay"}
icon={"/images/giropay.png"}
></PaymentOption>
<PaymentOption
paymentType={Enums.Bezahlmethoden.creditcard}
bind:selectedPaymentType
bezahlmethode={Enums.Bezahlmethoden.creditcard}
bind:aktiveBezahlmethode
name={"Kreditkarte"}
icon={"/images/mastercard.png"}
></PaymentOption>
<PaymentOption
paymentType={Enums.Bezahlmethoden.rechnung}
bind:selectedPaymentType
bezahlmethode={Enums.Bezahlmethoden.rechnung}
bind:aktiveBezahlmethode
name={"Rechnung"}
icon={"/images/rechnung.png"}
></PaymentOption>

View File

@@ -13,17 +13,17 @@ import { getAnsichtsausweis, getDatenblatt } from "#lib/server/ausweis.js";
export const GET = defineApiRoute({
input: z.object({
uid: z.string(),
uid_ausweis: z.string(),
}),
output: z.void(),
middleware: adminMiddleware,
async fetch({ uid }, context, user) {
async fetch({ uid_ausweis }, context, user) {
const ausweisart = getAusweisartFromUUID(uid);
if (ausweisart === "VerbrauchsausweisWohnen") {
const ausweis = await prisma.verbrauchsausweisWohnen.findUnique({
where: {
uid,
uid: uid_ausweis,
},
include: {
aufnahme: {
@@ -48,7 +48,9 @@ export const GET = defineApiRoute({
const rechnung = await prisma.rechnung.findFirst({
where: {
aufnahme_id: ausweis.aufnahme.id,
verbrauchsausweis_wohnen: {
uid: uid_ausweis
},
},
orderBy: {
erstellt_am: "desc",

View File

@@ -0,0 +1,103 @@
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 "@ibcornelsen/database/server";
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { z } from "zod";
export const PUT = defineApiRoute({
input: z.object({
uid_ausweis: UUidWithPrefix
}),
middleware: adminMiddleware,
async fetch(input, context, transfer) {
const adapter = getPrismaAusweisAdapter(input.uid_ausweis) as Prisma.VerbrauchsausweisWohnenDelegate;
if (!adapter) {
throw new APIError({
code: "BAD_REQUEST",
message: "Ungültige 'uid_ausweis'"
})
}
const ausweis = await adapter.findUnique({
where: {
uid: input.uid_ausweis
}
})
if (!ausweis) {
throw new APIError({
code: "NOT_FOUND",
message: "Ausweis konnte nicht gefunden werden."
})
}
const response = await adapter.findUnique({
where: {
uid: input.uid_ausweis
},
select: {
rechnung: true
}
})
if (!response || !response.rechnung) {
await adapter.update({
where: {
uid: input.uid_ausweis
},
data: {
storniert: true
}
})
throw new APIError({
code: "NOT_FOUND",
message: "Rechnung konnte nicht gefunden werden aber Ausweis wurde storniert."
})
}
const rechnung = response.rechnung;
await adapter.update({
where: {
uid: input.uid_ausweis
},
data: {
storniert: true,
rechnung: {
update: {
status: "canceled",
storniert_am: new Date()
}
}
},
select: {
rechnung: {
select: {
betrag: true,
bezahlmethode: true,
status: true
}
}
}
})
if (rechnung.betrag > 0 && rechnung.bezahlmethode !== "rechnung" && rechnung.status === "paid" && rechnung.transaktions_referenz) {
const refund = await mollieClient.paymentRefunds.create({
description: "Rückerstattung IBCornelsen",
paymentId: rechnung.transaktions_referenz,
amount: {
currency: "EUR",
value: rechnung.betrag.toFixed(2)
},
metadata: {
rechnung_uid: rechnung.uid
},
testmode: true
})
}
},
})

View File

@@ -0,0 +1,46 @@
import { AufnahmeClient, ObjektClient, UploadedGebaeudeBild, UUidWithPrefix, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js";
import { filterAusweise } from "#lib/filters.js";
import { omit } from "#lib/helpers.js";
import { authorizationHeaders, authorizationMiddleware } from "#lib/middleware/authorization.js";
import { Enums, prisma, VerbrauchsausweisWohnenSchema } from "@ibcornelsen/database/server";
import { defineApiRoute } from "astro-typesafe-api/server";
import { z } from "zod";
export const GET = defineApiRoute({
middleware: authorizationMiddleware,
headers: authorizationHeaders,
input: z.object({
filters: filterAusweise.optional(),
limit: z.number().optional(),
skip: z.number().optional()
}),
async fetch(input, context, user) {
const ausweise = await prisma.verbrauchsausweisWohnen.findMany({
where: {
...input.filters,
benutzer: {
uid: user.uid
},
},
include: {
aufnahme: {
include: {
objekt: true,
bilder: true,
events: true
}
}
},
skip: input.skip || 0,
take: Math.min(input.limit || 50, 50)
})
return ausweise.map(ausweis => ({
ausweis: omit(ausweis, ["aufnahme"]) as VerbrauchsausweisWohnenClient,
aufnahme: omit(omit(ausweis.aufnahme, ["events"]), ["objekt"]) as AufnahmeClient,
objekt: omit(ausweis.aufnahme.objekt, ["bilder"]) as ObjektClient,
bilder: ausweis.aufnahme.bilder as unknown as UploadedGebaeudeBild[],
events: ausweis.aufnahme.events
}))
},
})

View File

@@ -86,6 +86,7 @@ export const PUT = defineApiRoute({
});
}
// TODO
// Wir erstellen eine neue Rechnung in unserer Datenbank.
const rechnung = await prisma.rechnung.create({
data: {
@@ -93,7 +94,11 @@ export const PUT = defineApiRoute({
betrag,
bezahlmethode: bezahlmethode,
status: Enums.Rechnungsstatus.open,
aufnahme_id: ausweis.aufnahme_id,
verbrauchsausweis_wohnen: {
connect: {
uid: ausweis_uid
}
},
services,
ausweistyp
},

View File

@@ -9,6 +9,9 @@ import { createCaller } from "src/astro-typesafe-api-caller";
const caller = createCaller(Astro)
const params = Astro.params;
const page = Number(params.page)
let user: BenutzerClient;
try {
@@ -34,14 +37,21 @@ const ausweise = await prisma.verbrauchsausweisWohnen.findMany({
include: {
aufnahme: {
include: {
objekt: {
include: {
bilder: true,
}
},
objekt: true,
bilder: true,
events: true
}
}
},
skip: (page - 1) * 10,
take: 10
})
const totalPages = await prisma.verbrauchsausweisWohnen.count({
where: {
benutzer: {
uid: user.uid
}
}
})
@@ -55,5 +65,5 @@ const reformedAusweise = ausweise.map(ausweis => ({
---
<UserLayout title="Ausweise Prüfen" {user}>
<DashboardAusweisePruefenModule ausweise={reformedAusweise} client:load></DashboardAusweisePruefenModule>
<DashboardAusweisePruefenModule totalPages={Math.ceil(totalPages / 10)} page={page} ausweise={reformedAusweise} client:load></DashboardAusweisePruefenModule>
</UserLayout>

View File

@@ -0,0 +1,3 @@
---
return Astro.redirect("/dashboard/admin/ausweise-pruefen/1")
---

View File

@@ -2,11 +2,11 @@
import { createCaller } from "../../../astro-typesafe-api-caller.js";
import { validateAccessTokenServer } from "#server/lib/validateAccessToken";
import DashboardModule from "#modules/Dashboard/DashboardModule.svelte";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
import Layout from "#layouts/Layout.astro";
import { prisma } from "@ibcornelsen/database/server";
import DashboardObjektModule from "#modules/Dashboard/DashboardObjektModule.svelte";
import UserLayout from "#layouts/DashboardLayout.astro";
import DashboardAufnahmeModule from "#modules/Dashboard/DashboardAufnahmeModule.svelte";
const { uid } = Astro.params;
@@ -28,7 +28,7 @@ if (!user) {
return Astro.redirect("/auth/login")
}
const objekt = await prisma.objekt.findUnique({
const aufnahme = await prisma.aufnahme.findUnique({
where: {
benutzer: {
uid: user.uid
@@ -36,19 +36,24 @@ const objekt = await prisma.objekt.findUnique({
uid
},
include: {
objekt: true,
bilder: true,
unterlagen: true,
aufnahmen: {
include: {
bedarfsausweis_wohnen: true,
verbrauchsausweis_gewerbe: true,
verbrauchsausweis_wohnen: true
}
}
bedarfsausweise_wohnen: true,
verbrauchsausweise_gewerbe: true,
verbrauchsausweise_wohnen: true,
bedarfsausweise_gewerbe: true,
geg_nachweise_gewerbe: true,
geg_nachweise_wohnen: true,
events: true
}
})
if (!aufnahme) {
return Astro.redirect("/dashboard")
}
---
<UserLayout title="Dashboard" {user}>
<DashboardObjektModule {user} {objekt} client:only/>
<DashboardAufnahmeModule {user} {aufnahme} objekt={aufnahme.objekt} client:only/>
</UserLayout>

View File

@@ -1,20 +1,17 @@
---
import UserLayout from "#layouts/DashboardLayout.astro";
import { createCaller } from "#lib/caller";
import { getCurrentUser } from "#lib/server/user";
import DashboardEinstellungenModule from "#modules/Dashboard/DashboardEinstellungenModule.svelte";
import { validateAccessTokenServer } from "#server/lib/validateAccessToken";
const valid = await validateAccessTokenServer(Astro);
const user = getCurrentUser(Astro)
if (!valid) {
if (!user) {
return Astro.redirect("/auth/login", 302);
}
const caller = createCaller(Astro);
const benutzer = await caller.v1.benutzer.self();
---
<UserLayout title="Einstellungen">
<DashboardEinstellungenModule benutzer={benutzer} client:load />
<UserLayout title="Einstellungen" {user}>
<DashboardEinstellungenModule benutzer={user} client:load />
</UserLayout>

View File

@@ -36,9 +36,9 @@ const objekte = await prisma.objekt.findMany({
include: {
bilder: true,
unterlagen: true,
bedarfsausweis_wohnen: true,
verbrauchsausweis_gewerbe: true,
verbrauchsausweis_wohnen: true
bedarfsausweise_wohnen: true,
verbrauchsausweise_gewerbe: true,
verbrauchsausweise_wohnen: true
}
}
}

View File

@@ -1,6 +1,5 @@
---
import Layout from "#layouts/Layout.astro";
import { getPrismaAusweisAdapter } from "#lib/server/ausweis";
import { getCurrentUser } from "#lib/server/user";
import { prisma } from "@ibcornelsen/database/server";

View File

@@ -26,6 +26,6 @@ if (!ausweis || !user) {
---
<AusweisLayout title="Kundendaten Aufnehmen - IBCornelsen">
<KaufabschlussModule user={user} ausweis={ausweis} selectedPaymentType={Enums.Bezahlmethoden.paypal} client:load></KaufabschlussModule>
<KaufabschlussModule user={user} ausweis={ausweis} aktiveBezahlmethode={Enums.Bezahlmethoden.paypal} client:load></KaufabschlussModule>
</AusweisLayout>