140 lines
5.1 KiB
Vue
140 lines
5.1 KiB
Vue
<script setup>
|
|
import { reactive } from 'vue'
|
|
import { route } from 'ziggy-js'
|
|
|
|
const props = defineProps({
|
|
settings: { type: Object, default: () => ({}) },
|
|
})
|
|
|
|
const fields = [
|
|
{ key: 'agenda_start_title', label: 'Ablauf-Start', placeholder: 'z.B. Ablauf* oder Beginn*' },
|
|
{ key: 'agenda_end_title', label: 'Ablauf-Ende', placeholder: 'z.B. Ende* oder Schluss*' },
|
|
{
|
|
key: 'agenda_announcement_position',
|
|
label: 'Ankündigungen-Position',
|
|
placeholder: 'z.B. Informationen*,Hinweise*',
|
|
helpText: 'Komma-getrennte Liste. Das erste passende Element im Ablauf bestimmt, wo die Ankündigungsfolien eingefügt werden. * als Platzhalter.',
|
|
},
|
|
{
|
|
key: 'agenda_sermon_matching',
|
|
label: 'Predigt-Erkennung',
|
|
placeholder: 'z.B. Predigt*,Sermon*',
|
|
helpText: 'Komma-getrennte Liste. Erkannte Elemente bekommen einen Predigt-Upload-Bereich. * als Platzhalter.',
|
|
},
|
|
]
|
|
|
|
const form = reactive({})
|
|
for (const field of fields) {
|
|
form[field.key] = props.settings[field.key] ?? ''
|
|
}
|
|
|
|
const saving = reactive({})
|
|
const saved = reactive({})
|
|
const errors = reactive({})
|
|
|
|
async function saveField(key) {
|
|
if (saving[key]) return
|
|
saving[key] = true
|
|
errors[key] = null
|
|
saved[key] = false
|
|
try {
|
|
const response = await fetch(route('settings.update'), {
|
|
method: 'PATCH',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
'X-XSRF-TOKEN': decodeURIComponent(document.cookie.match(/XSRF-TOKEN=([^;]+)/)?.[1] ?? ''),
|
|
},
|
|
body: JSON.stringify({ key, value: form[key] || null }),
|
|
})
|
|
if (!response.ok) {
|
|
const data = await response.json()
|
|
errors[key] = data.message || 'Speichern fehlgeschlagen'
|
|
return
|
|
}
|
|
saved[key] = true
|
|
setTimeout(() => { saved[key] = false }, 2000)
|
|
} catch {
|
|
errors[key] = 'Netzwerkfehler beim Speichern'
|
|
} finally {
|
|
saving[key] = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<h3 class="text-sm font-semibold text-gray-900 mb-1">
|
|
Agenda-Konfiguration
|
|
</h3>
|
|
<p class="text-xs text-gray-500 mb-4">
|
|
Diese Einstellungen steuern, wie der Gottesdienst-Ablauf angezeigt und exportiert wird.
|
|
</p>
|
|
|
|
<div class="divide-y divide-gray-100">
|
|
<div
|
|
v-for="field in fields"
|
|
:key="field.key"
|
|
class="py-4"
|
|
>
|
|
<label
|
|
:for="'setting-' + field.key"
|
|
class="block text-sm font-medium text-gray-700"
|
|
>
|
|
{{ field.label }}
|
|
</label>
|
|
|
|
<div class="relative mt-1.5">
|
|
<input
|
|
:id="'setting-' + field.key"
|
|
:data-testid="'setting-' + field.key"
|
|
v-model="form[field.key]"
|
|
type="text"
|
|
:placeholder="field.placeholder || ''"
|
|
class="block w-full rounded-lg border-gray-300 text-sm shadow-sm transition-colors focus:border-amber-400 focus:ring-amber-400/40"
|
|
:class="{
|
|
'border-red-300 focus:border-red-400 focus:ring-red-400/40': errors[field.key],
|
|
'border-emerald-300': saved[field.key],
|
|
}"
|
|
@blur="saveField(field.key)"
|
|
/>
|
|
|
|
<div
|
|
v-if="saving[field.key]"
|
|
class="absolute inset-y-0 right-0 flex items-center pr-3"
|
|
>
|
|
<svg class="h-4 w-4 animate-spin text-gray-400" fill="none" viewBox="0 0 24 24">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
|
</svg>
|
|
</div>
|
|
|
|
<div
|
|
v-else-if="saved[field.key]"
|
|
class="absolute inset-y-0 right-0 flex items-center pr-3"
|
|
>
|
|
<svg class="h-4 w-4 text-emerald-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
<p
|
|
v-if="errors[field.key]"
|
|
class="mt-1.5 text-xs text-red-600"
|
|
:data-testid="'error-' + field.key"
|
|
>
|
|
{{ errors[field.key] }}
|
|
</p>
|
|
|
|
<p
|
|
v-if="field.helpText"
|
|
class="mt-1.5 text-xs text-gray-400"
|
|
>
|
|
{{ field.helpText }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|