f047_neu_Edit-der-Namen #28

Merged
trachi93 merged 14 commits from f047_neu_Edit-der-Namen into development 2025-08-19 09:30:13 +02:00
14 changed files with 425 additions and 330 deletions

View File

@@ -1,3 +1,31 @@
<script lang="ts">
export let href = null;
export let type: 'button' | 'submit' | 'reset' = 'button';
mina marked this conversation as resolved Outdated
Outdated
Review

type hinzufügen
export let type: 'button' | 'submit' | 'reset' = 'button';

type hinzufügen export let type: 'button' | 'submit' | 'reset' = 'button';
export let size = 'md';
export let variant = 'primary';
export let fullWidth = false;
export let align = 'center';
export let disabled = false;
let classNames = '';
export { classNames as class };
</script>
{#if href}
<a on:click {href} class:w-full={fullWidth} class="button {variant} {size} {classNames} {align}"
><slot />
</a>
{:else}
<button
on:click
{type}
{disabled}
class:w-full={fullWidth}
class="button {variant} {size} {classNames} {align}"
>
<slot />
</button>
{/if}
<style> <style>
.button { .button {
@apply inline-flex; @apply inline-flex;
@@ -172,31 +200,3 @@
@apply justify-end; @apply justify-end;
} }
</style> </style>
<script lang="ts">
export let href = null;
export let type = 'button';
export let size = 'md';
export let variant = 'primary';
export let fullWidth = false;
export let align = 'center';
export let disabled = false;
let classNames = '';
export { classNames as class };
</script>
{#if href}
<a on:click {href} class:w-full={fullWidth} class="button {variant} {size} {classNames} {align}"
><slot />
</a>
{:else}
<button
on:click
{type}
{disabled}
class:w-full={fullWidth}
class="button {variant} {size} {classNames} {align}"
>
<slot />
</button>
{/if}

View File

@@ -0,0 +1,90 @@
<script lang="ts">
import Edit from '$lib/icons/Edit.svelte';
import Trash from '$lib/icons/Trash.svelte';
import { tick } from 'svelte';
interface ListItem {
name: string;
token?: string;
// add other properties as needed
}
let {
list,
editedName = $bindable(),
currentName,
onSave = () => {},
onDelete = () => {}
} = $props();
let localName = $state(currentName);
let wasCancelled = $state(false);
let error: string = $derived(validateName(localName));
let isEditing = $state(false);
let inputRef: HTMLInputElement;
function validateName(name: string) {
const trimmed = name.trim();
if (!trimmed) {
return 'Name darf nicht leer sein.';
}
const duplicate = list.some(
(item: ListItem) => item.name === trimmed && item.name !== currentName
);
if (duplicate) return 'Name existiert bereits.';
return '';
}
function commitIfValid() {
if (!error && !wasCancelled && localName != currentName) {
const trimmedName: string = localName.trim();
inputRef?.blur();
isEditing = false;
onSave(trimmedName, currentName);
} else {
localName = currentName;
resetEdit();
}
}
function resetEdit() {
wasCancelled = false;
inputRef?.blur();
isEditing = false;
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Enter') {
event.preventDefault();
commitIfValid();
} else if (event.key === 'Escape') {
event.preventDefault();
localName = currentName;
resetEdit();
}
}
async function startEdit() {
isEditing = true;
await tick();
inputRef?.focus();
}
</script>
<div>
<input
bind:this={inputRef}
bind:value={localName}
onblur={commitIfValid}
onkeydown={handleKeydown}
/>
<button onclick={startEdit}><Edit /></button>
<button onclick={() => onDelete(currentName)}><Trash /></button>
{#if error}
<p style="color: red;">{error}</p>
{/if}
</div>

Binary file not shown.

View File

@@ -31,10 +31,16 @@ export const getCrimesListByToken = async (vorgangToken: string) => {
* @param vorgangToken * @param vorgangToken
* @returns vorgangObj with keys `token`, `name`, `pin` || undefined * @returns vorgangObj with keys `token`, `name`, `pin` || undefined
*/ */
export const getVorgangByToken = (vorgangToken: string): {token: string, name:string, pin: string} | undefined => { export const getVorgangByToken = (
const getVorgangSQLStmt = `SELECT token, name, pin FROM cases WHERE token = ?`; vorgangToken: string
): { token: string; name: string; pin: string } | undefined => {
const getVorgangSQLStmt = `SELECT token, name, pin
FROM cases
WHERE token = ?`;
const statement = db.prepare(getVorgangSQLStmt); const statement = db.prepare(getVorgangSQLStmt);
const result = statement.get(vorgangToken) as {token: string, name:string, pin: string} | undefined; const result = statement.get(vorgangToken) as
| { token: string; name: string; pin: string }
| undefined;
return result; return result;
}; };
@@ -44,10 +50,16 @@ export const getVorgangByToken = (vorgangToken: string): {token: string, name:st
* @param vorgangName * @param vorgangName
* @returns vorgangObj with keys `token`, `name`, `pin` || undefined * @returns vorgangObj with keys `token`, `name`, `pin` || undefined
*/ */
export const getVorgangByName = (vorgangName: string): {token: string, name: string, pin: string} | undefined => { export const getVorgangByName = (
const getVorgangByNameSQLStmt = `SELECT token, name, pin FROM cases WHERE name = ?`; vorgangName: string
): { token: string; name: string; pin: string } | undefined => {
const getVorgangByNameSQLStmt = `SELECT token, name, pin
FROM cases
WHERE name = ?`;
const statement = db.prepare(getVorgangByNameSQLStmt); const statement = db.prepare(getVorgangByNameSQLStmt);
const result = statement.get(vorgangName) as {token: string, name: string, pin: string} | undefined; const result = statement.get(vorgangName) as
| { token: string; name: string; pin: string }
| undefined;
return result; return result;
}; };
@@ -88,13 +100,22 @@ export const getListOfVorgänge = async () => {
* Fetches list of vorgänge from database * Fetches list of vorgänge from database
* @returns list with of available vorgaenge * @returns list with of available vorgaenge
*/ */
export const getVorgaenge = (): {vorgangToken: string, vorgangName: string, vorgangPIN: string}[] => { export const getVorgaenge = (): {
const getVorgaengeSQLStmt = `SELECT token, name, pin from cases`; vorgangToken: string;
vorgangName: string;
vorgangPIN: string;
}[] => {
const getVorgaengeSQLStmt = `SELECT token, name, pin
from cases`;
const statement = db.prepare(getVorgaengeSQLStmt); const statement = db.prepare(getVorgaengeSQLStmt);
const result = statement.all() as { token: string; name: string; pin: string }[]; const result = statement.all() as { token: string; name: string; pin: string }[];
const vorgaenge_list: {vorgangToken: string, vorgangName: string, vorgangPIN: string}[] = []; const vorgaenge_list: { vorgangToken: string; vorgangName: string; vorgangPIN: string }[] = [];
for (const resultItem of result) { for (const resultItem of result) {
const vorg = { vorgangToken: resultItem.token, vorgangName: resultItem.name, vorgangPIN: resultItem.pin }; const vorg = {
vorgangToken: resultItem.token,
vorgangName: resultItem.name,
vorgangPIN: resultItem.pin
};
vorgaenge_list.push(vorg); vorgaenge_list.push(vorg);
} }

View File

@@ -15,9 +15,6 @@
if (!target) return; if (!target) return;
let filename = target.id.split('del__')[1]; let filename = target.id.split('del__')[1];
// delete request
// --------------
let url = `/api/list/${filename}`; let url = `/api/list/${filename}`;
try { try {
@@ -46,13 +43,16 @@
<ul role="list" class="divide-y divide-gray-100"> <ul role="list" class="divide-y divide-gray-100">
{#each vorgangList as vorgangItem} {#each vorgangList as vorgangItem}
<li> <li>
<a href="/list/{vorgangItem.vorgangToken}?pin={vorgangItem.vorgangPIN}" class="flex justify-between gap-x-6 py-5"> <a
href="/list/{vorgangItem.vorgangToken}?pin={vorgangItem.vorgangPIN}"
class="flex justify-between gap-x-6 py-5"
>
<div class="flex gap-x-4"> <div class="flex gap-x-4">
<!-- Ordner -->
<Folder /> <Folder />
<div class="min-w-0 flex-auto"> <div class="min-w-0 flex-auto">
<span class="text-sm font-semibold leading-6 text-gray-900">{vorgangItem.vorgangName}</span> <span class="text-sm font-semibold leading-6 text-gray-900"
<!-- Delete button --> >{vorgangItem.vorgangName}</span
>
<button <button
style="padding: 2px" style="padding: 2px"
id="del__{vorgangItem.vorgangToken}" id="del__{vorgangItem.vorgangToken}"

View File

@@ -1,16 +0,0 @@
import { getVorgangByToken, getCrimesListByToken } from '$lib/server/vorgangService';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, url }) => {
const vorgangToken = params.vorgang;
const vorgangPIN = url.searchParams.get('pin');
const crimesList = await getCrimesListByToken(vorgangToken);
const vorgang = getVorgangByToken(vorgangToken);
return {
crimesList,
vorgangPIN,
vorgang
};
};

View File

@@ -1,7 +1,5 @@
<script lang="ts"> <script lang="ts">
Review

Wie du beschrieben hast, unnötige Kommentare und console.logs raus

Wie du beschrieben hast, unnötige Kommentare und console.logs raus
Review

Sollte soweit passen, ansonsten bitte selbstständig rauslöschen

Sollte soweit passen, ansonsten bitte selbstständig rauslöschen
import shortenFileSize from '$lib/helper/shortenFileSize'; import shortenFileSize from '$lib/helper/shortenFileSize';
import { page } from '$app/stores';
import timeElapsed from '$lib/helper/timeElapsed'; import timeElapsed from '$lib/helper/timeElapsed';
import Alert from '$lib/components/Alert.svelte'; import Alert from '$lib/components/Alert.svelte';
@@ -11,280 +9,210 @@
import ModalContent from '$lib/components/Modal/ModalContent.svelte'; import ModalContent from '$lib/components/Modal/ModalContent.svelte';
import ModalFooter from '$lib/components/Modal/ModalFooter.svelte'; import ModalFooter from '$lib/components/Modal/ModalFooter.svelte';
import Cube from '$lib/icons/Cube.svelte'; import Cube from '$lib/icons/Cube.svelte';
import Edit from '$lib/icons/Edit.svelte'; import { invalidate, invalidateAll } from '$app/navigation';
import Trash from '$lib/icons/Trash.svelte'; import NameItemEditor from '$lib/components/NameItemEditor.svelte';
/** export let data; */ //Seite für die Tatort-Liste
/** @type {import('./$types').PageData} */ let { data } = $props();
export let data;
console.log('tatorte: debug ', data);
interface ListItem { interface ListItem {
//sollte Typ Vorgang sein, aber der einfachheit ist es noch ListItem, damit die Komponente NameItemEditor für Vorgang und Tatort eingesetzt werden kann
name: string; name: string;
size: number; size: number;
lastModified: string | number | Date; lastModified: string | number | Date;
show_button?: boolean; show_button?: boolean;
prefix?: string;
// add other properties as needed // add other properties as needed
} }
const vorgang = data.vorgang; let vorgangName: string = data.vorgang.vorgangName;
const crimesList: ListItem[] = data.crimesList; let crimesList: ListItem[] = $state(data.crimesList);
const vorgangPIN: string = data.vorgangPIN; const vorgangPIN: string = data.vorgang.vorgangPIN;
let vorgangToken: string = data.vorgang.vorgangToken;
let open = false; //Variablen für Modal
$: open; let open = $state(false);
let inProgress = false; let inProgress = $state(false);
$: inProgress; let isError = $state(false);
mina marked this conversation as resolved Outdated
Outdated
Review

In Progress wird nie gesetzt. Daher bisher keine Funktion

In Progress wird nie gesetzt. Daher bisher keine Funktion
Outdated
Review

Da es zum Modal gehört habe ich es angepasst und nicht gelöscht.

Da es zum Modal gehört habe ich es angepasst und nicht gelöscht.
let err = false;
$: err;
let rename_input; //Variable um nur admin UI anzuzeigen
$: rename_input; let admin = data?.user?.admin;
function uploadSuccessful() { async function handleSave(newName: string, oldName: string) {
open = false; open = true;
} inProgress = true;
try {
const res = await fetch(`/api/list/${vorgangToken}/${oldName}`, {
method: 'PUT',
headers: {
mina marked this conversation as resolved Outdated
Outdated
Review

Funktioniert es denn jetzt???

Funktioniert es denn jetzt???
Outdated
Review

Ja, funktioniert.

Ja, funktioniert.
'Content-Type': 'application/json'
},
body: JSON.stringify({ vorgangToken, oldName, newName })
})
.then(() => {
inProgress = false;
})
.catch((err) => {
inProgress = false;
isError = true;
console.log('ERROR', err);
});
function defocus_element(i: number) { if (!res.ok) {
let item = crimesList[i]; const msg = await res.text();
let text_field_id = `label__${item.name}`; console.error('❌ Fehler beim Umbenennen:', msg);
isError = true;
let text_field = document.getElementById(text_field_id); inProgress = false;
if (text_field) {
text_field.setAttribute('contenteditable', 'false');
text_field.textContent = item.name;
}
// reshow button
crimesList[i].show_button = true;
return;
}
async function handleEditFieldInput(ev: KeyboardEvent, listItemIndex: number) {
let item = crimesList[listItemIndex];
if (ev.key == 'Escape') {
let text_field_id = `label__${item.name}`;
let text_field = document.getElementById(text_field_id);
if (text_field) {
text_field.setAttribute('contenteditable', 'false');
text_field.textContent = item.name;
}
// reshow button
item.show_button = true;
return;
}
if (ev.key == 'Enter') {
let name_field = ev.currentTarget as HTMLElement | null;
let new_name = name_field
? name_field.textContent || (name_field as any).innerText || ''
: '';
if (new_name == '') {
alert('Bitte einen gültigen Namen eingeben.');
ev.preventDefault();
return;
}
// actual upload
// -------------
// to prevent from item being selected
ev.preventDefault();
// construct PUT 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;
data_obj['old_name'] =
ev.currentTarget && (ev.currentTarget as HTMLElement).id
? (ev.currentTarget as HTMLElement).id.split('__')[1]
: '';
open = true;
inProgress = true;
const response = await fetch(url, { method: 'PUT', body: JSON.stringify(data_obj) });
inProgress = false;
if (!response.ok) {
err = true;
if (response.status == 400) {
let json_res = await response.json();
return;
}
throw new Error(`Fehlgeschlagen: ${response.status}`);
} else { } else {
uploadSuccessful(); await invalidateAll();
setTimeout(() => { crimesList = data.crimesList;
window.location.reload(); open = false;
}, 500); inProgress = false;
} }
} catch (err) {
console.error('⚠️ Netzwerkfehler:', err);
inProgress = false;
}
}
// --- upload finished --- async function handleDelete(tatort: string) {
open = true;
inProgress = true;
let url = new URL(data.url);
url.pathname += `/${tatort}`;
console.log('Delete tatort: ', `/api${url.pathname}`, url.pathname);
return; try {
const res = await fetch(`/api${url.pathname}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ vorgangToken, tatort })
})
.then(() => {
inProgress = false;
})
.catch((err) => {
isError = true;
inProgress = false;
console.log('ERROR', err);
});
if (!res.ok) {
const msg = await res.text();
console.error('❌ Fehler beim Löschen:', msg);
} else {
console.log('🗑️ Erfolgreich gelöscht:', url.pathname);
await invalidateAll();
crimesList = data.crimesList;
}
} catch (err) {
isError = true;
inProgress = false;
console.error('⚠️ Netzwerkfehler beim Löschen:', err);
} }
} }
function constructMailToLink() { function constructMailToLink() {
const subject = "Link zum Tatvorgang"; const subject = 'Link zum Tatvorgang';
const link = $page.url.toString().split('?')[0];
const link = data.url.origin + data.url.pathname;
const body = `Hallo, const body = `Hallo,
hier ist der Link zum Tatvorgang: hier ist der Link zum Tatvorgang:
${link} ${link}
Der Zugangs-PIN wird zur Sicherheit über einen zweiten Kommunikationskanal übermittelt. Der Zugangs-PIN wird zur Sicherheit über einen zweiten Kommunikationskanal übermittelt.
Mit freundlichen Grüßen, Mit freundlichen Grüßen,
`; `;
const mailtoLink = `mailto:?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`; const mailtoLink = `mailto:?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
return mailtoLink; return mailtoLink;
} }
function closeModal() {
open = false;
isError = false;
}
</script> </script>
<div class="-z-10 bg-white"> {#if data.vorgang && data.crimesList}
<div class="flex flex-col items-center justify-center w-full"> <div class="-z-10 bg-white">
<h1 class="text-xl">Vorgang {vorgang.name}</h1> <div class="flex flex-col items-center justify-center w-full">
{#if data?.user?.admin} <h1 class="text-xl">Vorgang {vorgangName}</h1>
Zugangs-PIN: {vorgang.pin}
<a href="{constructMailToLink()}"><Button>Share Link</Button></a> {#if admin}
{/if} Zugangs-PIN: {vorgangPIN}
</div> <a class="pt-2 pb-6" href={constructMailToLink()}><Button>Share Link</Button></a>
<div class="mx-auto flex justify-center max-w-7xl h-full"> {/if}
<ul class="divide-y divide-gray-100"> </div>
{#each crimesList as item, crimeListItemIndex} <div class="mx-auto flex justify-center max-w-7xl h-full">
<li> <ul class="divide-y divide-gray-100">
<a {#each data.crimesList as item, crimeListItemIndex}
href="/view/{$page.params.vorgang}/{item.name}?pin={vorgangPIN}" <li>
class=" flex justify-between gap-x-6 py-5"
aria-label="zum 3D-modell"
>
<div class=" flex gap-x-4"> <div class=" flex gap-x-4">
<Cube /> <a
href="/view/{vorgangToken}/{item.name}?pin={vorgangPIN}"
class=" flex justify-between gap-x-6 py-5"
aria-label="/view/{vorgangToken}/{item.name}?pin={vorgangPIN}"
title={item.name}
>
<Cube />
</a>
<div class="min-w-0 flex-auto"> <div class="min-w-0 flex-auto">
{#if data?.user?.admin} {#if admin}
<span <NameItemEditor
id="label__{item.name}" list={data.crimesList}
class="text-sm font-semibold leading-6 text-gray-900 inline-block min-w-1" editedName={data.crimeNames[crimeListItemIndex]}
contenteditable={!item.show_button} currentName={item.name}
role="textbox" onSave={handleSave}
tabindex="0" onDelete={handleDelete}
aria-label="Dateiname bearbeiten" ></NameItemEditor>
on:focusout={() => {
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) => {
handleEditFieldInput(ev, crimeListItemIndex);
}}>{item.name}</span
>
{#if item.show_button}
<button
style="padding: 2px"
id="edit__{item.name}"
aria-label="Datei umbenennen"
on:click|preventDefault={(ev) => {
let text_field_id = `label__${item.name}`;
let text_field = document.getElementById(text_field_id);
if (text_field) {
text_field.setAttribute('contenteditable', 'true');
text_field.focus();
text_field.textContent = '';
}
// hide button
item.show_button = false;
}}
>
<Edit />
</button>
{/if}
<button
style="padding: 2px"
id="del__{item.name}"
on:click|preventDefault={async (ev) => {
let delete_item = window.confirm('Bist du sicher?');
if (delete_item) {
// bucket: tatort, name: <vorgang>/item-name
let vorgang = $page.params.vorgang;
let filename = '';
if (ev && ev.currentTarget && (ev.currentTarget as HTMLElement).id) {
filename = (ev.currentTarget as HTMLElement).id.split('del__')[1];
}
// delete request
// --------------
let url = new URL($page.url);
url.pathname += `/${filename}`;
try {
const response = await fetch(`/api${url.pathname}`, { method: 'DELETE' });
if (response.status == 204) {
setTimeout(() => {
window.location.reload();
}, 500);
}
} catch (error) {
if (error instanceof Error) {
console.log(error.message);
} else {
console.log(error);
}
}
}
}}
aria-label="Datei löschen"
>
<Trash />
</button>
{:else} {:else}
<span class="text-sm font-semibold leading-6 text-gray-900 inline-block min-w-1" <span class="text-sm font-semibold leading-6 text-gray-900 inline-block min-w-1"
>{item.name}</span >{item.name}</span
> >
{/if} {/if}
<p class="mt-1 truncate text-xs leading-5 text-gray-500"> {#if item.size}
{shortenFileSize(item.size)} <p class="mt-1 truncate text-xs leading-5 text-gray-500">
</p> {shortenFileSize(item.size)}
</p>
{/if}
</div> </div>
</div> </div>
<div class="hidden sm:flex sm:flex-col sm:items-end"> <div class="hidden sm:flex sm:flex-col sm:items-end">
<p class="text-sm leading-6 text-gray-900">3D Tatort</p> <p class="text-sm leading-6 text-gray-900">3D Tatort</p>
<p class="mt-1 text-xs leading-5 text-gray-500"> {#if item.lastModified}
Zuletzt geändert <time datetime="2023-01-23T13:23Z" <p class="mt-1 text-xs leading-5 text-gray-500">
>{timeElapsed(new Date(item.lastModified))}</time Zuletzt geändert <time datetime="2023-01-23T13:23Z"
> >{timeElapsed(new Date(item.lastModified))}</time
</p> >
</p>
{/if}
</div> </div>
</a> </li>
</li> {/each}
{/each} </ul>
</ul> </div>
</div>
<Modal {open} <Modal {open}
><ModalTitle>Umbenennen</ModalTitle><ModalContent> ><ModalTitle>Umbenennen</ModalTitle><ModalContent>
{#if inProgress} {#if inProgress}
<p class="py-2 mb-1">Vorgang läuft...</p> <p class="py-2 mb-1">Vorgang läuft...</p>
{/if} {:else if isError}
{#if err} <Alert class="w-full" type="error">Fehler beim Umbenennen</Alert>
<Alert class="w-full" type="error">Fehler beim Umbenennen</Alert> {:else}
{/if} <Alert class="w-full">Umbenennen erfolgreich</Alert>
</ModalContent> {/if}
<ModalFooter><Button disabled={inProgress} on:click={uploadSuccessful}>Ok</Button></ModalFooter> </ModalContent>
</Modal> <ModalFooter><Button disabled={inProgress} on:click={closeModal}>Ok</Button></ModalFooter>
</div> </Modal>
</div>
{/if}
<style> <style>
ul { ul {

View File

@@ -0,0 +1,27 @@
import { redirect } from '@sveltejs/kit';
export async function load({fetch, params, url}){
const vorgangResponse = await fetch(`/api/list`);
const vorgangList = await vorgangResponse.json()
const vorgangToken = params.vorgang;
const crimesListResponse = await fetch(`/api/list/${vorgangToken}`)
const crimesList = await crimesListResponse.json();
const vorgang = vorgangList.find(v => v.vorgangToken === vorgangToken); //vorgang sollte ein eigener Typ werden, und dann kann man es hier vernünftig typisieren
if(!vorgang || !crimesList){
throw new Error(`Fehlgeschlagen, es wurden keine Daten zum token gefunden`);
}
//Variabeln für NameItemEditor
const crimeNames: string[] = crimesList.map((l) => l.name);
if (crimesList.length === 0) {
throw redirect(302, '/upload'); // weiterleiten auf die hinzufügen seite
Review

Ok, aber warum. Erwarte ich hier nicht dann die Liste??

Ok, aber warum. Erwarte ich hier nicht dann die Liste??
Review

Ich habe die Umleitung gesetzt, wenn die Liste leer ist, also wenn keine Dateien zum Anzeigen vorhanden sind. So kann man quasi direkt eine Datei neu upload. Wenn nicht gewünscht bitte eigenständig bearbeiten

Ich habe die Umleitung gesetzt, wenn die Liste leer ist, also wenn keine Dateien zum Anzeigen vorhanden sind. So kann man quasi direkt eine Datei neu upload. Wenn nicht gewünscht bitte eigenständig bearbeiten
}
return {
vorgang,
vorgangList,
crimesList,
url,
crimeNames
}
}

View File

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

View File

@@ -0,0 +1,14 @@
import { getVorgaenge } from '$lib/server/vorgangService';
import { json } from '@sveltejs/kit';
export async function GET({ locals }) {
if (!locals.user) {
return json({ error: 'Unauthorized' }, { status: 401 });
}
const vorgaenge = getVorgaenge();
return new Response(JSON.stringify(vorgaenge), {
status: 200
});
}

View File

@@ -1,5 +1,10 @@
import { client } from '$lib/minio'; import { client } from '$lib/minio';
import { deleteVorgangByToken, vorgangNameExists } from '$lib/server/vorgangService'; import {
deleteVorgangByToken,
getCrimesListByToken,
vorgangNameExists
} from '$lib/server/vorgangService';
import { json } from '@sveltejs/kit';
export async function DELETE({ params }) { export async function DELETE({ params }) {
const vorgangToken = params.vorgang; const vorgangToken = params.vorgang;
@@ -38,3 +43,21 @@ export async function HEAD({ params }) {
return new Response(null, { status: 500 }); return new Response(null, { status: 500 });
} }
} }
export async function GET({ params, locals }) {
if (!locals.user) {
return json({ error: 'Unauthorized' }, { status: 401 });
}
try {
const vorgangToken = params.vorgang;
const crimesList = await getCrimesListByToken(vorgangToken);
return new Response(JSON.stringify(crimesList), {
status: 200
});
} catch (err) {
console.error('Fehler im GET-Handler:', err);
return new Response(null, { status: 500 });
}
}

View File

@@ -1,4 +1,6 @@
import { BUCKET, client } from '$lib/minio'; import { BUCKET, client } from '$lib/minio';
import { json } from '@sveltejs/kit';
import { getVorgangByName } from '$lib/server/vorgangService';
export async function GET() { export async function GET() {
const stream = client.listObjectsV2(BUCKET, '', true); const stream = client.listObjectsV2(BUCKET, '', true);
@@ -23,7 +25,6 @@ export async function GET() {
}); });
} }
export async function DELETE({ request }: { request: Request }) { export async function DELETE({ request }: { request: Request }) {
const url_fragments = request.url.split('/'); const url_fragments = request.url.split('/');
const item = url_fragments.at(-1); const item = url_fragments.at(-1);
@@ -33,3 +34,32 @@ export async function DELETE({ request }: { request: Request }) {
return new Response(null, { status: 204 }); return new Response(null, { status: 204 });
} }
// rename operation for crimes
export async function PUT({ params, request }) {
const data = await request.json();
const vorgangToken = params.vorgang;
// prepare copy, incl. check if new name exists already
const crimeOldName = data['oldName'];
const crimeS3FullBucketPathOld = `/tatort/${vorgangToken}/${crimeOldName}`;
const crimeNewName = `${vorgangToken}/${data['newName']}`;
if (!crimeOldName || !crimeNewName) {
return json({ msg: 'Der Name darf nicht leer sein.' }, { status: 400 });
}
try {
await client.statObject('tatort', crimeNewName);
return json({ msg: 'Die Datei existiert bereits.' }, { status: 400 });
} catch (error) {
console.log(error, 'continue operation');
}
await client.copyObject('tatort', crimeNewName, crimeS3FullBucketPathOld);
await client.removeObject('tatort', `${vorgangToken}/${crimeOldName}`);
return json({ success: 'success' }, { status: 200 });
}

View File

@@ -2,10 +2,12 @@ import { db } from '$lib/server/dbService';
/** @type {import('./$types').RequestHandler} */ /** @type {import('./$types').RequestHandler} */
export async function GET({ params }) { export async function GET({ params }) {
const vorgangName = params.vorgang; const vorgangToken = params.vorgang;
const getPINSQLStatement = `SELECT pin FROM cases WHERE name = ?;`; const getPINSQLStatement = `SELECT pin
const row = db.prepare(getPINSQLStatement).get(vorgangName); FROM cases
WHERE token = ?;`;
const row = db.prepare(getPINSQLStatement).get(vorgangToken);
const vorgangPIN = row?.pin; const vorgangPIN = row?.pin;
if (vorgangPIN) { if (vorgangPIN) {

View File

@@ -0,0 +1,10 @@
//Rollenabfrage ob Benutzer admin ist
import { json } from "@sveltejs/kit";
export async function GET({locals}) {
const isAdmin = locals.user?.admin === true;
const data = {admin: isAdmin}
return json(data)
}