Complete sermon itinerary application with Nuxt 3, SQLite, authentication, and Docker deployment

This commit is contained in:
2025-10-01 22:15:01 -04:00
parent dacaea6fa4
commit 1b282c05fe
26 changed files with 1245 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
<template>
<button
@click="showModal = true"
class="inline-flex items-center px-3 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm12 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z" />
</svg>
QR Code
</button>
<QRCodeModal
v-if="showModal"
:sermon="sermon"
@close="showModal = false"
/>
</template>
<script setup lang="ts">
interface Sermon {
id?: number
slug: string
title: string
date: string
bible_references: string
personal_appliance: string
pastors_challenge: string
}
defineProps<{
sermon: Sermon
}>()
const showModal = ref(false)
</script>

View File

@@ -0,0 +1,63 @@
<template>
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4" @click.self="$emit('close')">
<div class="bg-white rounded-lg shadow-xl max-w-md w-full p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-900">Scan to View Sermon</h3>
<button
@click="$emit('close')"
class="text-gray-400 hover:text-gray-600"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="flex flex-col items-center">
<div class="bg-white p-4 rounded-lg border-2 border-gray-200">
<canvas ref="qrCanvas"></canvas>
</div>
<p class="mt-4 text-sm text-gray-600 text-center">{{ sermon.title }}</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import QRCode from 'qrcode'
interface Sermon {
id?: number
slug: string
title: string
date: string
bible_references: string
personal_appliance: string
pastors_challenge: string
}
const props = defineProps<{
sermon: Sermon
}>()
defineEmits<{
close: []
}>()
const qrCanvas = ref<HTMLCanvasElement | null>(null)
const config = useRuntimeConfig()
onMounted(async () => {
if (qrCanvas.value) {
const url = `${config.public.siteUrl}/${props.sermon.slug}`
await QRCode.toCanvas(qrCanvas.value, url, {
width: 256,
margin: 2,
color: {
dark: '#000000',
light: '#FFFFFF'
}
})
}
})
</script>

44
components/SermonCard.vue Normal file
View File

@@ -0,0 +1,44 @@
<template>
<div class="bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow overflow-hidden">
<div class="p-6">
<h3 class="text-xl font-semibold text-gray-900 mb-2">{{ sermon.title }}</h3>
<p class="text-sm text-gray-500 mb-4">{{ formatDate(sermon.date) }}</p>
<div class="flex items-center justify-between">
<NuxtLink
:to="`/${sermon.slug}`"
class="text-blue-600 hover:text-blue-700 font-medium text-sm"
>
View Sermon
</NuxtLink>
<QRCodeButton :sermon="sermon" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
interface Sermon {
id?: number
slug: string
title: string
date: string
bible_references: string
personal_appliance: string
pastors_challenge: string
}
defineProps<{
sermon: Sermon
}>()
function formatDate(dateString: string) {
const date = new Date(dateString)
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})
}
</script>