Dashboard
This commit is contained in:
@@ -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"),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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"> & {
|
||||
|
||||
@@ -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 />
|
||||
68
src/components/Dashboard/AusweisePruefenFilter.svelte
Normal file
68
src/components/Dashboard/AusweisePruefenFilter.svelte
Normal 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>
|
||||
@@ -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}"
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
33
src/components/Pagination.svelte
Normal file
33
src/components/Pagination.svelte
Normal 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>
|
||||
@@ -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
11
src/lib/filters.ts
Normal 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()
|
||||
})
|
||||
@@ -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"]))
|
||||
}))
|
||||
}
|
||||
}
|
||||
44
src/modules/Dashboard/DashboardAufnahmeModule.svelte
Normal file
44
src/modules/Dashboard/DashboardAufnahmeModule.svelte
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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) {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
103
src/pages/api/admin/stornieren.ts
Normal file
103
src/pages/api/admin/stornieren.ts
Normal 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
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
46
src/pages/api/ausweise/index.ts
Normal file
46
src/pages/api/ausweise/index.ts
Normal 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
|
||||
}))
|
||||
},
|
||||
})
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
3
src/pages/dashboard/admin/ausweise-pruefen/index.astro
Normal file
3
src/pages/dashboard/admin/ausweise-pruefen/index.astro
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
return Astro.redirect("/dashboard/admin/ausweise-pruefen/1")
|
||||
---
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user