2026.06.04 frontend építés közben
This commit is contained in:
412
frontend_old/views/AdminDashboard.vue
Normal file
412
frontend_old/views/AdminDashboard.vue
Normal file
@@ -0,0 +1,412 @@
|
||||
<template>
|
||||
<div class="admin-dashboard">
|
||||
<!-- Page Header -->
|
||||
<div class="mb-8">
|
||||
<h1 class="text-3xl font-bold mb-2">Admin Dashboard</h1>
|
||||
<p class="text-gray-400">Monitor system health, manage translations, and oversee platform operations.</p>
|
||||
</div>
|
||||
|
||||
<!-- Stats Cards -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
<div class="bg-gray-800/50 rounded-xl border border-gray-700 p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-sm text-gray-400 mb-1">Total Users</div>
|
||||
<div class="text-3xl font-bold">{{ stats.totalUsers }}</div>
|
||||
</div>
|
||||
<div class="w-12 h-12 rounded-lg bg-blue-900/30 flex items-center justify-center">
|
||||
<span class="text-2xl">👥</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 text-sm text-gray-400">
|
||||
<span class="text-green-400">+{{ stats.newUsersToday }} today</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-800/50 rounded-xl border border-gray-700 p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-sm text-gray-400 mb-1">Active Sessions</div>
|
||||
<div class="text-3xl font-bold">{{ stats.activeSessions }}</div>
|
||||
</div>
|
||||
<div class="w-12 h-12 rounded-lg bg-green-900/30 flex items-center justify-center">
|
||||
<span class="text-2xl">🔌</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 text-sm text-gray-400">
|
||||
<span class="text-green-400">{{ stats.sessionGrowth }}% growth</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-800/50 rounded-xl border border-gray-700 p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-sm text-gray-400 mb-1">Vehicles Catalog</div>
|
||||
<div class="text-3xl font-bold">{{ stats.vehicleCount }}</div>
|
||||
</div>
|
||||
<div class="w-12 h-12 rounded-lg bg-purple-900/30 flex items-center justify-center">
|
||||
<span class="text-2xl">🚗</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 text-sm text-gray-400">
|
||||
<span class="text-blue-400">{{ stats.pendingVerification }} pending</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-800/50 rounded-xl border border-gray-700 p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-sm text-gray-400 mb-1">API Health</div>
|
||||
<div class="text-3xl font-bold">{{ stats.apiLatency }}ms</div>
|
||||
</div>
|
||||
<div class="w-12 h-12 rounded-lg bg-yellow-900/30 flex items-center justify-center">
|
||||
<span class="text-2xl">⚡</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 text-sm text-gray-400">
|
||||
<span :class="stats.apiStatus === 'healthy' ? 'text-green-400' : 'text-red-400'">
|
||||
{{ stats.apiStatus }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Two Column Layout -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<!-- i18n Translations Manager -->
|
||||
<div class="bg-gray-800/50 rounded-xl border border-gray-700 p-6">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h2 class="text-xl font-bold">i18n Translation Manager</h2>
|
||||
<div class="flex space-x-2">
|
||||
<button
|
||||
class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg transition-colors"
|
||||
@click="refreshTranslations"
|
||||
>
|
||||
Refresh
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors"
|
||||
@click="showAddModal = true"
|
||||
>
|
||||
Add Key
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Language Selector -->
|
||||
<div class="mb-6">
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">Select Language</label>
|
||||
<div class="flex space-x-2">
|
||||
<button
|
||||
v-for="lang in languages"
|
||||
:key="lang.code"
|
||||
class="px-4 py-2 rounded-lg transition-colors"
|
||||
:class="selectedLanguage === lang.code
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-gray-700 text-gray-300 hover:bg-gray-600'"
|
||||
@click="selectedLanguage = lang.code"
|
||||
>
|
||||
{{ lang.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Translations Table -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr class="border-b border-gray-700">
|
||||
<th class="text-left py-3 px-4 text-sm font-medium text-gray-400">Key</th>
|
||||
<th class="text-left py-3 px-4 text-sm font-medium text-gray-400">Translation</th>
|
||||
<th class="text-left py-3 px-4 text-sm font-medium text-gray-400">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(translation, key) in filteredTranslations"
|
||||
:key="key"
|
||||
class="border-b border-gray-800 hover:bg-gray-800/30 transition-colors"
|
||||
>
|
||||
<td class="py-3 px-4 font-mono text-sm">{{ key }}</td>
|
||||
<td class="py-3 px-4">
|
||||
<input
|
||||
v-model="translationEdits[key]"
|
||||
class="w-full bg-gray-900 border border-gray-700 rounded px-3 py-2 text-sm"
|
||||
:placeholder="translation"
|
||||
@change="saveTranslation(key, translationEdits[key])"
|
||||
/>
|
||||
</td>
|
||||
<td class="py-3 px-4">
|
||||
<button
|
||||
class="px-3 py-1 bg-red-900/30 hover:bg-red-900/50 text-red-400 rounded text-sm"
|
||||
@click="deleteTranslation(key)"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div v-if="Object.keys(filteredTranslations).length === 0" class="text-center py-8 text-gray-500">
|
||||
No translations found for {{ selectedLanguage.toUpperCase() }}.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- System Activity & Quick Actions -->
|
||||
<div class="space-y-8">
|
||||
<!-- Recent Activity -->
|
||||
<div class="bg-gray-800/50 rounded-xl border border-gray-700 p-6">
|
||||
<h2 class="text-xl font-bold mb-6">Recent System Activity</h2>
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
v-for="activity in recentActivity"
|
||||
:key="activity.id"
|
||||
class="flex items-start space-x-4 p-3 rounded-lg hover:bg-gray-800/30 transition-colors"
|
||||
>
|
||||
<div class="w-10 h-10 rounded-full bg-gray-700 flex items-center justify-center">
|
||||
<span class="text-lg">{{ activity.icon }}</span>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium">{{ activity.title }}</div>
|
||||
<div class="text-sm text-gray-400">{{ activity.description }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">{{ activity.time }}</div>
|
||||
</div>
|
||||
<div :class="`px-3 py-1 rounded-full text-xs ${activity.statusClass}`">
|
||||
{{ activity.status }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions -->
|
||||
<div class="bg-gray-800/50 rounded-xl border border-gray-700 p-6">
|
||||
<h2 class="text-xl font-bold mb-6">Quick Actions</h2>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<button
|
||||
class="p-4 bg-gray-700 hover:bg-gray-600 rounded-xl transition-colors text-left"
|
||||
@click="runSystemAudit"
|
||||
>
|
||||
<div class="text-2xl mb-2">🔍</div>
|
||||
<div class="font-medium">Run System Audit</div>
|
||||
<div class="text-sm text-gray-400">Check for inconsistencies</div>
|
||||
</button>
|
||||
<button
|
||||
class="p-4 bg-gray-700 hover:bg-gray-600 rounded-xl transition-colors text-left"
|
||||
@click="clearCache"
|
||||
>
|
||||
<div class="text-2xl mb-2">🧹</div>
|
||||
<div class="font-medium">Clear Cache</div>
|
||||
<div class="text-sm text-gray-400">Refresh all caches</div>
|
||||
</button>
|
||||
<button
|
||||
class="p-4 bg-gray-700 hover:bg-gray-600 rounded-xl transition-colors text-left"
|
||||
@click="exportData"
|
||||
>
|
||||
<div class="text-2xl mb-2">📤</div>
|
||||
<div class="font-medium">Export Data</div>
|
||||
<div class="text-sm text-gray-400">Backup database</div>
|
||||
</button>
|
||||
<button
|
||||
class="p-4 bg-gray-700 hover:bg-gray-600 rounded-xl transition-colors text-left"
|
||||
@click="showUserImpersonation = true"
|
||||
>
|
||||
<div class="text-2xl mb-2">👤</div>
|
||||
<div class="font-medium">Impersonate User</div>
|
||||
<div class="text-sm text-gray-400">Test as another user</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Translation Modal -->
|
||||
<div v-if="showAddModal" class="fixed inset-0 bg-black/70 flex items-center justify-center z-50">
|
||||
<div class="bg-gray-800 rounded-xl border border-gray-700 p-6 w-full max-w-md">
|
||||
<h3 class="text-xl font-bold mb-4">Add Translation Key</h3>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">Key</label>
|
||||
<input
|
||||
v-model="newKey"
|
||||
class="w-full bg-gray-900 border border-gray-700 rounded px-3 py-2"
|
||||
placeholder="e.g., dashboard.welcome"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">Value</label>
|
||||
<textarea
|
||||
v-model="newValue"
|
||||
class="w-full bg-gray-900 border border-gray-700 rounded px-3 py-2"
|
||||
rows="3"
|
||||
placeholder="Translation text"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-end space-x-3">
|
||||
<button
|
||||
class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg"
|
||||
@click="showAddModal = false"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg"
|
||||
@click="addTranslation"
|
||||
>
|
||||
Add Translation
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
|
||||
// Stats data
|
||||
const stats = ref({
|
||||
totalUsers: 1247,
|
||||
newUsersToday: 12,
|
||||
activeSessions: 89,
|
||||
sessionGrowth: 4.2,
|
||||
vehicleCount: 8563,
|
||||
pendingVerification: 42,
|
||||
apiLatency: 42,
|
||||
apiStatus: 'healthy' as 'healthy' | 'degraded' | 'down'
|
||||
})
|
||||
|
||||
// i18n translations
|
||||
const languages = ref([
|
||||
{ code: 'hu', name: 'Hungarian' },
|
||||
{ code: 'en', name: 'English' },
|
||||
{ code: 'de', name: 'German' },
|
||||
{ code: 'fr', name: 'French' }
|
||||
])
|
||||
|
||||
const selectedLanguage = ref('hu')
|
||||
const translations = ref<Record<string, string>>({
|
||||
'dashboard.welcome': 'Üdvözöljük az admin felületen',
|
||||
'dashboard.stats': 'Statisztikák',
|
||||
'users.total': 'Összes felhasználó',
|
||||
'vehicles.catalog': 'Jármű katalógus',
|
||||
'system.health': 'Rendszer állapot',
|
||||
'navigation.home': 'Kezdőlap',
|
||||
'navigation.admin': 'Adminisztráció',
|
||||
'button.save': 'Mentés',
|
||||
'button.cancel': 'Mégse',
|
||||
'error.not_found': 'Nem található',
|
||||
'success.updated': 'Sikeresen frissítve'
|
||||
})
|
||||
|
||||
const translationEdits = ref<Record<string, string>>({})
|
||||
|
||||
// Filter translations for selected language (simulated)
|
||||
const filteredTranslations = computed(() => {
|
||||
// In a real app, this would fetch from backend API
|
||||
// For now, we'll just return all translations
|
||||
return translations.value
|
||||
})
|
||||
|
||||
// Recent activity
|
||||
const recentActivity = ref([
|
||||
{
|
||||
id: 1,
|
||||
icon: '👤',
|
||||
title: 'New user registration',
|
||||
description: 'John Doe registered via email',
|
||||
time: '5 minutes ago',
|
||||
status: 'Completed',
|
||||
statusClass: 'bg-green-900/30 text-green-400'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: '🚗',
|
||||
title: 'Vehicle catalog update',
|
||||
description: 'Robot-1-GB added 12 new vehicle models',
|
||||
time: '15 minutes ago',
|
||||
status: 'Processing',
|
||||
statusClass: 'bg-blue-900/30 text-blue-400'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: '🔧',
|
||||
title: 'System maintenance',
|
||||
description: 'Database backup completed',
|
||||
time: '1 hour ago',
|
||||
status: 'Completed',
|
||||
statusClass: 'bg-green-900/30 text-green-400'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
icon: '⚠️',
|
||||
title: 'API rate limit warning',
|
||||
description: 'DVLA API approaching daily limit',
|
||||
time: '2 hours ago',
|
||||
status: 'Warning',
|
||||
statusClass: 'bg-yellow-900/30 text-yellow-400'
|
||||
}
|
||||
])
|
||||
|
||||
// Modal state
|
||||
const showAddModal = ref(false)
|
||||
const newKey = ref('')
|
||||
const newValue = ref('')
|
||||
const showUserImpersonation = ref(false)
|
||||
|
||||
// Methods
|
||||
function refreshTranslations() {
|
||||
// Simulate API call
|
||||
console.log('Refreshing translations for', selectedLanguage.value)
|
||||
// In real app: fetch from /api/v1/translations/hu
|
||||
}
|
||||
|
||||
function saveTranslation(key: string, value: string) {
|
||||
translations.value[key] = value
|
||||
console.log('Saving translation:', key, value)
|
||||
// In real app: POST to backend
|
||||
}
|
||||
|
||||
function deleteTranslation(key: string) {
|
||||
if (confirm(`Delete translation key "${key}"?`)) {
|
||||
delete translations.value[key]
|
||||
console.log('Deleted translation:', key)
|
||||
}
|
||||
}
|
||||
|
||||
function addTranslation() {
|
||||
if (newKey.value && newValue.value) {
|
||||
translations.value[newKey.value] = newValue.value
|
||||
newKey.value = ''
|
||||
newValue.value = ''
|
||||
showAddModal.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function runSystemAudit() {
|
||||
alert('System audit started. Check logs for results.')
|
||||
}
|
||||
|
||||
function clearCache() {
|
||||
alert('Cache cleared successfully.')
|
||||
}
|
||||
|
||||
function exportData() {
|
||||
alert('Data export initiated. Download will start shortly.')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// Initialize translation edits
|
||||
Object.keys(translations.value).forEach(key => {
|
||||
translationEdits.value[key] = translations.value[key]
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.admin-dashboard {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user