f047_neu_Edit-der-Namen #28
@@ -1,3 +1,31 @@
|
||||
<script lang="ts">
|
||||
export let href = null;
|
||||
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>
|
||||
.button {
|
||||
@apply inline-flex;
|
||||
@@ -172,31 +200,3 @@
|
||||
@apply justify-end;
|
||||
}
|
||||
</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}
|
||||
|
||||
90
src/lib/components/NameItemEditor.svelte
Normal file
90
src/lib/components/NameItemEditor.svelte
Normal 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.
@@ -31,10 +31,16 @@ export const getCrimesListByToken = async (vorgangToken: string) => {
|
||||
* @param vorgangToken
|
||||
* @returns vorgangObj with keys `token`, `name`, `pin` || undefined
|
||||
*/
|
||||
export const getVorgangByToken = (vorgangToken: string): {token: string, name:string, pin: string} | undefined => {
|
||||
const getVorgangSQLStmt = `SELECT token, name, pin FROM cases WHERE token = ?`;
|
||||
export const getVorgangByToken = (
|
||||
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 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;
|
||||
};
|
||||
@@ -44,10 +50,16 @@ export const getVorgangByToken = (vorgangToken: string): {token: string, name:st
|
||||
* @param vorgangName
|
||||
* @returns vorgangObj with keys `token`, `name`, `pin` || undefined
|
||||
*/
|
||||
export const getVorgangByName = (vorgangName: string): {token: string, name: string, pin: string} | undefined => {
|
||||
const getVorgangByNameSQLStmt = `SELECT token, name, pin FROM cases WHERE name = ?`;
|
||||
export const getVorgangByName = (
|
||||
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 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;
|
||||
};
|
||||
@@ -88,13 +100,22 @@ export const getListOfVorgänge = async () => {
|
||||
* Fetches list of vorgänge from database
|
||||
* @returns list with of available vorgaenge
|
||||
*/
|
||||
export const getVorgaenge = (): {vorgangToken: string, vorgangName: string, vorgangPIN: string}[] => {
|
||||
const getVorgaengeSQLStmt = `SELECT token, name, pin from cases`;
|
||||
export const getVorgaenge = (): {
|
||||
vorgangToken: string;
|
||||
vorgangName: string;
|
||||
vorgangPIN: string;
|
||||
}[] => {
|
||||
const getVorgaengeSQLStmt = `SELECT token, name, pin
|
||||
from cases`;
|
||||
const statement = db.prepare(getVorgaengeSQLStmt);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
const target = ev.currentTarget as HTMLElement | null;
|
||||
if (!target) return;
|
||||
let filename = target.id.split('del__')[1];
|
||||
|
||||
// delete request
|
||||
// --------------
|
||||
|
||||
let url = `/api/list/${filename}`;
|
||||
|
||||
@@ -46,13 +43,16 @@
|
||||
<ul role="list" class="divide-y divide-gray-100">
|
||||
{#each vorgangList as vorgangItem}
|
||||
<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">
|
||||
<!-- Ordner -->
|
||||
<Folder />
|
||||
<div class="min-w-0 flex-auto">
|
||||
<span class="text-sm font-semibold leading-6 text-gray-900">{vorgangItem.vorgangName}</span>
|
||||
<!-- Delete button -->
|
||||
<span class="text-sm font-semibold leading-6 text-gray-900"
|
||||
>{vorgangItem.vorgangName}</span
|
||||
>
|
||||
<button
|
||||
style="padding: 2px"
|
||||
id="del__{vorgangItem.vorgangToken}"
|
||||
|
||||
@@ -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
|
||||
};
|
||||
};
|
||||
@@ -1,7 +1,5 @@
|
||||
<script lang="ts">
|
||||
|
|
||||
import shortenFileSize from '$lib/helper/shortenFileSize';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import timeElapsed from '$lib/helper/timeElapsed';
|
||||
|
||||
import Alert from '$lib/components/Alert.svelte';
|
||||
@@ -11,280 +9,210 @@
|
||||
import ModalContent from '$lib/components/Modal/ModalContent.svelte';
|
||||
import ModalFooter from '$lib/components/Modal/ModalFooter.svelte';
|
||||
import Cube from '$lib/icons/Cube.svelte';
|
||||
import Edit from '$lib/icons/Edit.svelte';
|
||||
import Trash from '$lib/icons/Trash.svelte';
|
||||
import { invalidate, invalidateAll } from '$app/navigation';
|
||||
import NameItemEditor from '$lib/components/NameItemEditor.svelte';
|
||||
|
||||
/** export let data; */
|
||||
/** @type {import('./$types').PageData} */
|
||||
export let data;
|
||||
//Seite für die Tatort-Liste
|
||||
let { data } = $props();
|
||||
|
||||
console.log('tatorte: debug ', data);
|
||||
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;
|
||||
size: number;
|
||||
lastModified: string | number | Date;
|
||||
show_button?: boolean;
|
||||
prefix?: string;
|
||||
// add other properties as needed
|
||||
}
|
||||
|
||||
const vorgang = data.vorgang;
|
||||
const crimesList: ListItem[] = data.crimesList;
|
||||
const vorgangPIN: string = data.vorgangPIN;
|
||||
let vorgangName: string = data.vorgang.vorgangName;
|
||||
let crimesList: ListItem[] = $state(data.crimesList);
|
||||
const vorgangPIN: string = data.vorgang.vorgangPIN;
|
||||
let vorgangToken: string = data.vorgang.vorgangToken;
|
||||
|
||||
let open = false;
|
||||
$: open;
|
||||
let inProgress = false;
|
||||
$: inProgress;
|
||||
let err = false;
|
||||
$: err;
|
||||
//Variablen für Modal
|
||||
let open = $state(false);
|
||||
let inProgress = $state(false);
|
||||
let isError = $state(false);
|
||||
|
||||
let rename_input;
|
||||
$: rename_input;
|
||||
//Variable um nur admin UI anzuzeigen
|
||||
let admin = data?.user?.admin;
|
||||
|
||||
function uploadSuccessful() {
|
||||
open = false;
|
||||
}
|
||||
async function handleSave(newName: string, oldName: string) {
|
||||
open = true;
|
||||
inProgress = true;
|
||||
try {
|
||||
const res = await fetch(`/api/list/${vorgangToken}/${oldName}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'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) {
|
||||
let item = crimesList[i];
|
||||
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
|
||||
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}`);
|
||||
if (!res.ok) {
|
||||
const msg = await res.text();
|
||||
console.error('❌ Fehler beim Umbenennen:', msg);
|
||||
isError = true;
|
||||
inProgress = false;
|
||||
} else {
|
||||
uploadSuccessful();
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 500);
|
||||
await invalidateAll();
|
||||
crimesList = data.crimesList;
|
||||
open = false;
|
||||
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() {
|
||||
const subject = "Link zum Tatvorgang";
|
||||
const link = $page.url.toString().split('?')[0];
|
||||
const subject = 'Link zum Tatvorgang';
|
||||
|
||||
const link = data.url.origin + data.url.pathname;
|
||||
const body = `Hallo,
|
||||
|
||||
hier ist der Link zum Tatvorgang:
|
||||
${link}
|
||||
hier ist der Link zum Tatvorgang:
|
||||
${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)}`;
|
||||
|
||||
return mailtoLink;
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
open = false;
|
||||
isError = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="-z-10 bg-white">
|
||||
<div class="flex flex-col items-center justify-center w-full">
|
||||
<h1 class="text-xl">Vorgang {vorgang.name}</h1>
|
||||
{#if data?.user?.admin}
|
||||
Zugangs-PIN: {vorgang.pin}
|
||||
<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, crimeListItemIndex}
|
||||
<li>
|
||||
<a
|
||||
href="/view/{$page.params.vorgang}/{item.name}?pin={vorgangPIN}"
|
||||
class=" flex justify-between gap-x-6 py-5"
|
||||
aria-label="zum 3D-modell"
|
||||
>
|
||||
{#if data.vorgang && data.crimesList}
|
||||
<div class="-z-10 bg-white">
|
||||
<div class="flex flex-col items-center justify-center w-full">
|
||||
<h1 class="text-xl">Vorgang {vorgangName}</h1>
|
||||
|
||||
{#if admin}
|
||||
Zugangs-PIN: {vorgangPIN}
|
||||
<a class="pt-2 pb-6" 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 data.crimesList as item, crimeListItemIndex}
|
||||
<li>
|
||||
<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">
|
||||
{#if data?.user?.admin}
|
||||
<span
|
||||
id="label__{item.name}"
|
||||
class="text-sm font-semibold leading-6 text-gray-900 inline-block min-w-1"
|
||||
contenteditable={!item.show_button}
|
||||
role="textbox"
|
||||
tabindex="0"
|
||||
aria-label="Dateiname bearbeiten"
|
||||
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>
|
||||
{#if admin}
|
||||
<NameItemEditor
|
||||
list={data.crimesList}
|
||||
editedName={data.crimeNames[crimeListItemIndex]}
|
||||
currentName={item.name}
|
||||
onSave={handleSave}
|
||||
onDelete={handleDelete}
|
||||
></NameItemEditor>
|
||||
{:else}
|
||||
<span class="text-sm font-semibold leading-6 text-gray-900 inline-block min-w-1"
|
||||
>{item.name}</span
|
||||
>
|
||||
{/if}
|
||||
<p class="mt-1 truncate text-xs leading-5 text-gray-500">
|
||||
{shortenFileSize(item.size)}
|
||||
</p>
|
||||
{#if item.size}
|
||||
<p class="mt-1 truncate text-xs leading-5 text-gray-500">
|
||||
{shortenFileSize(item.size)}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<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="mt-1 text-xs leading-5 text-gray-500">
|
||||
Zuletzt geändert <time datetime="2023-01-23T13:23Z"
|
||||
>{timeElapsed(new Date(item.lastModified))}</time
|
||||
>
|
||||
</p>
|
||||
{#if item.lastModified}
|
||||
<p class="mt-1 text-xs leading-5 text-gray-500">
|
||||
Zuletzt geändert <time datetime="2023-01-23T13:23Z"
|
||||
>{timeElapsed(new Date(item.lastModified))}</time
|
||||
>
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Modal {open}
|
||||
><ModalTitle>Umbenennen</ModalTitle><ModalContent>
|
||||
{#if inProgress}
|
||||
<p class="py-2 mb-1">Vorgang läuft...</p>
|
||||
{/if}
|
||||
{#if err}
|
||||
<Alert class="w-full" type="error">Fehler beim Umbenennen</Alert>
|
||||
{/if}
|
||||
</ModalContent>
|
||||
<ModalFooter><Button disabled={inProgress} on:click={uploadSuccessful}>Ok</Button></ModalFooter>
|
||||
</Modal>
|
||||
</div>
|
||||
<Modal {open}
|
||||
><ModalTitle>Umbenennen</ModalTitle><ModalContent>
|
||||
{#if inProgress}
|
||||
<p class="py-2 mb-1">Vorgang läuft...</p>
|
||||
{:else if isError}
|
||||
<Alert class="w-full" type="error">Fehler beim Umbenennen</Alert>
|
||||
{:else}
|
||||
<Alert class="w-full">Umbenennen erfolgreich</Alert>
|
||||
{/if}
|
||||
</ModalContent>
|
||||
<ModalFooter><Button disabled={inProgress} on:click={closeModal}>Ok</Button></ModalFooter>
|
||||
</Modal>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
ul {
|
||||
|
||||
27
src/routes/(token-based)/list/[vorgang]/+page.ts
Normal file
27
src/routes/(token-based)/list/[vorgang]/+page.ts
Normal 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
|
||||
|
jared
commented
Ok, aber warum. Erwarte ich hier nicht dann die Liste?? Ok, aber warum. Erwarte ich hier nicht dann die Liste??
mina
commented
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
|
||||
}
|
||||
}
|
||||
@@ -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 });
|
||||
};
|
||||
14
src/routes/api/list/+server.ts
Normal file
14
src/routes/api/list/+server.ts
Normal 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
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
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 }) {
|
||||
const vorgangToken = params.vorgang;
|
||||
@@ -38,3 +43,21 @@ export async function HEAD({ params }) {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { BUCKET, client } from '$lib/minio';
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { getVorgangByName } from '$lib/server/vorgangService';
|
||||
|
||||
export async function GET() {
|
||||
const stream = client.listObjectsV2(BUCKET, '', true);
|
||||
@@ -23,7 +25,6 @@ export async function GET() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export async function DELETE({ request }: { request: Request }) {
|
||||
const url_fragments = request.url.split('/');
|
||||
const item = url_fragments.at(-1);
|
||||
@@ -33,3 +34,32 @@ export async function DELETE({ request }: { request: Request }) {
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@ import { db } from '$lib/server/dbService';
|
||||
|
||||
/** @type {import('./$types').RequestHandler} */
|
||||
export async function GET({ params }) {
|
||||
const vorgangName = params.vorgang;
|
||||
const vorgangToken = params.vorgang;
|
||||
|
||||
const getPINSQLStatement = `SELECT pin FROM cases WHERE name = ?;`;
|
||||
const row = db.prepare(getPINSQLStatement).get(vorgangName);
|
||||
const getPINSQLStatement = `SELECT pin
|
||||
FROM cases
|
||||
WHERE token = ?;`;
|
||||
const row = db.prepare(getPINSQLStatement).get(vorgangToken);
|
||||
const vorgangPIN = row?.pin;
|
||||
|
||||
if (vorgangPIN) {
|
||||
|
||||
10
src/routes/api/user/+server.ts
Normal file
10
src/routes/api/user/+server.ts
Normal 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)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user
Wie du beschrieben hast, unnötige Kommentare und console.logs raus
Sollte soweit passen, ansonsten bitte selbstständig rauslöschen