Enhance application structure: Add runtime configuration for environment variables, integrate new dependencies for Cloudinary and UI components, and refactor member management features including improved forms and member dashboard. Update styles and layout for better user experience.
This commit is contained in:
parent
6e7e27ac4e
commit
e4a0a9ab0f
61 changed files with 7902 additions and 950 deletions
220
app/layouts/admin.vue
Normal file
220
app/layouts/admin.vue
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
<template>
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<!-- Admin Navigation -->
|
||||
<nav class="bg-white border-b border-gray-200 shadow-sm">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between items-center py-4">
|
||||
<div class="flex items-center gap-8">
|
||||
<NuxtLink to="/" class="text-xl font-bold text-gray-900 hover:text-blue-600">
|
||||
Ghost Guild
|
||||
</NuxtLink>
|
||||
|
||||
<div class="hidden md:flex items-center gap-1">
|
||||
<NuxtLink
|
||||
to="/admin"
|
||||
:class="[
|
||||
'px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200',
|
||||
$route.path === '/admin'
|
||||
? 'bg-blue-100 text-blue-700 shadow-sm'
|
||||
: 'text-gray-600 hover:text-blue-600 hover:bg-blue-50'
|
||||
]"
|
||||
>
|
||||
<svg class="w-4 h-4 inline-block mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2 2z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3a2 2 0 012-2h4a2 2 0 012 2v4m-6 4h4"/>
|
||||
</svg>
|
||||
Dashboard
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink
|
||||
to="/admin/members"
|
||||
:class="[
|
||||
'px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200',
|
||||
$route.path.includes('/admin/members')
|
||||
? 'bg-blue-100 text-blue-700 shadow-sm'
|
||||
: 'text-gray-600 hover:text-blue-600 hover:bg-blue-50'
|
||||
]"
|
||||
>
|
||||
<svg class="w-4 h-4 inline-block mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"/>
|
||||
</svg>
|
||||
Members
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink
|
||||
to="/admin/events"
|
||||
:class="[
|
||||
'px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200',
|
||||
$route.path.includes('/admin/events')
|
||||
? 'bg-blue-100 text-blue-700 shadow-sm'
|
||||
: 'text-gray-600 hover:text-blue-600 hover:bg-blue-50'
|
||||
]"
|
||||
>
|
||||
<svg class="w-4 h-4 inline-block mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
Events
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink
|
||||
to="/admin/analytics"
|
||||
:class="[
|
||||
'px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200',
|
||||
$route.path.includes('/admin/analytics')
|
||||
? 'bg-blue-100 text-blue-700 shadow-sm'
|
||||
: 'text-gray-600 hover:text-blue-600 hover:bg-blue-50'
|
||||
]"
|
||||
>
|
||||
<svg class="w-4 h-4 inline-block mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
Analytics
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<!-- User Menu -->
|
||||
<div class="relative" @click="showUserMenu = !showUserMenu" v-click-outside="() => showUserMenu = false">
|
||||
<button class="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-gray-100 cursor-pointer transition-colors">
|
||||
<div class="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center">
|
||||
<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="hidden md:block text-sm font-medium text-gray-700">Admin</span>
|
||||
<svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- User Menu Dropdown -->
|
||||
<div v-if="showUserMenu" class="absolute right-0 mt-2 w-56 bg-white rounded-lg shadow-lg border border-gray-200 py-1 z-50">
|
||||
<NuxtLink to="/" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||||
<svg class="w-4 h-4 mr-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
||||
</svg>
|
||||
View Site
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/admin/settings" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||||
<svg class="w-4 h-4 mr-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
</svg>
|
||||
Settings
|
||||
</NuxtLink>
|
||||
<hr class="my-1 border-gray-200">
|
||||
<button @click="logout" class="flex items-center w-full px-4 py-2 text-sm text-red-600 hover:bg-red-50">
|
||||
<svg class="w-4 h-4 mr-3 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/>
|
||||
</svg>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Mobile Navigation -->
|
||||
<div class="md:hidden bg-white border-b border-gray-200">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 py-3 overflow-x-auto">
|
||||
<NuxtLink
|
||||
to="/admin"
|
||||
:class="[
|
||||
'px-3 py-2 rounded-lg text-sm font-medium whitespace-nowrap transition-colors',
|
||||
$route.path === '/admin'
|
||||
? 'bg-blue-100 text-blue-700'
|
||||
: 'text-gray-600 hover:text-blue-600 hover:bg-blue-50'
|
||||
]"
|
||||
>
|
||||
Dashboard
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink
|
||||
to="/admin/members"
|
||||
:class="[
|
||||
'px-3 py-2 rounded-lg text-sm font-medium whitespace-nowrap transition-colors',
|
||||
$route.path.includes('/admin/members')
|
||||
? 'bg-blue-100 text-blue-700'
|
||||
: 'text-gray-600 hover:text-blue-600 hover:bg-blue-50'
|
||||
]"
|
||||
>
|
||||
Members
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink
|
||||
to="/admin/events"
|
||||
:class="[
|
||||
'px-3 py-2 rounded-lg text-sm font-medium whitespace-nowrap transition-colors',
|
||||
$route.path.includes('/admin/events')
|
||||
? 'bg-blue-100 text-blue-700'
|
||||
: 'text-gray-600 hover:text-blue-600 hover:bg-blue-50'
|
||||
]"
|
||||
>
|
||||
Events
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink
|
||||
to="/admin/analytics"
|
||||
:class="[
|
||||
'px-3 py-2 rounded-lg text-sm font-medium whitespace-nowrap transition-colors',
|
||||
$route.path.includes('/admin/analytics')
|
||||
? 'bg-blue-100 text-blue-700'
|
||||
: 'text-gray-600 hover:text-blue-600 hover:bg-blue-50'
|
||||
]"
|
||||
>
|
||||
Analytics
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Page Content -->
|
||||
<main>
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-white border-t border-gray-200 mt-auto">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="py-8 text-center">
|
||||
<p class="text-sm text-gray-600">
|
||||
© 2025 Ghost Guild. Admin Panel.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const showUserMenu = ref(false)
|
||||
|
||||
// Close user menu when clicking outside
|
||||
const vClickOutside = {
|
||||
beforeMount(el, binding) {
|
||||
el.clickOutsideEvent = (event) => {
|
||||
if (!(el === event.target || el.contains(event.target))) {
|
||||
binding.value()
|
||||
}
|
||||
}
|
||||
document.addEventListener('click', el.clickOutsideEvent)
|
||||
},
|
||||
unmounted(el) {
|
||||
document.removeEventListener('click', el.clickOutsideEvent)
|
||||
}
|
||||
}
|
||||
|
||||
const logout = async () => {
|
||||
try {
|
||||
await $fetch('/api/auth/logout', { method: 'POST' })
|
||||
await navigateTo('/login')
|
||||
} catch (error) {
|
||||
console.error('Logout failed:', error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
7
app/layouts/default.vue
Normal file
7
app/layouts/default.vue
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<AppNavigation />
|
||||
<slot />
|
||||
<AppFooter />
|
||||
</div>
|
||||
</template>
|
||||
Loading…
Add table
Add a link
Reference in a new issue