feat(frontend): komponenty panelu
Widok rozbity na komponenty odpowiadające kafelkom pulpitu: - AppHeader, VoiceChannelPanel, PlayerPanel, AddTrackPanel - UploadsPanel wraz z UploadedFileRow współdzielonym z oknem modalnym - QueuePanel i QueueItem - przeciąganie, zmiana kolejności, limit odtworzeń - HistoryPanel, ModalShell, SettingsModal, UploadsLibraryModal, ToastHost Arkusz stylów przeniesiony ze static/style.css bez zmian w wyglądzie. Style, które app.js wstrzykiwał inline przy budowaniu HTML-a, przeniesione do klas współdzielonych i bloków scoped w komponentach. Poprawki bezpieczeństwa wynikające z przepisania: - Tytuły utworów i nazwy plików nie trafiają już do innerHTML. Poprzednia wersja składała wiersze kolejki, historii i listy plików ze stringów, więc tytuł zawierający znaczniki HTML był wstawiany do DOM bez escapowania. - Nazwy plików nie są już wklejane do atrybutów onclick z ręcznym escapowaniem apostrofu, które i tak nie obejmowało cudzysłowu. Zdarzenia podpina Vue, bez generowania kodu w stringach. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
119bff00a3
commit
038fa2ead4
16 files changed
+1245
No files matched your search
@@ -0,0 +1,94 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { FolderOpen, FolderUp, UploadCloud } from 'lucide-vue-next'
|
||||
import UploadedFileRow from '@/components/UploadedFileRow.vue'
|
||||
import { DASHBOARD_FILE_LIMIT, useUploads } from '@/composables/useUploads'
|
||||
import { useToasts } from '@/composables/useToasts'
|
||||
import { filesLabel } from '@/utils/plural'
|
||||
import type { BotStatus } from '@/types/status'
|
||||
|
||||
const props = defineProps<{ status: BotStatus | null }>()
|
||||
defineEmits<{ openLibrary: [] }>()
|
||||
|
||||
const { files, uploading, uploadLabel, upload, remove, enqueue } = useUploads()
|
||||
const { notify } = useToasts()
|
||||
const fileInput = ref<HTMLInputElement | null>(null)
|
||||
|
||||
const recent = computed(() => files.value.slice(0, DASHBOARD_FILE_LIMIT))
|
||||
|
||||
async function onFilesPicked(event: Event) {
|
||||
const input = event.target as HTMLInputElement
|
||||
if (input.files?.length) await upload(input.files)
|
||||
input.value = ''
|
||||
}
|
||||
|
||||
function play(filename: string) {
|
||||
if (!props.status?.active_voice_channel) {
|
||||
notify('Bot musi być połączony z kanałem głosowym!', 'error')
|
||||
return
|
||||
}
|
||||
enqueue(filename)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="glass-card uploads-panel">
|
||||
<div class="queue-header">
|
||||
<h2 class="card-title"><FolderUp /> Serwerowa baza plików</h2>
|
||||
<span class="queue-count">{{ filesLabel(files.length) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="upload-area">
|
||||
<input ref="fileInput" type="file" hidden multiple @change="onFilesPicked" />
|
||||
<button class="btn btn-secondary btn-block" :disabled="uploading" @click="fileInput?.click()">
|
||||
<UploadCloud /> Prześlij plik wideo/audio na serwer
|
||||
</button>
|
||||
<div v-if="uploading" class="upload-progress">
|
||||
<div class="progress-bar-bg">
|
||||
<div class="progress-bar-fill indeterminate"></div>
|
||||
</div>
|
||||
<span class="time-text">{{ uploadLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="queue-list-container list-short">
|
||||
<ul class="queue-list">
|
||||
<UploadedFileRow
|
||||
v-for="file in recent"
|
||||
:key="file.filename"
|
||||
:file="file"
|
||||
@enqueue="play"
|
||||
@remove="remove"
|
||||
/>
|
||||
<div v-if="!files.length" class="empty-queue-message">
|
||||
<FolderOpen />
|
||||
<p>Brak przesłanych plików na serwerze.</p>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-secondary btn-block" @click="$emit('openLibrary')">
|
||||
<FolderOpen /> Pokaż wszystkie pliki na serwerze ({{ files.length }})
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.upload-area {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.upload-progress {
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.upload-progress .time-text {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.btn-block:last-of-type {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user