Merge branch 'development' into f052_admin_area

This commit is contained in:
2025-08-05 08:34:16 +02:00
9 changed files with 37 additions and 243 deletions

View File

@@ -42,18 +42,6 @@
<p class="mt-1 text-gray-600">Fügen Sie einem Tatort Bilder hinzu.</p>
</div>
{/if}
<div class="group relative rounded-lg p-6 text-sm leading-6 hover:bg-gray-50 w-1/4">
<div
class="flex h-11 w-11 items-center justify-center rounded-lg bg-gray-50 group-hover:bg-white"
>
<FileRect class=" group-hover:text-indigo-600" {outline} />
</div>
<a href="/view" class="mt-6 block font-semibold text-gray-900">
Ansicht
<span class="absolute inset-0"></span>
</a>
<p class="mt-1 text-gray-600">Schau Dir einen Tatort in der 3D Ansicht an.</p>
</div>
</div>
</div>

View File

@@ -2,7 +2,7 @@ import { getVorgaenge } from '$lib/server/vorgangService';
import type { PageServerLoad } from '../../(token-based)/view/$types';
export const load: PageServerLoad = async () => {
const vorgangList = await getVorgaenge();
const vorgangList = getVorgaenge();
return {
vorgangList

View File

@@ -1,37 +0,0 @@
import { client } from '$lib/minio';
import { fail } from '@sveltejs/kit';
import vorgangNumberOccupied from '$lib/helper/vorgangNumberOccupied.js';
/** @type {import('./$types').Actions} */
export const actions = {
default: async ({ request }: {request: Request}) => {
const data = await request.formData();
const vorgangNumber = data.get('vorgangNumber');
const description = data.get('description');
if (!vorgangNumber) {
return fail(400, {
vorgangNumber,
description,
error: { vorgangNumber: 'Es muss eine Vorgangsnummer vorhanden sein.' }
});
}
if (await vorgangNumberOccupied(`${vorgangNumber}`)) {
return fail(400, {
vorgangNumber,
description,
error: { vorgangNumber: 'Die Vorgangsnummer wurde im System bereits angelegt.' }
});
}
const config = `${JSON.stringify({ vorgangNumber, description, version: 1 })}\n`;
await client.putObject('tatort', `${vorgangNumber}/config.json`, config, undefined, {
'Content-Type': 'application/json'
});
return { success: true };
}
};

View File

@@ -1,115 +0,0 @@
<script lang="ts">
import Alert from '$lib/components/Alert.svelte';
import Button from '$lib/components/Button.svelte';
import Modal from '$lib/components/Modal/Modal.svelte';
import ModalTitle from '$lib/components/Modal/ModalTitle.svelte';
import ModalContent from '$lib/components/Modal/ModalContent.svelte';
import ModalFooter from '$lib/components/Modal/ModalFooter.svelte';
import Exclamation from '$lib/icons/Exclamation.svelte';
export let form;
let open = false;
$: open = form?.success ?? false;
</script>
<div class="mx-auto max-w-2xl">
<div class="flex flex-col items-center justify-center w-full">
<h1 class="text-xl">Neuer Vorgang</h1>
</div>
<form method="POST">
<div class="space-y-12">
<div class="border-b border-gray-900/10 pb-12">
<p class="mt-8 text-sm leading-6 text-gray-600">
This information will be displayed publicly so be careful what you share.
</p>
<div class="mt-10 grid grid-cols-1 gap-x-6 gap-y-8">
<div>
<label for="vorgangNumber" class="block text-sm font-medium leading-6 text-gray-900"
><span class="flex"
>{#if form?.error?.vorgangNumber}
<span class="inline-block mr-1"><Exclamation /></span>
{/if} Vorgangs-Nr.</span
></label
>
<div class="mt-2">
<div
class="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600"
>
<input
value={form?.vorgangNumber ?? ''}
type="text"
name="vorgangNumber"
id="vorgangNumber"
class="block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 text-sm leading-6"
/>
</div>
</div>
{#if form?.error?.vorgangNumber}
<p class="block text-sm leading-6 text-red-900 mt-2">{form.error.vorgangNumber}</p>
{/if}
</div>
<div>
<label for="description" class="block text-sm font-medium leading-6 text-gray-900"
><span class="flex"
>{#if form?.description}
<span class="inline-block mr-1"><Exclamation /></span>
{/if} Beschreibung</span
></label
>
<div class="mt-2">
<textarea
value={form?.description?.toString() ?? ''}
id="description"
name="description"
rows="3"
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
></textarea>
</div>
{#if form?.error}
<p class="block text-sm leading-6 text-red-900 mt-2">{form.description}</p>
{/if}
</div>
<label for="vorgang-token">
<span >Zugangstoken (optional) </span>
</label>
<div class="mt-2">
<div
>
<input
type="text"
id="vorgang-token"
/>
</div>
</div>
</div>
</div>
<div class="mt-6 flex items-center justify-end gap-x-6">
<button type="button" class="text-sm font-semibold leading-6 text-gray-900">Cancel</button>
<Button
type="submit"
class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>Save</Button
>
</div>
</div>
</form>
<Modal {open}
><ModalTitle>vorgang anlegen</ModalTitle><ModalContent>
{#if form?.success}
<Alert class="w-full">Vorgang erfolgreich angelegt</Alert>
{:else}
<Alert class="w-full" type="error">Fehler beim Upload</Alert>
{/if}
</ModalContent>
<ModalFooter><Button on:click={() => (open = false)}>Ok</Button></ModalFooter>
</Modal>
</div>

View File

@@ -17,11 +17,11 @@ const isRequiredFieldValid = (value: unknown) => {
export const actions = {
url: async ({ request }: { request: Request }) => {
const data = await request.formData();
const vorgangName = data.get('vorgang');
const crimeName = data.get('name');
const type = data.get('type');
const vorgangPIN = data.get('vorgangPIN');
const fileName = data.get('fileName');
const vorgangName: string | null = data.get('vorgang') as string;
const crimeName: string | null = data.get('name') as string;
const type: string | null = data.get('type') as string;
const vorgangPIN: string | null = data.get('vorgangPIN') as string;
const fileName: string | null = data.get('fileName') as string;
const vorgangExists = vorgangNameExists(vorgangName);
let vorgangToken;

View File

@@ -59,8 +59,8 @@
return;
}
async function handle_input(ev: KeyboardEvent, i: number) {
let item = crimesList[i];
async function handleEditFieldInput(ev: KeyboardEvent, listItemIndex: number) {
let item = crimesList[listItemIndex];
if (ev.key == 'Escape') {
let text_field_id = `label__${item.name}`;
@@ -93,7 +93,7 @@
ev.preventDefault();
// construct PUT URL
const url = $page.url;
const url = $page.url.pathname.split('?')[0];
let data_obj: { new_name: string; old_name: string } = { new_name: '', old_name: '' };
data_obj['new_name'] = new_name;
@@ -129,13 +129,22 @@
}
}
async function setClipboard(text) {
const type = "text/plain";
const clipboardItemData = {
[type]: text,
};
const clipboardItem = new ClipboardItem(clipboardItemData);
await navigator.clipboard.write([clipboardItem]);
function constructMailToLink() {
const subject = "Link zum Tatvorgang";
const link = $page.url.toString().split('?')[0];
const body = `Hallo,
hier ist der Link zum Tatvorgang:
${link}
Der Zugangs-PIN wird zur Sicherheit über einen zweiten Kommunikationskanal übermittelt.
Mit freundlichen Grüßen,
`;
const mailtoLink = `mailto:?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
return mailtoLink;
}
</script>
@@ -144,12 +153,12 @@
<h1 class="text-xl">Vorgang {vorgang.name}</h1>
{#if data?.user?.admin}
Zugangs-PIN: {vorgang.pin}
<Button on:click={() => setClipboard($page.url.toString().split('?')[0])}>Copy Link</Button>
<a href="{constructMailToLink()}"><Button>Share Link</Button></a>
{/if}
</div>
<div class="mx-auto flex justify-center max-w-7xl h-full">
<ul class="divide-y divide-gray-100">
{#each crimesList as item, i}
{#each crimesList as item, crimeListItemIndex}
<li>
<a
href="/view/{$page.params.vorgang}/{item.name}?pin={vorgangPIN}"
@@ -168,12 +177,12 @@
tabindex="0"
aria-label="Dateiname bearbeiten"
on:focusout={() => {
defocus_element(i);
defocus_element(crimeListItemIndex);
}}
on:keydown|stopPropagation={// event needed to identify ID
// TO-DO: check if event is needed or if index is sufficient
async (ev) => {
handle_input(ev, i);
handleEditFieldInput(ev, crimeListItemIndex);
}}>{item.name}</span
>

View File

@@ -2,21 +2,20 @@ import { client } from '$lib/minio';
import { json } from '@sveltejs/kit';
// rename operation
// rename operation for crimes
export async function PUT({ request }: {request: Request}) {
const data = await request.json();
// Vorgang
const vorgang = request.url.split('/').at(-1);
const vorgangToken = request.url.split('/').at(-1);
// prepare copy, incl. check if new name exists already
const old_name = data["old_name"];
const src_full_path = `/tatort/${vorgang}/${old_name}`;
const new_name = `${vorgang}/${data["new_name"]}`;
const crimeOldName = data["old_name"];
const crimeS3FullBucketPathOld = `/tatort/${vorgangToken}/${crimeOldName}`;
const crimeNewName = `${vorgangToken}/${data["new_name"]}`;
try {
await client.statObject('tatort', new_name);
await client.statObject('tatort', crimeNewName);
return json({ msg: 'Die Datei existiert bereits.' }, { status: 400 });
} catch (error) {
// continue operation
@@ -24,10 +23,10 @@ export async function PUT({ request }: {request: Request}) {
}
// actual copy operation
await client.copyObject('tatort', new_name, src_full_path)
await client.copyObject('tatort', crimeNewName, crimeS3FullBucketPathOld)
// delete
await client.removeObject('tatort', `${vorgang}/${old_name}`)
await client.removeObject('tatort', `${vorgangToken}/${crimeOldName}`)
// return success or failure

View File

@@ -1,11 +0,0 @@
import { redirect } from '@sveltejs/kit';
export const actions = {
default: async ({request}: {request: Request}) => {
const data = await request.formData();
const vorgangId = data.get('vorgang-id');
const vorgangToken = data.get('vorgang-token');
if( vorgangId && vorgangToken) throw redirect(303, `/list/${vorgangId}?token=${vorgangToken}`);
}
}

View File

@@ -1,39 +0,0 @@
<script lang="ts">
import BaseInputField from '$lib/components/BaseInputField.svelte';
import Button from '$lib/components/Button.svelte';
import ArrowRight from '$lib/icons/Arrow-right.svelte';
export let form;
</script>
<div class="mx-auto max-w-2xl">
<div class="flex flex-col items-center justify-center w-full">
<h1 class="text-xl">Vorgang ansehen</h1>
</div>
<p class="mt-8 mb-8 text-sm leading-6 text-gray-600">
Anhand der Vorgangsnummer werden Sie zu den Dateien des Vorgangs weitergeleitet und können sich
den Vorgang dann ansehen.
</p>
<form method="POST">
<BaseInputField
id="vorgang-id"
name="vorgang-id"
label="Vorgangskennung"
type="text"
value={form?.vorgangId}
/>
<div class="mt-5">
<BaseInputField
id="vorgang-token"
name="vorgang-token"
label="Zugangstoken"
type="text"
value={form?.vorgangToken}
error={form?.error?.message}
/>
</div>
<div class="flex justify-end pt-4">
<Button type="submit"><ArrowRight /></Button>
</div>
</form>
</div>