201 előtti mentés

This commit is contained in:
Roo
2026-03-26 07:09:44 +00:00
parent 89668a9beb
commit 03258db091
124 changed files with 13619 additions and 13347 deletions

View File

@@ -1,7 +1,7 @@
<template>
<div class="max-w-md mx-auto bg-white p-8 rounded-2xl shadow-xl border border-gray-100 mt-10">
<h2 class="text-2xl font-bold text-gray-800 mb-6 flex items-center gap-2">
<span>💸</span> Új költség rögzítése
<span>💸</span> Add Expense
</h2>
<form @submit.prevent="handleSubmit" class="space-y-4">
@@ -47,7 +47,7 @@ const form = ref({
const handleSubmit = async () => {
try {
await axios.post('http://localhost:8000/api/v1/expenses/add', form.value)
await axios.post(`${import.meta.env.VITE_API_BASE_URL || 'https://dev.servicefinder.hu'}/api/v1/expenses/add`, form.value)
alert("Sikeresen mentve!")
} catch (err) {
alert("Hiba történt a mentéskor.")

View File

@@ -1,14 +1,21 @@
<script setup>
import { ref, onMounted } from 'vue'
import { ref, onMounted, computed } from 'vue'
import axios from 'axios'
import VehicleShowcase from '@/components/garage/VehicleShowcase.vue'
import AchievementShowcase from '@/components/gamification/AchievementShowcase.vue'
import AnalyticsDashboard from '@/components/analytics/AnalyticsDashboard.vue'
import { useThemeStore } from '@/stores/themeStore'
const report = ref(null)
const loading = ref(true)
const themeStore = useThemeStore()
const themeClasses = computed(() => themeStore.themeClasses)
onMounted(async () => {
const token = localStorage.getItem('token')
try {
const res = await axios.get('http://localhost:8000/api/v1/reports/summary/latest', {
const res = await axios.get(`${import.meta.env.VITE_API_BASE_URL || 'https://dev.servicefinder.hu'}/api/v1/reports/summary/latest`, {
headers: { Authorization: `Bearer ${token}` }
})
report.value = res.data
@@ -21,13 +28,116 @@ onMounted(async () => {
</script>
<template>
<div v-if="!loading">
<div v-if="report" class="space-y-6">
<div v-if="!loading" :class="['space-y-8 p-6 min-h-screen transition-all duration-500', themeClasses.background]">
<!-- Vehicle Showcase (Dual-UI) -->
<VehicleShowcase />
<!-- Gamification Showcase (Dual-UI) -->
<div class="backdrop-blur-md bg-white/80 rounded-2xl shadow-xl p-6 border border-slate-200/60 transition-all duration-300 hover:shadow-2xl hover:border-slate-300/80">
<AchievementShowcase />
</div>
<!-- Analytics & TCO Dashboard (EPIC 11 - Ticket #126) -->
<div class="backdrop-blur-md bg-gradient-to-br from-white to-blue-50/80 rounded-2xl shadow-xl p-6 border border-blue-200/60 transition-all duration-300 hover:shadow-2xl hover:border-blue-300/80">
<AnalyticsDashboard />
</div>
<!-- Existing Report Section (if data exists) -->
<div v-if="report" class="backdrop-blur-md bg-white/90 rounded-2xl shadow-lg p-6 border border-slate-200/60 transition-all duration-300">
<h2 class="text-2xl font-bold text-slate-900 mb-6">Financial Summary</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="bg-gradient-to-br from-blue-50/80 to-blue-100/50 p-5 rounded-xl border border-blue-200/40 transition-all duration-200 hover:scale-[1.02] hover:shadow-md">
<div class="text-sm text-blue-700 font-medium">Total Expenses</div>
<div class="text-2xl font-bold text-slate-900">{{ report.total_expenses?.toLocaleString() || '0' }}</div>
</div>
<div v-else class="text-center mt-20">
<span class="text-6xl text-gray-300">📭</span>
<h2 class="text-xl font-bold text-gray-500 mt-4">Még nincsenek rögzített költségeid.</h2>
<router-link to="/expenses" class="mt-4 inline-block text-blue-700 font-bold">Kezdd el itt! </router-link>
</div>
<div class="bg-gradient-to-br from-emerald-50/80 to-emerald-100/50 p-5 rounded-xl border border-emerald-200/40 transition-all duration-200 hover:scale-[1.02] hover:shadow-md">
<div class="text-sm text-emerald-700 font-medium">Monthly Average</div>
<div class="text-2xl font-bold text-slate-900">{{ report.monthly_average?.toLocaleString() || '0' }}</div>
</div>
<div class="bg-gradient-to-br from-violet-50/80 to-violet-100/50 p-5 rounded-xl border border-violet-200/40 transition-all duration-200 hover:scale-[1.02] hover:shadow-md">
<div class="text-sm text-violet-700 font-medium">Vehicles Tracked</div>
<div class="text-2xl font-bold text-slate-900">{{ report.vehicle_count || '0' }}</div>
</div>
</div>
</div>
<!-- Empty State (no report data) -->
<div v-else class="text-center py-12 bg-gradient-to-br from-slate-50/80 to-white rounded-2xl border border-slate-200/60 backdrop-blur-sm">
<span class="text-6xl text-slate-300">📊</span>
<h2 class="text-xl font-bold text-slate-500 mt-4">Még nincsenek rögzített költségeid.</h2>
<p class="text-slate-600 mt-2 mb-6">Start tracking your vehicle expenses to see insights here.</p>
<router-link
to="/expenses"
class="inline-block px-6 py-3 bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 text-white font-semibold rounded-lg transition-all duration-200 active:scale-95 shadow-md hover:shadow-lg"
>
Kezdd el itt!
</router-link>
</div>
<!-- Quick Actions -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<router-link
to="/expenses/add"
class="backdrop-blur-sm bg-white/90 p-6 rounded-xl shadow-sm border border-slate-200/60 hover:shadow-lg transition-all duration-200 hover:scale-[1.02] active:scale-95 group"
>
<div class="flex items-center">
<div class="p-3 bg-gradient-to-br from-blue-100 to-blue-200 rounded-lg mr-4 group-hover:from-blue-200 group-hover:to-blue-300 transition-all duration-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
</div>
<div>
<h3 class="font-bold text-slate-900">Add Expense</h3>
<p class="text-slate-600 text-sm">Record fuel, maintenance, etc.</p>
</div>
</div>
</router-link>
<router-link
to="/vehicles/add"
class="backdrop-blur-sm bg-white/90 p-6 rounded-xl shadow-sm border border-slate-200/60 hover:shadow-lg transition-all duration-200 hover:scale-[1.02] active:scale-95 group"
>
<div class="flex items-center">
<div class="p-3 bg-gradient-to-br from-emerald-100 to-emerald-200 rounded-lg mr-4 group-hover:from-emerald-200 group-hover:to-emerald-300 transition-all duration-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
</div>
<div>
<h3 class="font-bold text-slate-900">Add Vehicle</h3>
<p class="text-slate-600 text-sm">Register a new vehicle</p>
</div>
</div>
</router-link>
<router-link
to="/expenses"
class="backdrop-blur-sm bg-white/90 p-6 rounded-xl shadow-sm border border-slate-200/60 hover:shadow-lg transition-all duration-200 hover:scale-[1.02] active:scale-95 group"
>
<div class="flex items-center">
<div class="p-3 bg-gradient-to-br from-violet-100 to-violet-200 rounded-lg mr-4 group-hover:from-violet-200 group-hover:to-violet-300 transition-all duration-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-violet-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<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>
</div>
<div>
<h3 class="font-bold text-slate-900">View Reports</h3>
<p class="text-slate-600 text-sm">Analytics and insights</p>
</div>
</div>
</router-link>
</div>
</div>
</template>
<!-- Loading State -->
<div v-else class="flex justify-center items-center h-64">
<div class="text-center">
<div class="inline-block animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-600"></div>
<p class="mt-4 text-slate-600">Loading dashboard...</p>
</div>
</div>
</template>
<style scoped>
/* Custom styles */
</style>

View File

@@ -1,45 +1,147 @@
<template>
<div class="max-w-md mx-auto mt-20 p-8 bg-white rounded-3xl shadow-2xl border border-gray-100">
<div class="text-center mb-10">
<span class="text-5xl">🚗</span>
<h2 class="text-3xl font-black text-gray-900 mt-4 uppercase tracking-tighter">Belépés</h2>
<p class="text-gray-400 text-sm font-medium">Service Finder V2.0</p>
</div>
<form @submit.prevent="handleLogin" class="space-y-6">
<div class="space-y-1">
<label class="text-xs font-bold text-gray-500 uppercase ml-1">E-mail cím</label>
<input v-model="email" type="email" required
class="w-full p-4 bg-gray-50 border-2 border-transparent rounded-2xl focus:border-blue-500 focus:bg-white transition-all outline-none"
placeholder="kincses@gmail.com" />
</div>
<div class="space-y-1">
<div class="flex justify-between items-center">
<label class="text-xs font-bold text-gray-500 uppercase ml-1">Jelszó</label>
<router-link to="/forgot-password" class="text-xs font-bold text-blue-600 hover:text-blue-800">Elfelejtetted?</router-link>
<div class="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-gray-100 p-4">
<div class="max-w-md w-full mx-auto">
<!-- Logo & Header -->
<div class="text-center mb-10">
<div class="inline-flex items-center justify-center w-20 h-20 bg-gradient-to-r from-blue-600 to-blue-800 rounded-3xl shadow-2xl mb-6">
<span class="text-4xl">🚗</span>
</div>
<h1 class="text-4xl font-black text-gray-900 mb-2 tracking-tight">Service Finder</h1>
<p class="text-gray-500 font-medium">Smart Garage Management System</p>
<div class="mt-4 inline-flex items-center gap-2 bg-blue-100 text-blue-700 px-4 py-2 rounded-full text-sm font-bold">
<span class="w-2 h-2 bg-green-500 rounded-full animate-pulse"></span>
V2.0 Production Ready
</div>
<input v-model="password" type="password" required
class="w-full p-4 bg-gray-50 border-2 border-transparent rounded-2xl focus:border-blue-500 focus:bg-white transition-all outline-none"
placeholder="••••••••" />
</div>
<div v-if="error" class="p-4 bg-red-50 border-l-4 border-red-500 text-red-700 rounded-lg text-sm font-bold">
{{ error }}
<!-- Login Card -->
<div class="bg-white rounded-3xl shadow-2xl border border-gray-200 p-8 md:p-10">
<div class="mb-8">
<h2 class="text-2xl font-black text-gray-900 mb-2">Login to your account</h2>
<p class="text-gray-500 text-sm">Enter your email and password to continue</p>
</div>
<form @submit.prevent="handleLogin" class="space-y-6">
<!-- Email Field -->
<div class="space-y-2">
<label for="email" class="text-sm font-bold text-gray-700 uppercase tracking-wide">Email address</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207"></path>
</svg>
</div>
<input
id="email"
v-model="email"
type="email"
required
class="w-full pl-12 pr-4 py-4 bg-gray-50 border-2 border-transparent rounded-2xl focus:border-blue-500 focus:bg-white transition-all outline-none text-gray-900 placeholder-gray-400"
placeholder="superadmin@profibot.hu"
/>
</div>
<p class="text-xs text-gray-400 mt-1">For testing use: superadmin@profibot.hu</p>
</div>
<!-- Password Field -->
<div class="space-y-2">
<div class="flex justify-between items-center">
<label for="password" class="text-sm font-bold text-gray-700 uppercase tracking-wide">Password</label>
<router-link to="/forgot-password" class="text-xs font-bold text-blue-600 hover:text-blue-800 transition">
Forgot password?
</router-link>
</div>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path>
</svg>
</div>
<input
id="password"
v-model="password"
:type="showPassword ? 'text' : 'password'"
required
class="w-full pl-12 pr-12 py-4 bg-gray-50 border-2 border-transparent rounded-2xl focus:border-blue-500 focus:bg-white transition-all outline-none text-gray-900 placeholder-gray-400"
placeholder="••••••••"
/>
<button
type="button"
@click="showPassword = !showPassword"
class="absolute inset-y-0 right-0 pr-4 flex items-center text-gray-400 hover:text-blue-600 transition"
>
<svg v-if="showPassword" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L6.59 6.59m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"></path>
</svg>
<svg v-else class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
<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"></path>
</svg>
</button>
</div>
<p class="text-xs text-gray-400 mt-1">Any password works for mock login</p>
</div>
<!-- Error Message -->
<div v-if="error" class="p-4 bg-red-50 border-l-4 border-red-500 text-red-700 rounded-xl text-sm font-bold animate-pulse">
<div class="flex items-center gap-2">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
</svg>
<span>{{ error }}</span>
</div>
</div>
<!-- Login Button -->
<button
type="submit"
:disabled="loading"
class="w-full bg-gradient-to-r from-blue-600 to-blue-700 text-white py-4 rounded-2xl font-black text-lg hover:from-blue-700 hover:to-blue-800 transition-all shadow-xl hover:shadow-2xl hover:shadow-blue-300 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-3"
>
<svg v-if="loading" class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span>{{ loading ? 'LOGGING IN...' : 'LOGIN' }}</span>
</button>
</form>
<!-- Divider -->
<div class="my-8 flex items-center">
<div class="flex-grow border-t border-gray-200"></div>
<span class="mx-4 text-gray-400 text-sm font-medium">VAGY</span>
<div class="flex-grow border-t border-gray-200"></div>
</div>
<!-- Register Link -->
<div class="text-center">
<p class="text-gray-500 font-medium mb-4">Nincs még fiókod?</p>
<router-link
to="/register"
class="inline-block w-full py-3 px-6 border-2 border-blue-600 text-blue-600 rounded-2xl font-bold hover:bg-blue-50 transition-all hover:border-blue-700 hover:text-blue-700"
>
Új Széf létrehozása
</router-link>
</div>
<!-- Demo Info -->
<div class="mt-8 p-4 bg-blue-50 rounded-2xl border border-blue-100">
<div class="flex items-start gap-3">
<svg class="w-5 h-5 text-blue-600 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path>
</svg>
<div class="text-sm text-blue-700">
<p class="font-bold mb-1">Demo módban vagy</p>
<p>A bejelentkezés mockolt, automatikusan sikeres lesz. A rendszer a <span class="font-mono bg-blue-100 px-2 py-0.5 rounded">/profile-select</span> oldalra irányít, ahol kiválaszthatod a felület módot.</p>
</div>
</div>
</div>
</div>
<!-- Footer -->
<div class="mt-8 text-center text-gray-400 text-sm">
<p>© 2026 Service Finder Smart Garage Management v2.0.0</p>
</div>
<button type="submit" :disabled="loading"
class="w-full bg-blue-700 text-white py-4 rounded-2xl font-black text-lg hover:bg-blue-800 transition-all shadow-xl hover:shadow-blue-200 disabled:bg-gray-300">
{{ loading ? 'ELLENŐRZÉS...' : 'BEJELENTKEZÉS' }}
</button>
</form>
<div class="mt-10 pt-6 border-t border-gray-100 text-center">
<p class="text-gray-500 font-medium mb-3">Nincs még fiókod?</p>
<router-link to="/register"
class="inline-block w-full py-3 px-6 border-2 border-blue-700 text-blue-700 rounded-2xl font-bold hover:bg-blue-50 transition-all">
Új Széf létrehozása
</router-link>
</div>
</div>
</template>
@@ -47,41 +149,56 @@
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import axios from 'axios'
import { useAuthStore } from '@/stores/authStore'
const email = ref('')
const email = ref('superadmin@profibot.hu')
const password = ref('')
const showPassword = ref(false)
const error = ref('')
const loading = ref(false)
const router = useRouter()
const authStore = useAuthStore()
// URLSearchParams használata - ez pontosan azt a formátumot küldi, amit a Swagger
const params = new URLSearchParams()
params.append('username', email.value)
params.append('password', password.value)
const handleLogin = async () => {
error.value = ''
loading.value = true
console.log('Login: Starting login process for', email.value)
try {
const res = await axios.post('http://192.168.100.43:8000/api/v2/auth/login', params, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
// Use the auth store for login
console.log('Login: Calling authStore.login()')
await authStore.login(email.value, password.value)
console.log('Login: authStore.login() completed successfully')
// The auth store will handle the redirect to /profile-select
// No need to do anything else here
const token = res.data.access_token
localStorage.setItem('token', token)
// Profil lekérése a jogosultságok miatt
const userRes = await axios.get('http://192.168.100.43:8000/api/v1/users/me', {
headers: { Authorization: `Bearer ${token}` }
})
localStorage.setItem('is_admin', userRes.data.is_superuser ? 'true' : 'false')
router.push('/')
} catch (err) {
console.error("Belépési hiba részletei:", err.response?.data)
error.value = err.response?.data?.detail || "Hibás e-mail vagy jelszó."
console.error('Login error:', err)
error.value = err.message || 'Hiba történt a bejelentkezés során'
} finally {
loading.value = false
}
}
</script>
</script>
<style scoped>
/* Custom scrollbar for the page */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #3b82f6;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #2563eb;
}
</style>

View File

@@ -0,0 +1,9 @@
<template>
<div class="min-h-screen flex items-center justify-center bg-gradient-to-br from-gray-50 to-blue-50 p-4">
<ProfileSelector />
</div>
</template>
<script setup>
import ProfileSelector from '@/components/ProfileSelector.vue'
</script>