add death to admin ui
All checks were successful
deploy / build-and-deploy (/testvaro, /opt/website-test, website-test) (push) Successful in 14s
deploy / build-and-deploy (/varo, /opt/website, website) (push) Successful in 14s

This commit is contained in:
2025-06-25 16:08:03 +02:00
parent 7a0db65f78
commit 6789a65285
12 changed files with 296 additions and 9 deletions

View File

@@ -6,7 +6,7 @@
import TeamSearch from '@components/admin/search/TeamSearch.svelte';
import { editReportStatus, getReportStatus } from '@app/admin/reports/reports.ts';
import { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
import Icon from "@iconify/svelte";
import Icon from '@iconify/svelte';
// html bindings
let previewDialogElem: HTMLDialogElement;

View File

@@ -0,0 +1,49 @@
<script lang="ts">
import { addDeath, fetchDeaths } from '@app/admin/teamDeaths/teamDeaths.ts';
import Icon from '@iconify/svelte';
import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
// states
let createPopupOpen = $state(false);
// lifecycle
$effect(() => {
fetchDeaths();
});
</script>
<div>
<button class="btn btn-soft w-full" onclick={() => (createPopupOpen = true)}>
<Icon icon="heroicons:plus-16-solid" />
<span>Neuer Spielertod</span>
</button>
</div>
<CrudPopup
texts={{
title: 'Spielertod erstellen',
submitButtonTitle: 'Erstellen',
confirmPopupTitle: 'Spielertod erstellen?',
confirmPopupMessage: 'Soll der neue Spielertod erstellt werden?'
}}
target={null}
keys={[
[
{
key: 'killed',
type: 'user-search',
label: 'Getöteter Spieler',
options: { required: true, validate: (user) => !!user?.id }
},
{
key: 'killer',
type: 'user-search',
label: 'Killer',
options: { validate: (user) => (user?.username ? !!user?.id : true) }
}
],
[{ key: 'message', type: 'textarea', label: 'Todesnachricht', options: { required: true, dynamicWidth: true } }]
]}
onSubmit={addDeath}
bind:open={createPopupOpen}
/>

View File

@@ -0,0 +1,69 @@
<script lang="ts">
// state
import { type Death, deaths, deleteDeath, editDeath } from '@app/admin/teamDeaths/teamDeaths.ts';
import { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
import DataTable from '@components/admin/table/DataTable.svelte';
import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
let editPopupDeath = $state(null);
let editPopupOpen = $derived(!!editPopupDeath);
// lifecycle
$effect(() => {
if (!editPopupOpen) editPopupDeath = null;
});
// callbacks
function onDeathDelete(death: Death) {
$confirmPopupState = {
title: 'Tod löschen?',
message: 'Soll der Tod wirklich gelöscht werden?',
onConfirm: () => deleteDeath(death)
};
}
</script>
{#snippet username(user?: { id: number; username: string })}
{user?.username}
{/snippet}
<DataTable
data={deaths}
count={true}
keys={[
{ key: 'killed', label: 'Getöteter Spieler', width: 20, transform: username },
{ key: 'killer', label: 'Killer', width: 20, transform: username },
{ key: 'message', label: 'Todesnachricht', width: 50 }
]}
onEdit={(death) => (editPopupDeath = death)}
onDelete={onDeathDelete}
/>
<CrudPopup
texts={{
title: 'Tod bearbeiten',
submitButtonTitle: 'Speichern',
confirmPopupTitle: 'Änderungen speichern?',
confirmPopupMessage: 'Sollen die Änderungen gespeichert werden?'
}}
target={editPopupDeath}
keys={[
[
{
key: 'killed',
type: 'user-search',
label: 'Getöteter Spieler',
options: { required: true, validate: (user) => !!user?.id }
},
{
key: 'killer',
type: 'user-search',
label: 'Killer',
options: { validate: (user) => (user?.username ? !!user?.id : true) }
}
],
[{ key: 'message', type: 'textarea', label: 'Todesnachricht', options: { required: true, dynamicWidth: true } }]
]}
onSubmit={editDeath}
bind:open={editPopupDeath}
/>

View File

@@ -0,0 +1,61 @@
import { type ActionReturnType, actions } from 'astro:actions';
import { writable } from 'svelte/store';
import { actionErrorPopup } from '@util/action.ts';
import { addToWritableArray, deleteFromWritableArray, updateWritableArray } from '@util/state.ts';
// types
export type Deaths = Exclude<ActionReturnType<typeof actions.team.deaths>['data'], undefined>['deaths'];
export type Death = Deaths[0];
// state
export const deaths = writable<Deaths>([]);
// actions
export async function fetchDeaths() {
const { data, error } = await actions.team.deaths();
if (error) {
actionErrorPopup(error);
return;
}
deaths.set(data.deaths);
}
export async function addDeath(death: Death) {
const { data, error } = await actions.team.addDeath({
deadUserId: death.killed.id,
killerUserId: death.killer?.id,
message: death.message
});
if (error) {
actionErrorPopup(error);
return;
}
addToWritableArray(deaths, Object.assign(death, { id: data.id }));
}
export async function editDeath(death: Death) {
const { error } = await actions.team.editDeath({
id: death.id,
deadUserId: death.killed.id,
killerUserId: death.killer?.id,
message: death.message
});
if (error) {
actionErrorPopup(error);
return;
}
updateWritableArray(deaths, death, (d) => d.id == death.id);
}
export async function deleteDeath(death: Death) {
const { error } = await actions.team.deleteDeath({ id: death.id });
if (error) {
actionErrorPopup(error);
return;
}
deleteFromWritableArray(deaths, (d) => d.id == death.id);
}