49 lines
4.0 KiB
Vue
49 lines
4.0 KiB
Vue
<template>
|
||
<div class="min-h-screen bg-gray-50 py-10">
|
||
<div class="max-w-2xl mx-auto px-4">
|
||
<h1 class="text-3xl font-display font-bold text-gray-900">myTischtennis-Verbindung</h1>
|
||
<p class="mt-2 text-gray-600">Vereinszugang für den geschützten Import aktueller TTR- und QTTR-Werte.</p>
|
||
<form class="mt-8 bg-white rounded-xl shadow-lg p-6 space-y-5" @submit.prevent="save">
|
||
<div class="rounded-lg bg-amber-50 border border-amber-200 p-4 text-sm text-amber-900">
|
||
Das Passwort wird verschlüsselt auf dem Server gespeichert und nie wieder angezeigt. Zugriff haben ausschließlich Admin und Vorstand.
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">myTischtennis-E-Mail</label>
|
||
<input v-model.trim="form.email" type="email" required autocomplete="username" class="w-full rounded-lg border border-gray-300 px-3 py-2">
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Passwort</label>
|
||
<input v-model="form.password" type="password" required autocomplete="current-password" class="w-full rounded-lg border border-gray-300 px-3 py-2">
|
||
</div>
|
||
<div class="grid sm:grid-cols-2 gap-4">
|
||
<label class="block text-sm font-medium text-gray-700">Verband<input v-model.trim="form.association" required class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2"></label>
|
||
<label class="block text-sm font-medium text-gray-700">Vereinsnummer<input v-model.trim="form.clubId" required class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2"></label>
|
||
</div>
|
||
<p v-if="status.configured" class="text-sm text-gray-600">Hinterlegt: {{ status.email }} · letzter erfolgreicher Abruf: {{ formattedLastImport }}</p>
|
||
<p v-if="status.lastImportError" class="rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-800">
|
||
Letzter Abruf fehlgeschlagen: {{ status.lastImportError }}
|
||
</p>
|
||
<p v-if="message" class="text-sm" :class="error ? 'text-red-700' : 'text-green-700'">{{ message }}</p>
|
||
<div class="flex gap-3 flex-wrap">
|
||
<button :disabled="busy" class="rounded-lg bg-primary-600 px-4 py-2 font-semibold text-white disabled:opacity-60">Zugang speichern</button>
|
||
<button type="button" :disabled="busy || !status.configured" class="rounded-lg border border-primary-600 px-4 py-2 font-semibold text-primary-700 disabled:opacity-60" @click="runImport">Jetzt abrufen</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, onMounted, reactive, ref } from 'vue'
|
||
definePageMeta({ middleware: 'auth' })
|
||
useHead({ title: 'myTischtennis-Verbindung – CMS' })
|
||
const form = reactive({ email: '', password: '', association: 'HeTTV', clubId: '43030' })
|
||
const status = ref({ configured: false })
|
||
const busy = ref(false); const message = ref(''); const error = ref(false)
|
||
const formattedLastImport = computed(() => status.value.lastSuccessfulImportAt ? new Intl.DateTimeFormat('de-DE', { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(status.value.lastSuccessfulImportAt)) : 'noch nie')
|
||
async function load() { status.value = await $fetch('/api/cms/mytischtennis') }
|
||
async function save() { busy.value = true; message.value = ''; error.value = false; try { status.value = await $fetch('/api/cms/mytischtennis', { method: 'PUT', body: form }); form.password = ''; message.value = 'Zugang verschlüsselt gespeichert.' } catch { error.value = true; message.value = 'Zugang konnte nicht gespeichert werden.' } finally { busy.value = false } }
|
||
async function runImport() { busy.value = true; message.value = ''; error.value = false; try { const result = await $fetch('/api/cms/mytischtennis/import', { method: 'POST' }); message.value = `${result.rowCount} Werte wurden aktualisiert.`; await load() } catch (caught) { error.value = true; message.value = caught?.data?.statusMessage || 'Abruf fehlgeschlagen.'; await load().catch(() => {}) } finally { busy.value = false } }
|
||
onMounted(load)
|
||
</script>
|