Bugfixes & Features

1. Wenn pdf hochgeladen wird und abgespeichert wird erscheint es nicht im Formular aber im Dashboard.

2. Loading Spinner beim Hochladen von Dateien damit man sieht das was passiert.

3. Bei Bedarfsausweis Gewerbe anfragen sind in der Liste bei Gebäudetyp nur Wohngebäude!!
This commit is contained in:
Moritz Utcke
2025-10-13 11:33:45 -04:00
parent d4793af2a4
commit 4381578205
8 changed files with 117 additions and 63 deletions

View File

@@ -1,14 +1,6 @@
import { api } from "astro-typesafe-api/client"; import { api } from "astro-typesafe-api/client";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js"; import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
import {
AufnahmeClient,
BedarfsausweisWohnenClient,
BildClient,
ObjektClient,
VerbrauchsausweisGewerbeClient,
VerbrauchsausweisWohnenClient,
} from "#components/Ausweis/types.js";
import { import {
Aufnahme, Aufnahme,
BedarfsausweisWohnen, BedarfsausweisWohnen,

View File

@@ -75,7 +75,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
> >
<option disabled selected value={null}>Bitte auswählen</option> <option disabled selected value={null}>Bitte auswählen</option>
{#if ausweisart==Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisWohnen || ausweisart === Enums.Ausweisart.BedarfsausweisWohnen || ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe} {#if ausweisart==Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisWohnen || ausweisart === Enums.Ausweisart.BedarfsausweisWohnen}
<option value="Einfamilienhaus">Einfamilienhaus</option> <option value="Einfamilienhaus">Einfamilienhaus</option>
<option value="Freistehendes Einfamilienhaus">Freistehendes Einfamilienhaus</option> <option value="Freistehendes Einfamilienhaus">Freistehendes Einfamilienhaus</option>
<option value="Freistehendes Zweifamilienhaus">Freistehendes Zweifamilienhaus</option> <option value="Freistehendes Zweifamilienhaus">Freistehendes Zweifamilienhaus</option>
@@ -87,7 +87,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
<option value="Atrium-Bungalow">Atrium-Bungalow</option> <option value="Atrium-Bungalow">Atrium-Bungalow</option>
<option value="Winkelbungalow">Winkelbungalow</option> <option value="Winkelbungalow">Winkelbungalow</option>
{:else if ausweisart==Enums.Ausweisart.VerbrauchsausweisGewerbe || ausweisart=== Enums.Ausweisart.GEGNachweisGewerbe} {:else if ausweisart==Enums.Ausweisart.VerbrauchsausweisGewerbe || ausweisart=== Enums.Ausweisart.GEGNachweisGewerbe || ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe}
<option value="Verwaltungsgebäude (allgemein)">Verwaltungsgebäude (allgemein)</option> <option value="Verwaltungsgebäude (allgemein)">Verwaltungsgebäude (allgemein)</option>
<option value="Parlaments- und Gerichtsgebäude">Parlaments- und Gerichtsgebäude</option> <option value="Parlaments- und Gerichtsgebäude">Parlaments- und Gerichtsgebäude</option>
<option value="Ministerien u. Ämter u. Behörden">Ministerien u. Ämter u. Behörden</option> <option value="Ministerien u. Ämter u. Behörden">Ministerien u. Ämter u. Behörden</option>

View File

@@ -1,10 +1,10 @@
<script lang="ts"> <script lang="ts">
import { BedarfsausweisWohnenClient, GEGNachweisWohnenClient, ObjektClient, UnterlageClient, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "./Ausweis/types.js"; import { BedarfsausweisWohnenClient, ObjektClient, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "./Ausweis/types.js";
import { Trash, Upload } from "radix-svelte-icons"; import { Trash, Upload } from "radix-svelte-icons";
import HelpLabel from "#components/labels/HelpLabel.svelte"; import HelpLabel from "#components/labels/HelpLabel.svelte";
export let kategorie: string = ""; export let kategorie: string = "";
export let files: Unterlage[] = []; export let files: (Unterlage & { preview?: boolean })[] = [];
export let max: number = Infinity; export let max: number = Infinity;
export let min: number = 1; export let min: number = 1;
export let name: string = ""; export let name: string = "";
@@ -26,6 +26,8 @@
for (let i = 0; i < fileArray.length; i++) { for (let i = 0; i < fileArray.length; i++) {
const file = fileArray[i]; const file = fileArray[i];
files.push({ preview: true, kategorie, name: file.name, mime: file.type, aufnahme_id: null, id: "" });
files = files;
if (i == max) { if (i == max) {
break; break;
@@ -62,7 +64,13 @@
name: file.name name: file.name
}) })
files.push({ id, kategorie, name: file.name, mime: mimeType, aufnahme_id: null }); const placeholder = files.find((f) => f.name === fileArray[i].name && f.preview === true && f.kategorie === kategorie);
if (!placeholder) {
return;
}
placeholder!.preview = false;
placeholder!.id = id;
placeholder!.aufnahme_id = ausweis.id;
files = files; files = files;
@@ -110,23 +118,37 @@
<div class="grid grid-cols-2 gap-2"> <div class="grid grid-cols-2 gap-2">
{#each files as file, i} {#each files as file, i}
{#if file.kategorie == kategorie} {#if file.kategorie == kategorie}
<div class="relative group"> {#if file.preview === true}
<div <!-- Show loading spinner -->
class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all text-center items-center flex p-4 text-opacity-75 text-black" <div class="relative group">
>{file.name}</div> <div
<div class="invisible group-hover:visible absolute left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] flex flex-row gap-2"> class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all text-center items-center justify-center flex p-4 text-opacity-75 text-black"
<button >
type="button" <div class="flex flex-row gap-4">
class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]" <span>{file.name}</span>
on:click={() => { <span class="loader"></span>
delete files[i]; </div>
files = files.filter((x) => x); </div>
}}
>
<Trash size={20} color="#fff"></Trash>
</button>
</div> </div>
</div> {:else}
<div class="relative group">
<div
class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all text-center items-center flex p-4 text-opacity-75 text-black"
>{file.name}</div>
<div class="invisible group-hover:visible absolute left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] flex flex-row gap-2">
<button
type="button"
class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]"
on:click={() => {
delete files[i];
files = files.filter((x) => x);
}}
>
<Trash size={20} color="#fff"></Trash>
</button>
</div>
</div>
{/if}
{/if} {/if}
{/each} {/each}

View File

@@ -7,7 +7,7 @@
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js"; import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
export let images: BildClient[] = []; export let images: (BildClient & { preview?: boolean })[] = [];
export let max: number = Infinity; export let max: number = Infinity;
export let min: number = 1; export let min: number = 1;
export let name: string = ""; export let name: string = "";
@@ -35,40 +35,43 @@
<div class="grid grid-cols-2 gap-2"> <div class="grid grid-cols-2 gap-2">
{#each images as image, i} {#each images as image, i}
{#if image.kategorie == kategorie} {#if image.kategorie == kategorie}
<div class="relative group"> {#if image.preview === true}
<img <!-- Show loading spinner -->
src="/bilder/{image.id}.jpg" <div class="relative group">
alt={kategorie} <div
class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all" class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all text-center items-center justify-center flex p-4 text-opacity-75 text-black"
/> >
<div class="invisible group-hover:visible absolute left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] flex flex-row gap-2"> <div class="flex flex-row gap-4">
<button <span class="loader"></span>
type="button" </div>
class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]" </div>
on:click={() => { </div>
deleteImage(images[i]) {:else}
}} <div class="relative group">
> <img
<Trash size={20} color="#fff"></Trash> src="/bilder/{image.id}.jpg"
</button> alt={kategorie}
<!-- <button class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all"
type="button" />
class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]" <div class="invisible group-hover:visible absolute left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] flex flex-row gap-2">
on:click={async () => { <button
let image = await rotateImage(images[i]); type="button"
images[i] = image; class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]"
images = images on:click={() => {
}} deleteImage(images[i])
> }}
<RotateCounterClockwise size={20} color="#fff"></RotateCounterClockwise> >
</button> --> <Trash size={20} color="#fff"></Trash>
</button>
</div>
</div> </div>
</div> {/if}
{/if} {/if}
{/each} {/each}
<!-- Wir zeigen Platzhalter an, damit der Nutzer sieht wie viele Bilder er hochladen soll --> <!-- Wir zeigen Platzhalter an, damit der Nutzer sieht wie viele Bilder er hochladen soll -->
{#each { length: Math.max(0, Math.min(max, 4) - images.filter(image => image.kategorie === kategorie).length) } as _, i} {#each { length: Math.max(0, Math.min(max, 4) - images.filter(image => image.kategorie === kategorie).length) } as _, i}
<div class="relative group"> <div class="relative group">
<img <img
src="/placeholder.png" src="/placeholder.png"

View File

@@ -21,7 +21,7 @@
import { addNotification } from "./Notifications/shared.js"; import { addNotification } from "./Notifications/shared.js";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js"; import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
export let images: BildClient[] = []; export let images: (BildClient & { preview?: boolean })[] = [];
export let ausweis: export let ausweis:
| VerbrauchsausweisWohnenClient | VerbrauchsausweisWohnenClient
| VerbrauchsausweisGewerbeClient | VerbrauchsausweisGewerbeClient
@@ -39,8 +39,8 @@
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {
const file = files[i]; const file = files[i];
console.log(file); images.push({ preview: true, benutzer_id: null, kategorie, created_at: new Date(), updated_at: new Date(), id: "" });
images = images;
if (file.type !== "image/jpeg" && file.type !== "image/png" && file.type !== "image/webp" && file.type !== "image/heif" && file.type !== "image/heic") { if (file.type !== "image/jpeg" && file.type !== "image/png" && file.type !== "image/webp" && file.type !== "image/heif" && file.type !== "image/heic") {
continue; continue;
@@ -119,7 +119,13 @@
dismissable: true dismissable: true
}) })
} else { } else {
images.push({ id: result.id, kategorie }); const placeholder = images.find((f) => f.kategorie === kategorie && f.preview === true);
if (!placeholder) {
return;
}
placeholder!.preview = false;
placeholder!.id = result.id;
placeholder!.benutzer_id = ausweis.benutzer_id;
images = images; images = images;
if (i == Math.min(files.length, max) - 1) { if (i == Math.min(files.length, max) - 1) {

View File

@@ -53,5 +53,8 @@ if (user.rolle !== Enums.BenutzerRolle.ADMIN) {
if (result.length > 0) { if (result.length > 0) {
return Astro.redirect(`/dashboard/objekte/${result[0].id}?p=${page}`); return Astro.redirect(`/dashboard/objekte/${result[0].id}?p=${page}`);
} else {
return Astro.redirect("/dashboard/objekte/leer");
} }
--- ---

View File

@@ -8,6 +8,7 @@ import {
Enums, Enums,
Objekt, Objekt,
prisma, prisma,
Unterlage,
VerbrauchsausweisGewerbe, VerbrauchsausweisGewerbe,
VerbrauchsausweisWohnen, VerbrauchsausweisWohnen,
} from "#lib/server/prisma"; } from "#lib/server/prisma";
@@ -16,6 +17,7 @@ import {
getBedarfsausweisWohnen, getBedarfsausweisWohnen,
getBilder, getBilder,
getObjekt, getObjekt,
getUnterlagen,
getVerbrauchsausweisGewerbe, getVerbrauchsausweisGewerbe,
getVerbrauchsausweisWohnen, getVerbrauchsausweisWohnen,
} from "#lib/server/db"; } from "#lib/server/db";
@@ -52,6 +54,7 @@ let ausweis:
let aufnahme: Aufnahme | null = {} as Aufnahme; let aufnahme: Aufnahme | null = {} as Aufnahme;
let objekt: Objekt | null = {} as Objekt; let objekt: Objekt | null = {} as Objekt;
let bilder: Bild[] = []; let bilder: Bild[] = [];
let unterlagen: Unterlage[] = [];
let loadFromDatabase = false; let loadFromDatabase = false;
if (typ === AusstellungsTyp.Neuausstellung) { if (typ === AusstellungsTyp.Neuausstellung) {
@@ -147,6 +150,7 @@ if (typ === AusstellungsTyp.Neuausstellung) {
} }
bilder = await getBilder(aufnahme.id); bilder = await getBilder(aufnahme.id);
unterlagen = await getUnterlagen(aufnahme.id);
loadFromDatabase = true; loadFromDatabase = true;
} else if (typ === AusstellungsTyp.Alternativdokument) { } else if (typ === AusstellungsTyp.Alternativdokument) {
if (!user) { if (!user) {
@@ -249,6 +253,7 @@ if (typ === AusstellungsTyp.Neuausstellung) {
{aufnahme} {aufnahme}
{bilder} {bilder}
{ausweistyp} {ausweistyp}
{unterlagen}
{ausweis_id} {ausweis_id}
{user} {user}
{loadFromDatabase} {loadFromDatabase}

View File

@@ -279,3 +279,26 @@ article {
/*SIDEBAR-RIGHT*/ /*SIDEBAR-RIGHT*/
/*FOOTER*/ /*FOOTER*/
/* LOADERS */
.loader {
width: 24px;
height: 24px;
border: 3px solid #444f94;
border-bottom-color: transparent;
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
animation: rotation 1s linear infinite;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}