Initial commit

This commit is contained in:
2026-02-03 10:38:46 +01:00
commit eb304f4b14
144 changed files with 22605 additions and 0 deletions

View File

@@ -0,0 +1,193 @@
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useAuth } from '../composables/useAuth'
const router = useRouter()
const route = useRoute()
const { user, logout } = useAuth()
const isScrolled = ref(false)
function onScroll() {
isScrolled.value = window.scrollY > 0
}
onMounted(() => {
window.addEventListener('scroll', onScroll, { passive: true })
onScroll()
})
onBeforeUnmount(() => {
window.removeEventListener('scroll', onScroll)
})
const pageTitle = computed(() => {
const name = route.name?.toString() ?? ''
if (name === 'events') return 'Events'
if (name === 'event-create') return 'Create Event'
if (name === 'event-edit') return 'Edit Event'
if (name === 'event-uploads') return 'Event Uploads'
return 'Admin'
})
async function handleLogout() {
await logout()
router.push('/login')
}
</script>
<template>
<div class="admin-layout">
<aside class="admin-sidebar">
<div class="sidebar-brand">
<span class="brand-icon"></span>
<span class="brand-text">Event Uploader</span>
</div>
<nav class="sidebar-nav">
<router-link to="/" class="nav-item" :class="{ active: route.name === 'events' }">
<span class="nav-icon"></span>
<span>Events</span>
</router-link>
<router-link to="/events/create" class="nav-item" :class="{ active: route.name === 'event-create' }">
<span class="nav-icon">+</span>
<span>Create Event</span>
</router-link>
</nav>
<div class="sidebar-footer">
<a
href="#"
class="nav-item"
@click.prevent="handleLogout"
>
<span class="nav-icon"></span>
<span>Logout</span>
</a>
</div>
</aside>
<main class="admin-main">
<header class="main-header" :class="{ 'main-header--scrolled': isScrolled }">
<h1 class="page-title">{{ pageTitle }}</h1>
<div class="header-actions">
<span class="user-email">{{ user?.email }}</span>
</div>
</header>
<div class="main-content">
<router-view />
</div>
</main>
</div>
</template>
<style scoped>
.admin-layout {
display: flex;
min-height: 100vh;
background: var(--admin-bg);
}
.admin-sidebar {
width: 260px;
flex-shrink: 0;
background: var(--admin-sidebar-bg);
color: var(--admin-sidebar-text);
display: flex;
flex-direction: column;
border-right: 1px solid var(--admin-sidebar-border);
}
.sidebar-brand {
padding: 1.5rem 1.25rem;
display: flex;
align-items: center;
gap: 0.75rem;
border-bottom: 1px solid var(--admin-sidebar-border);
}
.brand-icon {
font-size: 1.5rem;
opacity: 0.9;
}
.brand-text {
font-weight: 600;
font-size: 1.1rem;
letter-spacing: -0.02em;
}
.sidebar-nav {
flex: 1;
padding: 1rem 0.75rem;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.nav-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.625rem 1rem;
border-radius: var(--admin-radius);
color: var(--admin-sidebar-text-muted);
text-decoration: none;
font-size: 0.9375rem;
font-weight: 500;
transition: background 0.15s, color 0.15s;
}
.nav-item:hover {
background: var(--admin-sidebar-hover);
color: var(--admin-sidebar-text);
}
.nav-item.active {
background: var(--admin-sidebar-active);
color: var(--admin-primary);
}
.nav-icon {
font-size: 1rem;
width: 1.25rem;
text-align: center;
opacity: 0.8;
}
.sidebar-footer {
padding: 1rem 0.75rem;
border-top: 1px solid var(--admin-sidebar-border);
}
.main-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.25rem 1.5rem;
background: var(--admin-surface);
border-bottom: 1px solid var(--admin-border);
position: sticky;
top: 0;
z-index: 10;
transition: box-shadow 0.15s ease;
}
.main-header--scrolled {
box-shadow: var(--admin-shadow);
}
.page-title {
margin: 0;
font-size: 1.375rem;
font-weight: 600;
color: var(--admin-heading);
letter-spacing: -0.02em;
}
.user-email {
font-size: 0.875rem;
color: var(--admin-muted);
}
.main-content {
padding: var(--admin-space-8);
flex: 1;
}
</style>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
defineProps<{
message?: string
}>()
</script>
<template>
<div class="admin-card admin-card-body text-center py-5">
<div class="spinner-border text-primary" role="status" aria-label="Loading">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3 mb-0 text-muted">{{ message ?? 'Loading...' }}</p>
</div>
</template>

View File

@@ -0,0 +1,41 @@
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>