Implement intelligent menu filtering that hides self-referencing links and shows all applicable pages based on user role and authentication state. Changes: - Menu component now uses currentPath to filter out the current page link - Added computed properties to detect which page user is on - Fixed profile, admin, and users pages to dynamically detect admin status from API - Added menu to forgot-password page for consistent navigation - All pages now pass correct authentication state to Menu component This ensures menus always show relevant navigation options while avoiding redundant links to the current page. Admin users now see all admin options (Manage Sermons, Manage Users) regardless of which page they're on, except the current one. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
784 lines
28 KiB
Vue
784 lines
28 KiB
Vue
<template>
|
||
<div class="min-h-screen bg-gray-50 flex flex-col">
|
||
<header class="bg-white shadow-sm">
|
||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||
<div class="flex items-center justify-between mb-4">
|
||
<NuxtLink to="/">
|
||
<img src="/logos/logo.png" alt="New Life Christian Church" class="h-16 w-auto cursor-pointer hover:opacity-80" />
|
||
</NuxtLink>
|
||
<ClientOnly>
|
||
<Menu
|
||
:is-authenticated="isAuthenticated"
|
||
:is-admin="isAdmin"
|
||
:show-home="true"
|
||
:current-path="route.fullPath"
|
||
/>
|
||
</ClientOnly>
|
||
</div>
|
||
<h1 class="text-2xl font-bold text-gray-900">Manage Sermons</h1>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="flex-1 max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12 w-full">
|
||
<!-- Manage Existing Sermons Section -->
|
||
<div class="bg-white shadow-lg rounded-lg p-8 mb-8">
|
||
<h2 class="text-2xl font-bold text-gray-900 mb-6">Select Sermon to Manage</h2>
|
||
|
||
<div v-if="allSermons && allSermons.length > 0" class="space-y-4">
|
||
<div class="space-y-4">
|
||
<div>
|
||
<label for="sermon-select" class="block text-sm font-medium text-gray-700 mb-2">
|
||
Choose a Sermon
|
||
</label>
|
||
<select
|
||
id="sermon-select"
|
||
v-model="selectedSermonId"
|
||
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||
>
|
||
<option value="">-- Choose a sermon --</option>
|
||
<option v-for="sermon in allSermons" :key="sermon.id" :value="sermon.id">
|
||
{{ sermon.title }} ({{ formatDate(sermon.date) }}){{ sermon.archived ? ' [ARCHIVED]' : '' }}
|
||
</option>
|
||
</select>
|
||
</div>
|
||
<div class="space-y-3">
|
||
<div class="flex gap-4">
|
||
<button
|
||
type="button"
|
||
@click="handleEdit"
|
||
:disabled="!selectedSermonId"
|
||
class="flex-1 px-6 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed font-medium"
|
||
>
|
||
Edit
|
||
</button>
|
||
<button
|
||
v-if="!isSelectedSermonArchived"
|
||
type="button"
|
||
@click="handleArchive"
|
||
:disabled="!selectedSermonId || archiving"
|
||
class="flex-1 px-6 py-2 bg-yellow-600 text-white rounded-md hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500 disabled:opacity-50 disabled:cursor-not-allowed font-medium"
|
||
>
|
||
{{ archiving ? 'Archiving...' : 'Archive' }}
|
||
</button>
|
||
<button
|
||
v-else
|
||
type="button"
|
||
@click="handleUnarchive"
|
||
:disabled="!selectedSermonId || archiving"
|
||
class="flex-1 px-6 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed font-medium"
|
||
>
|
||
{{ archiving ? 'Unarchiving...' : 'Unarchive' }}
|
||
</button>
|
||
</div>
|
||
<button
|
||
type="button"
|
||
@click="handleDelete"
|
||
:disabled="!selectedSermonId || deleting"
|
||
class="w-full px-6 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 disabled:opacity-50 disabled:cursor-not-allowed font-medium"
|
||
>
|
||
{{ deleting ? 'Deleting...' : 'Delete' }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="deleteError || archiveError" class="text-red-600 text-sm">
|
||
{{ deleteError || archiveError }}
|
||
</div>
|
||
<div v-if="deleteSuccess || archiveSuccess" class="text-green-600 text-sm">
|
||
{{ deleteSuccess || archiveSuccess }}
|
||
</div>
|
||
</div>
|
||
|
||
<div v-else class="text-gray-500 text-sm">
|
||
No sermons available to manage.
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Create/Edit Sermon Form -->
|
||
<form @submit.prevent="handleSubmit" class="bg-white shadow-lg rounded-lg p-8 space-y-8">
|
||
<div class="flex items-center justify-between">
|
||
<h2 class="text-2xl font-bold text-gray-900">
|
||
{{ editingSermonId ? 'Edit Sermon' : 'Create New Sermon' }}
|
||
</h2>
|
||
<button
|
||
v-if="editingSermonId"
|
||
type="button"
|
||
@click="cancelEdit"
|
||
class="text-sm text-gray-600 hover:text-gray-800"
|
||
>
|
||
Cancel Edit
|
||
</button>
|
||
</div>
|
||
<!-- Basic Info -->
|
||
<div class="space-y-6">
|
||
<div>
|
||
<label for="date" class="block text-sm font-medium text-gray-700 mb-2">
|
||
Primary Sermon Date
|
||
</label>
|
||
<input
|
||
id="date"
|
||
v-model="formData.date"
|
||
type="date"
|
||
required
|
||
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||
/>
|
||
<p class="text-xs text-gray-500 mt-1">This date will be used for the sermon URL and sorting</p>
|
||
</div>
|
||
|
||
<!-- Additional Dates Section -->
|
||
<div class="border-t pt-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 mb-2">Additional Dates</h3>
|
||
<p class="text-sm text-gray-600 mb-4">Add more dates if this sermon will be presented multiple times (e.g., Saturday and Sunday services)</p>
|
||
<div class="space-y-3">
|
||
<div v-for="(date, index) in sermonDates" :key="index" class="flex gap-3">
|
||
<input
|
||
v-model="sermonDates[index]"
|
||
type="date"
|
||
class="flex-1 px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||
/>
|
||
<button
|
||
v-if="sermonDates.length > 1"
|
||
type="button"
|
||
@click="removeDate(index)"
|
||
class="px-3 py-2 bg-red-100 text-red-700 rounded-md hover:bg-red-200"
|
||
>
|
||
−
|
||
</button>
|
||
</div>
|
||
<button
|
||
type="button"
|
||
@click="addDate"
|
||
class="px-4 py-2 bg-blue-100 text-blue-700 rounded-md hover:bg-blue-200 font-medium"
|
||
>
|
||
+ Add Date
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="title" class="block text-sm font-medium text-gray-700 mb-2">
|
||
Sermon Title
|
||
</label>
|
||
<input
|
||
id="title"
|
||
v-model="formData.title"
|
||
type="text"
|
||
required
|
||
placeholder="Enter sermon title"
|
||
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Section 1: Bible References -->
|
||
<div class="border-t pt-6">
|
||
<h2 class="text-xl font-semibold text-gray-900 mb-4">Section 1: Bible References</h2>
|
||
<div class="space-y-6">
|
||
<div v-for="(ref, index) in bibleReferences" :key="index" class="border border-gray-200 rounded-lg p-4 space-y-3">
|
||
<div class="flex flex-col md:flex-row gap-3">
|
||
<div class="w-full md:w-64">
|
||
<label :for="`version-${index}`" class="block text-sm font-medium text-gray-700 mb-1">
|
||
Bible Version
|
||
</label>
|
||
<select
|
||
:id="`version-${index}`"
|
||
v-model="ref.version"
|
||
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||
>
|
||
<option value="ESV">English Standard Version (ESV)</option>
|
||
<option value="NKJV">New King James Version (NKJV)</option>
|
||
<option value="NIV">New International Version (NIV)</option>
|
||
<option value="KJV">King James Version (KJV)</option>
|
||
<option value="NLT">New Living Translation (NLT)</option>
|
||
<option value="NASB">New American Standard Bible (NASB)</option>
|
||
<option value="CSB">Christian Standard Bible (CSB)</option>
|
||
</select>
|
||
</div>
|
||
<div class="flex-1 flex gap-3">
|
||
<div class="flex-1">
|
||
<label :for="`reference-${index}`" class="block text-sm font-medium text-gray-700 mb-1">
|
||
Book & Verses
|
||
</label>
|
||
<input
|
||
:id="`reference-${index}`"
|
||
v-model="ref.reference"
|
||
type="text"
|
||
placeholder="e.g., John 3:16 or Romans 8:28-30"
|
||
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||
/>
|
||
</div>
|
||
<button
|
||
v-if="bibleReferences.length > 1"
|
||
type="button"
|
||
@click="removeReference(index)"
|
||
class="self-end px-3 py-2 bg-red-100 text-red-700 rounded-md hover:bg-red-200 md:block"
|
||
>
|
||
−
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label :for="`text-${index}`" class="block text-sm font-medium text-gray-700 mb-1">
|
||
Verse Text
|
||
</label>
|
||
<textarea
|
||
:id="`text-${index}`"
|
||
v-model="ref.text"
|
||
rows="3"
|
||
placeholder="Paste the verse text here..."
|
||
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||
></textarea>
|
||
</div>
|
||
</div>
|
||
<button
|
||
type="button"
|
||
@click="addReference"
|
||
class="px-4 py-2 bg-blue-100 text-blue-700 rounded-md hover:bg-blue-200 font-medium"
|
||
>
|
||
+ Add Reference
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Section 2: Personal Appliance -->
|
||
<div class="border-t pt-6">
|
||
<h2 class="text-xl font-semibold text-gray-900 mb-4">Section 2: Personal Appliance</h2>
|
||
<textarea
|
||
v-model="formData.personal_appliance"
|
||
rows="6"
|
||
required
|
||
placeholder="Enter personal appliance content..."
|
||
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||
></textarea>
|
||
</div>
|
||
|
||
<!-- Section 3: Pastor's Challenge -->
|
||
<div class="border-t pt-6">
|
||
<h2 class="text-xl font-semibold text-gray-900 mb-4">Section 3: Pastor's Challenge</h2>
|
||
<textarea
|
||
v-model="formData.pastors_challenge"
|
||
rows="6"
|
||
required
|
||
placeholder="Enter pastor's challenge content..."
|
||
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||
></textarea>
|
||
</div>
|
||
|
||
<!-- Worship Songs Section -->
|
||
<div class="border-t pt-6">
|
||
<h2 class="text-xl font-semibold text-gray-900 mb-4">Worship Songs</h2>
|
||
<p class="text-sm text-gray-600 mb-4">Add songs that were sung during this service (optional)</p>
|
||
<div class="space-y-4">
|
||
<div v-for="(song, index) in worshipSongs" :key="index" class="border border-gray-200 rounded-lg p-4 space-y-3">
|
||
<div class="flex flex-col md:flex-row gap-3">
|
||
<div class="flex-1">
|
||
<label :for="`song-name-${index}`" class="block text-sm font-medium text-gray-700 mb-1">
|
||
Song Name
|
||
</label>
|
||
<input
|
||
:id="`song-name-${index}`"
|
||
v-model="song.name"
|
||
type="text"
|
||
placeholder="e.g., Amazing Grace"
|
||
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||
/>
|
||
</div>
|
||
<div class="flex-1">
|
||
<label :for="`song-artist-${index}`" class="block text-sm font-medium text-gray-700 mb-1">
|
||
Artist/Composer
|
||
</label>
|
||
<input
|
||
:id="`song-artist-${index}`"
|
||
v-model="song.artist"
|
||
type="text"
|
||
placeholder="e.g., John Newton"
|
||
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||
/>
|
||
</div>
|
||
<button
|
||
v-if="worshipSongs.length > 1"
|
||
type="button"
|
||
@click="removeSong(index)"
|
||
class="self-end px-3 py-2 bg-red-100 text-red-700 rounded-md hover:bg-red-200"
|
||
>
|
||
−
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<button
|
||
type="button"
|
||
@click="addSong"
|
||
class="px-4 py-2 bg-blue-100 text-blue-700 rounded-md hover:bg-blue-200 font-medium"
|
||
>
|
||
+ Add Song
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Error/Success Messages -->
|
||
<div v-if="error" class="text-red-600 text-sm">
|
||
{{ error }}
|
||
</div>
|
||
<div v-if="success" class="text-green-600 text-sm">
|
||
{{ success }}
|
||
</div>
|
||
|
||
<!-- Submit Button -->
|
||
<div class="flex gap-4">
|
||
<button
|
||
type="submit"
|
||
:disabled="loading"
|
||
class="flex-1 py-3 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
|
||
>
|
||
{{ loading ? (editingSermonId ? 'Updating...' : 'Creating...') : (editingSermonId ? 'Update Sermon' : 'Create Sermon') }}
|
||
</button>
|
||
<NuxtLink
|
||
to="/"
|
||
class="px-6 py-3 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50"
|
||
>
|
||
Cancel
|
||
</NuxtLink>
|
||
</div>
|
||
</form>
|
||
|
||
<!-- Database Settings Section -->
|
||
<div class="bg-white shadow-lg rounded-lg p-8 mt-8">
|
||
<h2 class="text-2xl font-bold text-gray-900 mb-6">Database Settings</h2>
|
||
|
||
<div class="space-y-4">
|
||
<div>
|
||
<label for="retention-policy" class="block text-sm font-medium text-gray-700 mb-2">
|
||
Sermon Retention Policy
|
||
</label>
|
||
<p class="text-sm text-gray-600 mb-3">
|
||
Choose how long to keep sermons in the database. Older sermons will be automatically deleted based on this policy.
|
||
</p>
|
||
<select
|
||
id="retention-policy"
|
||
v-model="retentionPolicy"
|
||
@change="handleRetentionPolicyChange"
|
||
:disabled="savingRetentionPolicy"
|
||
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 disabled:opacity-50"
|
||
>
|
||
<option value="forever">Keep Forever</option>
|
||
<option value="1_month">1 Month</option>
|
||
<option value="3_months">3 Months</option>
|
||
<option value="6_months">6 Months</option>
|
||
<option value="1_year">1 Year</option>
|
||
<option value="3_years">3 Years</option>
|
||
<option value="5_years">5 Years</option>
|
||
<option value="10_years">10 Years</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div v-if="retentionPolicyError" class="text-red-600 text-sm">
|
||
{{ retentionPolicyError }}
|
||
</div>
|
||
<div v-if="retentionPolicySuccess" class="text-green-600 text-sm">
|
||
{{ retentionPolicySuccess }}
|
||
</div>
|
||
|
||
<div class="border-t pt-4 mt-4">
|
||
<button
|
||
@click="handleManualCleanup"
|
||
:disabled="cleaningUp"
|
||
class="px-6 py-2 bg-orange-600 text-white rounded-md hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-orange-500 disabled:opacity-50 disabled:cursor-not-allowed font-medium"
|
||
>
|
||
{{ cleaningUp ? 'Running Cleanup...' : 'Run Cleanup Now' }}
|
||
</button>
|
||
<p class="text-xs text-gray-500 mt-2">
|
||
Manually trigger the cleanup process to delete sermons according to the retention policy.
|
||
This will also run automatically on a daily schedule.
|
||
</p>
|
||
</div>
|
||
|
||
<div v-if="cleanupError" class="text-red-600 text-sm">
|
||
{{ cleanupError }}
|
||
</div>
|
||
<div v-if="cleanupSuccess" class="text-green-600 text-sm">
|
||
{{ cleanupSuccess }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<Footer />
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
definePageMeta({
|
||
middleware: 'auth'
|
||
})
|
||
|
||
// Check authentication status
|
||
const { data: authData } = await useFetch('/api/auth/verify')
|
||
const isAuthenticated = computed(() => authData.value?.authenticated || false)
|
||
const isAdmin = computed(() => authData.value?.isAdmin || false)
|
||
|
||
const route = useRoute()
|
||
|
||
// Fetch all sermons for management (including archived)
|
||
const { data: allSermons, refresh: refreshSermons } = await useFetch('/api/sermons?includeArchived=true')
|
||
|
||
// Sermon management state
|
||
const selectedSermonId = ref('')
|
||
const deleteError = ref('')
|
||
const deleteSuccess = ref('')
|
||
const deleting = ref(false)
|
||
const archiving = ref(false)
|
||
const archiveError = ref('')
|
||
const archiveSuccess = ref('')
|
||
|
||
// Create sermon form state
|
||
const editingSermonId = ref<number | null>(null)
|
||
const bibleReferences = ref([{ version: 'ESV', reference: '', text: '' }])
|
||
const sermonDates = ref([''])
|
||
const worshipSongs = ref([{ name: '', artist: '' }])
|
||
const formData = ref({
|
||
date: '',
|
||
title: '',
|
||
personal_appliance: '',
|
||
pastors_challenge: ''
|
||
})
|
||
const error = ref('')
|
||
const success = ref('')
|
||
const loading = ref(false)
|
||
|
||
// Database settings state
|
||
const retentionPolicy = ref('forever')
|
||
const savingRetentionPolicy = ref(false)
|
||
const retentionPolicyError = ref('')
|
||
const retentionPolicySuccess = ref('')
|
||
const cleaningUp = ref(false)
|
||
const cleanupError = ref('')
|
||
const cleanupSuccess = ref('')
|
||
|
||
// Fetch current retention policy
|
||
const { data: retentionPolicyData } = await useFetch('/api/settings/retention-policy')
|
||
if (retentionPolicyData.value) {
|
||
retentionPolicy.value = retentionPolicyData.value.retentionPolicy
|
||
}
|
||
|
||
// Check if selected sermon is archived
|
||
const isSelectedSermonArchived = computed(() => {
|
||
if (!selectedSermonId.value || !allSermons.value) return false
|
||
const sermon = allSermons.value.find((s: any) => s.id === parseInt(selectedSermonId.value))
|
||
return sermon?.archived === 1
|
||
})
|
||
|
||
function addReference() {
|
||
bibleReferences.value.push({ version: 'ESV', reference: '', text: '' })
|
||
}
|
||
|
||
function removeReference(index: number) {
|
||
bibleReferences.value.splice(index, 1)
|
||
}
|
||
|
||
function addDate() {
|
||
sermonDates.value.push('')
|
||
}
|
||
|
||
function removeDate(index: number) {
|
||
sermonDates.value.splice(index, 1)
|
||
}
|
||
|
||
function addSong() {
|
||
worshipSongs.value.push({ name: '', artist: '' })
|
||
}
|
||
|
||
function removeSong(index: number) {
|
||
worshipSongs.value.splice(index, 1)
|
||
}
|
||
|
||
function formatDateToSlug(date: string) {
|
||
// Convert YYYY-MM-DD to MMDDYYYY
|
||
const [year, month, day] = date.split('-')
|
||
return `sermon-${month}${day}${year}`
|
||
}
|
||
|
||
async function handleSubmit() {
|
||
error.value = ''
|
||
success.value = ''
|
||
loading.value = true
|
||
|
||
try {
|
||
const slug = formatDateToSlug(formData.value.date)
|
||
// Filter out empty references and serialize as JSON
|
||
const validReferences = bibleReferences.value.filter(ref =>
|
||
ref.version && ref.reference && ref.text
|
||
)
|
||
const bible_references = JSON.stringify(validReferences)
|
||
|
||
// Filter valid dates and serialize as JSON
|
||
const validDates = sermonDates.value.filter(d => d)
|
||
const dates = validDates.length > 0 ? JSON.stringify(validDates) : null
|
||
|
||
// Filter valid songs and serialize as JSON
|
||
const validSongs = worshipSongs.value.filter(s => s.name)
|
||
const worship_songs = validSongs.length > 0 ? JSON.stringify(validSongs) : null
|
||
|
||
const body = {
|
||
slug,
|
||
title: formData.value.title,
|
||
date: formData.value.date,
|
||
bible_references,
|
||
personal_appliance: formData.value.personal_appliance,
|
||
pastors_challenge: formData.value.pastors_challenge,
|
||
dates,
|
||
worship_songs
|
||
}
|
||
|
||
if (editingSermonId.value) {
|
||
// Update existing sermon
|
||
await $fetch(`/api/sermons/update/${editingSermonId.value}`, {
|
||
method: 'PUT',
|
||
body
|
||
})
|
||
success.value = 'Sermon updated successfully!'
|
||
|
||
// Refresh sermon list
|
||
await refreshSermons()
|
||
|
||
// Don't redirect or reset form when editing - stay on page with success message
|
||
} else {
|
||
// Create new sermon
|
||
await $fetch('/api/sermons', {
|
||
method: 'POST',
|
||
body
|
||
})
|
||
success.value = 'Sermon created successfully!'
|
||
|
||
// Reset form
|
||
editingSermonId.value = null
|
||
formData.value = {
|
||
date: '',
|
||
title: '',
|
||
personal_appliance: '',
|
||
pastors_challenge: ''
|
||
}
|
||
bibleReferences.value = [{ version: 'ESV', reference: '', text: '' }]
|
||
|
||
// Refresh sermon list
|
||
await refreshSermons()
|
||
|
||
// Redirect after 2 seconds
|
||
setTimeout(() => {
|
||
navigateTo('/')
|
||
}, 2000)
|
||
}
|
||
} catch (e: any) {
|
||
error.value = e.data?.message || `Failed to ${editingSermonId.value ? 'update' : 'create'} sermon`
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function cancelEdit() {
|
||
editingSermonId.value = null
|
||
formData.value = {
|
||
date: '',
|
||
title: '',
|
||
personal_appliance: '',
|
||
pastors_challenge: ''
|
||
}
|
||
bibleReferences.value = [{ version: 'ESV', reference: '', text: '' }]
|
||
sermonDates.value = ['']
|
||
worshipSongs.value = [{ name: '', artist: '' }]
|
||
}
|
||
|
||
function handleEdit() {
|
||
if (!selectedSermonId.value) return
|
||
|
||
const sermon = allSermons.value?.find((s: any) => s.id === parseInt(selectedSermonId.value))
|
||
if (!sermon) return
|
||
|
||
// Set editing mode
|
||
editingSermonId.value = sermon.id
|
||
|
||
// Load sermon data into form
|
||
formData.value = {
|
||
date: sermon.date,
|
||
title: sermon.title,
|
||
personal_appliance: sermon.personal_appliance,
|
||
pastors_challenge: sermon.pastors_challenge
|
||
}
|
||
|
||
// Parse and load Bible references
|
||
try {
|
||
bibleReferences.value = JSON.parse(sermon.bible_references)
|
||
} catch {
|
||
// Fallback for old format
|
||
bibleReferences.value = [{ version: 'ESV', reference: sermon.bible_references, text: '' }]
|
||
}
|
||
|
||
// Parse and load additional dates
|
||
try {
|
||
sermonDates.value = sermon.dates ? JSON.parse(sermon.dates) : ['']
|
||
} catch {
|
||
sermonDates.value = ['']
|
||
}
|
||
|
||
// Parse and load worship songs
|
||
try {
|
||
worshipSongs.value = sermon.worship_songs ? JSON.parse(sermon.worship_songs) : [{ name: '', artist: '' }]
|
||
} catch {
|
||
worshipSongs.value = [{ name: '', artist: '' }]
|
||
}
|
||
|
||
// Scroll to form (just below the management section)
|
||
const formElement = document.querySelector('form')
|
||
if (formElement) {
|
||
formElement.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||
}
|
||
}
|
||
|
||
async function handleArchive() {
|
||
if (!selectedSermonId.value) return
|
||
|
||
if (!confirm('Are you sure you want to archive this sermon? It will be hidden from the main page.')) {
|
||
return
|
||
}
|
||
|
||
archiveError.value = ''
|
||
archiveSuccess.value = ''
|
||
archiving.value = true
|
||
|
||
try {
|
||
await $fetch(`/api/sermons/archive/${selectedSermonId.value}`, {
|
||
method: 'POST'
|
||
})
|
||
|
||
archiveSuccess.value = 'Sermon archived successfully!'
|
||
selectedSermonId.value = ''
|
||
|
||
await refreshSermons()
|
||
} catch (e: any) {
|
||
archiveError.value = e.data?.message || 'Failed to archive sermon'
|
||
} finally {
|
||
archiving.value = false
|
||
}
|
||
}
|
||
|
||
async function handleUnarchive() {
|
||
if (!selectedSermonId.value) return
|
||
|
||
if (!confirm('Are you sure you want to unarchive this sermon? It will be visible on the main page again.')) {
|
||
return
|
||
}
|
||
|
||
archiveError.value = ''
|
||
archiveSuccess.value = ''
|
||
archiving.value = true
|
||
|
||
try {
|
||
await $fetch(`/api/sermons/unarchive/${selectedSermonId.value}`, {
|
||
method: 'POST'
|
||
})
|
||
|
||
archiveSuccess.value = 'Sermon unarchived successfully!'
|
||
selectedSermonId.value = ''
|
||
|
||
await refreshSermons()
|
||
} catch (e: any) {
|
||
archiveError.value = e.data?.message || 'Failed to unarchive sermon'
|
||
} finally {
|
||
archiving.value = false
|
||
}
|
||
}
|
||
|
||
async function handleDelete() {
|
||
if (!selectedSermonId.value) return
|
||
|
||
if (!confirm('Are you sure you want to permanently delete this sermon?\n\n⚠️ WARNING: Deleting this sermon will also delete everyone\'s sermon notes for this sermon. This action cannot be undone.')) {
|
||
return
|
||
}
|
||
|
||
deleteError.value = ''
|
||
deleteSuccess.value = ''
|
||
deleting.value = true
|
||
|
||
try {
|
||
await $fetch(`/api/sermons/delete/${selectedSermonId.value}`, {
|
||
method: 'DELETE'
|
||
})
|
||
|
||
deleteSuccess.value = 'Sermon deleted successfully!'
|
||
selectedSermonId.value = ''
|
||
|
||
// Refresh the sermon list
|
||
await refreshSermons()
|
||
} catch (e: any) {
|
||
deleteError.value = e.data?.message || 'Failed to delete sermon'
|
||
} finally {
|
||
deleting.value = false
|
||
}
|
||
}
|
||
|
||
function formatDate(dateString: string) {
|
||
// Add T00:00:00 to ensure the date is interpreted as local time, not UTC
|
||
const date = new Date(dateString + 'T00:00:00')
|
||
return date.toLocaleDateString('en-US', {
|
||
year: 'numeric',
|
||
month: 'long',
|
||
day: 'numeric'
|
||
})
|
||
}
|
||
|
||
async function handleLogout() {
|
||
await $fetch('/api/auth/logout', { method: 'POST' })
|
||
await navigateTo('/login')
|
||
}
|
||
|
||
async function handleRetentionPolicyChange() {
|
||
retentionPolicyError.value = ''
|
||
retentionPolicySuccess.value = ''
|
||
savingRetentionPolicy.value = true
|
||
|
||
try {
|
||
await $fetch('/api/settings/retention-policy', {
|
||
method: 'POST',
|
||
body: {
|
||
retentionPolicy: retentionPolicy.value
|
||
}
|
||
})
|
||
|
||
retentionPolicySuccess.value = 'Retention policy saved successfully!'
|
||
|
||
// Clear success message after 3 seconds
|
||
setTimeout(() => {
|
||
retentionPolicySuccess.value = ''
|
||
}, 3000)
|
||
} catch (e: any) {
|
||
retentionPolicyError.value = e.data?.message || 'Failed to save retention policy'
|
||
} finally {
|
||
savingRetentionPolicy.value = false
|
||
}
|
||
}
|
||
|
||
async function handleManualCleanup() {
|
||
if (!confirm('Are you sure you want to run the cleanup process?\n\nThis will permanently delete all sermons older than the retention policy. This action cannot be undone.')) {
|
||
return
|
||
}
|
||
|
||
cleanupError.value = ''
|
||
cleanupSuccess.value = ''
|
||
cleaningUp.value = true
|
||
|
||
try {
|
||
const result = await $fetch('/api/sermons/cleanup', {
|
||
method: 'POST'
|
||
})
|
||
|
||
cleanupSuccess.value = result.message + ` (${result.deletedCount} sermon${result.deletedCount !== 1 ? 's' : ''} deleted)`
|
||
|
||
// Refresh sermon list after cleanup
|
||
await refreshSermons()
|
||
} catch (e: any) {
|
||
cleanupError.value = e.data?.message || 'Failed to run cleanup'
|
||
} finally {
|
||
cleaningUp.value = false
|
||
}
|
||
}
|
||
</script>
|