mirror of
https://github.com/ericasch6/scrum-poker-internal.git
synced 2026-10-13 16:12:09 +00:00
306 lines
12 KiB
Vue
306 lines
12 KiB
Vue
<script setup lang="ts">
|
|
import { Link, useForm } from '@inertiajs/vue3';
|
|
import {
|
|
DoorOpen,
|
|
Plus,
|
|
Settings as SettingsIcon,
|
|
Trash2,
|
|
UserMinus,
|
|
} from '@lucide/vue';
|
|
import { ref } from 'vue';
|
|
|
|
import DashboardRoomCard from '@/components/dashboard/DashboardRoomCard.vue';
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from '@/components/ui/dialog';
|
|
import { create, destroy, reopen, show } from '@/routes/room';
|
|
import { destroy as kickRoomPlayer } from '@/routes/room/player';
|
|
import { edit as roomSettings } from '@/routes/room/settings';
|
|
import type {
|
|
DashboardRoom,
|
|
DashboardRoomPlayer,
|
|
OwnedRoomListProps,
|
|
} from '@/types/dashboard';
|
|
|
|
defineProps<OwnedRoomListProps>();
|
|
|
|
const selectedRoom = ref<DashboardRoom | null>(null);
|
|
const selectedPlayer = ref<{
|
|
room: DashboardRoom;
|
|
player: DashboardRoomPlayer;
|
|
} | null>(null);
|
|
const deleteForm = useForm({});
|
|
const kickForm = useForm({});
|
|
|
|
function setDeleteDialogOpen(open: boolean): void {
|
|
if (!open && !deleteForm.processing) {
|
|
selectedRoom.value = null;
|
|
}
|
|
}
|
|
|
|
function setKickDialogOpen(open: boolean): void {
|
|
if (!open && !kickForm.processing) {
|
|
selectedPlayer.value = null;
|
|
}
|
|
}
|
|
|
|
function deleteRoom(): void {
|
|
if (selectedRoom.value === null || deleteForm.processing) {
|
|
return;
|
|
}
|
|
|
|
deleteForm.delete(destroy(selectedRoom.value.id).url, {
|
|
preserveScroll: true,
|
|
onSuccess: () => {
|
|
selectedRoom.value = null;
|
|
},
|
|
});
|
|
}
|
|
|
|
function kickPlayer(): void {
|
|
if (selectedPlayer.value === null || kickForm.processing) {
|
|
return;
|
|
}
|
|
|
|
kickForm.delete(
|
|
kickRoomPlayer({
|
|
room: selectedPlayer.value.room.id,
|
|
player: selectedPlayer.value.player.id,
|
|
}).url,
|
|
{
|
|
preserveScroll: true,
|
|
onSuccess: () => {
|
|
selectedPlayer.value = null;
|
|
},
|
|
},
|
|
);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<section class="flex flex-col gap-6">
|
|
<div
|
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
|
>
|
|
<div>
|
|
<h1 class="text-2xl font-bold tracking-tight">Meine Räume</h1>
|
|
<p class="mt-1 text-sm text-muted-foreground">
|
|
Deine erstellten Scrum-Poker-Räume auf einen Blick.
|
|
</p>
|
|
</div>
|
|
|
|
<Link
|
|
:href="create()"
|
|
class="inline-flex w-fit items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground transition hover:bg-primary/90"
|
|
>
|
|
<Plus class="size-4" />
|
|
Raum erstellen
|
|
</Link>
|
|
</div>
|
|
|
|
<div
|
|
v-if="rooms.length > 0"
|
|
class="grid gap-4 md:grid-cols-2 xl:grid-cols-3"
|
|
>
|
|
<DashboardRoomCard
|
|
v-for="room in rooms"
|
|
:key="room.id"
|
|
:room="room"
|
|
show-actions
|
|
>
|
|
<template #actions>
|
|
<Link
|
|
v-if="!room.closed_at"
|
|
:href="show(room.id)"
|
|
class="inline-flex items-center gap-2 rounded-md px-3 py-2 text-sm font-semibold transition hover:bg-blue-500/80"
|
|
>
|
|
<DoorOpen class="size-4" />
|
|
Raum beitreten
|
|
</Link>
|
|
<Link
|
|
v-else
|
|
:href="reopen(room.id)"
|
|
method="post"
|
|
as="button"
|
|
class="inline-flex items-center gap-2 rounded-md px-3 py-2 text-sm font-semibold text-primary-foreground transition hover:bg-primary/90"
|
|
>
|
|
<DoorOpen class="size-4" />
|
|
Raum öffnen
|
|
</Link>
|
|
|
|
<Link
|
|
:href="roomSettings(room.id)"
|
|
class="inline-flex items-center gap-2 rounded-md px-3 py-2 text-sm font-semibold transition hover:bg-slate-700"
|
|
>
|
|
<SettingsIcon class="size-4" />
|
|
Einstellungen
|
|
</Link>
|
|
|
|
<button
|
|
type="button"
|
|
class="inline-flex items-center gap-2 rounded-md px-3 py-2 text-sm font-semibold text-rose-300 transition hover:bg-rose-500/80"
|
|
@click="selectedRoom = room"
|
|
>
|
|
<Trash2 class="size-4" />
|
|
Raum löschen
|
|
</button>
|
|
|
|
<div
|
|
class="mt-2 w-full rounded-md border border-slate-700 bg-slate-900/40 p-3"
|
|
>
|
|
<h3 class="text-sm font-semibold">Teilnehmer</h3>
|
|
|
|
<ul
|
|
v-if="room.players.length > 0"
|
|
class="mt-2 flex flex-col gap-2"
|
|
>
|
|
<li
|
|
v-for="player in room.players"
|
|
:key="player.id"
|
|
class="flex items-center justify-between gap-3 rounded-md bg-slate-800 px-3 py-2"
|
|
>
|
|
<span class="min-w-0">
|
|
<span
|
|
class="block truncate text-sm"
|
|
:class="
|
|
player.is_moderator
|
|
? 'text-amber-300'
|
|
: 'text-slate-100'
|
|
"
|
|
>
|
|
{{ player.name }}
|
|
</span>
|
|
</span>
|
|
|
|
<span
|
|
v-if="player.is_owner"
|
|
class="shrink-0 text-xs text-slate-400"
|
|
>
|
|
Du
|
|
</span>
|
|
<button
|
|
v-else
|
|
type="button"
|
|
class="inline-flex shrink-0 items-center gap-1.5 rounded-md px-2.5 py-1.5 text-xs font-semibold text-rose-300 transition hover:bg-rose-500/20 hover:text-rose-200"
|
|
@click="selectedPlayer = { room, player }"
|
|
>
|
|
<UserMinus class="size-3.5" />
|
|
Kicken
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<p v-else class="mt-2 text-sm text-slate-400">
|
|
Aktuell sind keine Teilnehmer im Raum.
|
|
</p>
|
|
</div>
|
|
</template>
|
|
</DashboardRoomCard>
|
|
</div>
|
|
|
|
<div
|
|
v-else
|
|
class="grid min-h-72 place-items-center rounded-md border border-dashed border-sidebar-border p-8 text-center"
|
|
>
|
|
<div>
|
|
<h2 class="text-lg font-semibold">Noch keine eigenen Räume</h2>
|
|
<p class="mt-2 max-w-md text-sm text-muted-foreground">
|
|
Erstelle deinen ersten Raum. Er wird automatisch in diesem
|
|
Dashboard gespeichert.
|
|
</p>
|
|
<Link
|
|
:href="create()"
|
|
class="mt-5 inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground"
|
|
>
|
|
<Plus class="size-4" />
|
|
Ersten Raum erstellen
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
|
|
<Dialog
|
|
:open="selectedRoom !== null"
|
|
@update:open="setDeleteDialogOpen"
|
|
>
|
|
<DialogContent
|
|
class="border-slate-700 bg-slate-800 text-slate-100 sm:max-w-md"
|
|
>
|
|
<DialogHeader>
|
|
<DialogTitle>Raum wirklich löschen?</DialogTitle>
|
|
<DialogDescription class="text-slate-300">
|
|
„{{ selectedRoom?.title ?? 'Scrum Poker' }}“ wird aus
|
|
deinem Dashboard entfernt. Alle Spieler, die den Raum
|
|
gerade geöffnet haben, werden automatisch
|
|
hinausgeleitet.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<DialogFooter class="gap-6 space-x-4 sm:gap-0">
|
|
<button
|
|
type="button"
|
|
class="rounded-md border border-slate-600 px-4 py-2 text-sm font-semibold text-slate-200 transition hover:bg-slate-700"
|
|
:disabled="deleteForm.processing"
|
|
@click="selectedRoom = null"
|
|
>
|
|
Abbrechen
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="rounded-md bg-rose-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-rose-500 disabled:opacity-50"
|
|
:disabled="deleteForm.processing"
|
|
@click="deleteRoom"
|
|
>
|
|
{{
|
|
deleteForm.processing
|
|
? 'Wird gelöscht …'
|
|
: 'Raum löschen'
|
|
}}
|
|
</button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
|
|
<Dialog
|
|
:open="selectedPlayer !== null"
|
|
@update:open="setKickDialogOpen"
|
|
>
|
|
<DialogContent
|
|
class="border-slate-700 bg-slate-800 text-slate-100 sm:max-w-md"
|
|
>
|
|
<DialogHeader>
|
|
<DialogTitle>Teilnehmer wirklich kicken?</DialogTitle>
|
|
<DialogDescription class="text-slate-300">
|
|
{{ selectedPlayer?.player.name }} wird aus „{{
|
|
selectedPlayer?.room.title ?? 'Scrum Poker'
|
|
}}“ entfernt und zur Startseite weitergeleitet.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<DialogFooter class="gap-6 space-x-4 sm:gap-0">
|
|
<button
|
|
type="button"
|
|
class="rounded-md border border-slate-600 px-4 py-2 text-sm font-semibold text-slate-200 transition hover:bg-slate-700"
|
|
:disabled="kickForm.processing"
|
|
@click="selectedPlayer = null"
|
|
>
|
|
Abbrechen
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="rounded-md bg-rose-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-rose-500 disabled:opacity-50"
|
|
:disabled="kickForm.processing"
|
|
@click="kickPlayer"
|
|
>
|
|
{{ kickForm.processing ? 'Wird entfernt …' : 'Kicken' }}
|
|
</button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</section>
|
|
</template>
|