feat(ccli): integrate CCLI buttons in ArrangementDialog and SongDB Index

This commit is contained in:
Thorsten Bus 2026-05-11 10:29:52 +02:00
parent 3020800acb
commit b0320fbef5
2 changed files with 72 additions and 9 deletions

View file

@ -2,6 +2,7 @@
import { computed, nextTick, ref, watch, onMounted, onUnmounted } from 'vue' import { computed, nextTick, ref, watch, onMounted, onUnmounted } from 'vue'
import { router } from '@inertiajs/vue3' import { router } from '@inertiajs/vue3'
import { VueDraggable } from 'vue-draggable-plus' import { VueDraggable } from 'vue-draggable-plus'
import CcliPasteDialog from '@/Components/CcliPasteDialog.vue'
const MASTER_ID = 'master' const MASTER_ID = 'master'
@ -42,6 +43,7 @@ const searchQuery = ref('')
const selectedSongId = ref('') const selectedSongId = ref('')
const dropdownOpen = ref(false) const dropdownOpen = ref(false)
const assignError = ref('') const assignError = ref('')
const ccliDialogOpen = ref(false)
function normalize(value) { function normalize(value) {
return (value ?? '').toString().toLowerCase().trim() return (value ?? '').toString().toLowerCase().trim()
@ -539,6 +541,7 @@ function closeOnBackdrop(e) {
<p v-if="assignError" class="text-sm text-red-600">{{ assignError }}</p> <p v-if="assignError" class="text-sm text-red-600">{{ assignError }}</p>
<div class="flex items-center gap-2">
<button <button
type="button" type="button"
class="inline-flex items-center justify-center rounded-md bg-emerald-600 px-5 py-2 text-sm font-semibold text-white shadow transition hover:bg-emerald-700" class="inline-flex items-center justify-center rounded-md bg-emerald-600 px-5 py-2 text-sm font-semibold text-white shadow transition hover:bg-emerald-700"
@ -547,6 +550,25 @@ function closeOnBackdrop(e) {
> >
Zuordnen Zuordnen
</button> </button>
<a
v-if="searchQuery"
:href="'https://songselect.ccli.com/Search/Results?searchText=' + encodeURIComponent(searchQuery)"
target="_blank"
rel="noopener noreferrer"
data-testid="songselect-search-button"
class="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium text-gray-700 shadow-sm transition hover:bg-gray-50"
>
Auf SongSelect suchen
</a>
<button
type="button"
data-testid="open-ccli-paste-dialog-button"
@click="ccliDialogOpen = true"
class="inline-flex items-center justify-center rounded-md border border-blue-300 bg-blue-50 px-3 py-2 text-sm font-medium text-blue-700 shadow-sm transition hover:bg-blue-100"
>
Aus CCLI importieren
</button>
</div>
</div> </div>
</div> </div>
@ -735,6 +757,15 @@ function closeOnBackdrop(e) {
</div> </div>
</Transition> </Transition>
</Teleport> </Teleport>
<!-- CCLI Paste Dialog -->
<CcliPasteDialog
:open="ccliDialogOpen"
mode="service-form"
:service-song-id="props.serviceSongId"
@close="ccliDialogOpen = false"
@imported="(songId) => { ccliDialogOpen = false; router.reload({ only: ['service'] }) }"
/>
</template> </template>
<style scoped> <style scoped>

View file

@ -1,7 +1,8 @@
<script setup> <script setup>
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue' import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue'
import SongEditModal from '@/Components/SongEditModal.vue' import SongEditModal from '@/Components/SongEditModal.vue'
import { Head } from '@inertiajs/vue3' import CcliPasteDialog from '@/Components/CcliPasteDialog.vue'
import { Head, router } from '@inertiajs/vue3'
import axios from 'axios' import axios from 'axios'
import { ref, watch, onMounted } from 'vue' import { ref, watch, onMounted } from 'vue'
@ -22,6 +23,7 @@ const fileInput = ref(null)
// Edit modal state // Edit modal state
const showEditModal = ref(false) const showEditModal = ref(false)
const editSongId = ref(null) const editSongId = ref(null)
const ccliDialogOpen = ref(false)
let debounceTimer = null let debounceTimer = null
// Preview modal state // Preview modal state
@ -387,6 +389,28 @@ function pageRange() {
</Transition> </Transition>
</div> </div>
<!-- CCLI Import Buttons -->
<div class="mb-4 flex items-center gap-2">
<a
v-if="search"
:href="'https://songselect.ccli.com/Search/Results?searchText=' + encodeURIComponent(search)"
target="_blank"
rel="noopener noreferrer"
data-testid="songselect-search-button-songdb"
class="inline-flex items-center gap-1 rounded-md border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 shadow-sm transition hover:bg-gray-50"
>
Auf SongSelect suchen
</a>
<button
type="button"
data-testid="open-ccli-paste-dialog-button-songdb"
@click="ccliDialogOpen = true"
class="inline-flex items-center gap-1 rounded-md border border-blue-300 bg-blue-50 px-3 py-1.5 text-sm font-medium text-blue-700 shadow-sm transition hover:bg-blue-100"
>
Aus CCLI importieren
</button>
</div>
<!-- Song Count + Loading --> <!-- Song Count + Loading -->
<div class="mb-3 flex items-center justify-between px-1"> <div class="mb-3 flex items-center justify-between px-1">
<p class="text-xs font-medium text-gray-500"> <p class="text-xs font-medium text-gray-500">
@ -758,4 +782,12 @@ function pageRange() {
</Teleport> </Teleport>
</AuthenticatedLayout> </AuthenticatedLayout>
<!-- CCLI Paste Dialog -->
<CcliPasteDialog
:open="ccliDialogOpen"
mode="songdb"
@close="ccliDialogOpen = false"
@imported="(songId, mode) => { ccliDialogOpen = false; if (mode === 'stay') { router.reload({ only: ['songs'] }) } }"
/>
</template> </template>