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

@@ -0,0 +1,202 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useAuthStore } from './authStore'
export const useAnalyticsStore = defineStore('analytics', () => {
const authStore = useAuthStore()
// Real data - initially empty, will be fetched from API
const monthlyCosts = ref([])
const fuelEfficiencyTrends = ref([])
const costPerKmTrends = ref([])
const funFacts = ref({
totalKmDriven: 0,
totalTreesSaved: 0,
totalCo2Saved: 0,
totalMoneySaved: 0,
moonTrips: computed(() => Math.round(funFacts.value.totalKmDriven / 384400)),
earthCircuits: computed(() => Math.round(funFacts.value.totalKmDriven / 40075)),
})
const businessMetrics = ref({
fleetSize: 0,
averageVehicleAge: 0,
totalMonthlyCost: 0,
averageCostPerKm: 0,
utilizationRate: 0,
downtimeHours: 0,
})
const isLoading = ref(false)
const error = ref(null)
// Getters
const totalCosts = computed(() => {
return monthlyCosts.value.reduce((sum, month) => sum + month.total, 0)
})
const averageMonthlyCost = computed(() => {
return monthlyCosts.value.length > 0 ? totalCosts.value / monthlyCosts.value.length : 0
})
const averageFuelEfficiency = computed(() => {
const sum = fuelEfficiencyTrends.value.reduce((acc, item) => acc + item.efficiency, 0)
return fuelEfficiencyTrends.value.length > 0 ? sum / fuelEfficiencyTrends.value.length : 0
})
const averageCostPerKm = computed(() => {
const sum = costPerKmTrends.value.reduce((acc, item) => acc + item.cost, 0)
return costPerKmTrends.value.length > 0 ? sum / costPerKmTrends.value.length : 0
})
// Actions
function addMonthlyCost(data) {
monthlyCosts.value.push(data)
}
function updateFuelEfficiency(month, efficiency) {
const index = fuelEfficiencyTrends.value.findIndex(item => item.month === month)
if (index !== -1) {
fuelEfficiencyTrends.value[index].efficiency = efficiency
}
}
function updateFunFacts(newFacts) {
Object.assign(funFacts.value, newFacts)
}
// Real API fetch - NO MORE MOCK DATA
async function fetchDashboardAnalytics() {
isLoading.value = true
error.value = null
try {
// Get auth token
const token = authStore.token
if (!token) {
throw new Error('Not authenticated')
}
// Call real backend API
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL || 'https://dev.servicefinder.hu'}/api/v1/analytics/dashboard`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json'
}
})
if (!response.ok) {
throw new Error(`Failed to fetch analytics: ${response.status} ${response.statusText}`)
}
const data = await response.json()
console.log('AnalyticsStore: Fetched dashboard analytics', data)
// Transform API response to frontend format
monthlyCosts.value = data.monthly_costs || []
fuelEfficiencyTrends.value = data.fuel_efficiency_trends || []
costPerKmTrends.value = data.cost_per_km_trends || []
if (data.fun_facts) {
funFacts.value = {
totalKmDriven: data.fun_facts.total_km_driven || 0,
totalTreesSaved: data.fun_facts.total_trees_saved || 0,
totalCo2Saved: data.fun_facts.total_co2_saved || 0,
totalMoneySaved: data.fun_facts.total_money_saved || 0,
moonTrips: computed(() => Math.round((data.fun_facts.total_km_driven || 0) / 384400)),
earthCircuits: computed(() => Math.round((data.fun_facts.total_km_driven || 0) / 40075)),
}
}
if (data.business_metrics) {
businessMetrics.value = {
fleetSize: data.business_metrics.fleet_size || 0,
averageVehicleAge: data.business_metrics.average_vehicle_age || 0,
totalMonthlyCost: data.business_metrics.total_monthly_cost || 0,
averageCostPerKm: data.business_metrics.average_cost_per_km || 0,
utilizationRate: data.business_metrics.utilization_rate || 0,
downtimeHours: data.business_metrics.downtime_hours || 0,
}
}
return data
} catch (err) {
console.error('AnalyticsStore: Error fetching analytics', err)
error.value = err.message
// Keep empty data (no mock fallback)
} finally {
isLoading.value = false
}
}
// Fetch vehicle-specific analytics
async function fetchVehicleAnalytics(vehicleId) {
isLoading.value = true
error.value = null
try {
const token = authStore.token
if (!token) {
throw new Error('Not authenticated')
}
// Call vehicle summary endpoint
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL || 'https://dev.servicefinder.hu'}/api/v1/analytics/${vehicleId}/summary`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json'
}
})
if (!response.ok) {
throw new Error(`Failed to fetch vehicle analytics: ${response.status} ${response.statusText}`)
}
const data = await response.json()
console.log('AnalyticsStore: Fetched vehicle analytics', data)
// For now, just return the data - frontend components can use it directly
return data
} catch (err) {
console.error('AnalyticsStore: Error fetching vehicle analytics', err)
error.value = err.message
throw err
} finally {
isLoading.value = false
}
}
// Fetch fleet analytics (aggregated)
async function fetchFleetAnalytics() {
// For now, use the dashboard endpoint which includes fleet metrics
return fetchDashboardAnalytics()
}
return {
// State
monthlyCosts,
fuelEfficiencyTrends,
costPerKmTrends,
funFacts,
businessMetrics,
isLoading,
error,
// Getters
totalCosts,
averageMonthlyCost,
averageFuelEfficiency,
averageCostPerKm,
// Actions
addMonthlyCost,
updateFuelEfficiency,
updateFunFacts,
fetchDashboardAnalytics,
fetchVehicleAnalytics,
fetchFleetAnalytics,
}
})

View File

@@ -0,0 +1,103 @@
import { defineStore } from 'pinia'
import { ref, computed, onMounted } from 'vue'
import api from '@/services/api'
export const useAppModeStore = defineStore('appMode', () => {
// State
const mode = ref('personal') // backend compatible values: 'personal' or 'fleet'
const isLoading = ref(false)
// Getters
const isPrivateGarage = computed(() => mode.value === 'personal')
const isCorporateFleet = computed(() => mode.value === 'fleet')
// Actions
async function setMode(newMode) {
// Map UI values to backend compatible values
let backendMode = newMode
if (newMode === 'private_garage') {
backendMode = 'personal'
} else if (newMode === 'corporate_fleet') {
backendMode = 'fleet'
}
if (!['personal', 'fleet'].includes(backendMode)) {
console.error('Invalid mode:', newMode)
return
}
mode.value = backendMode
persistMode(backendMode)
await saveModeToBackend(backendMode)
}
function toggleMode() {
const newMode = mode.value === 'personal' ? 'fleet' : 'personal'
setMode(newMode)
}
// SSR-safe localStorage persistence
function persistMode(mode) {
if (typeof window !== 'undefined') {
localStorage.setItem('ui_mode', mode)
}
}
function getInitialMode() {
if (typeof window !== 'undefined') {
const saved = localStorage.getItem('ui_mode')
// Map UI values to backend compatible values
if (saved === 'private_garage' || saved === 'personal') {
return 'personal'
} else if (saved === 'corporate_fleet' || saved === 'fleet') {
return 'fleet'
}
}
// Default mode
return 'personal'
}
// Load user preferences from backend on app startup
async function loadModeFromBackend() {
if (typeof window === 'undefined') return
try {
isLoading.value = true
const response = await api.get('/api/v1/users/me')
const user = response.data
if (user.ui_mode && ['personal', 'fleet'].includes(user.ui_mode)) {
mode.value = user.ui_mode
persistMode(user.ui_mode)
}
} catch (error) {
console.warn('Failed to load UI mode from backend, using local storage', error)
} finally {
isLoading.value = false
}
}
// Save mode to backend via PATCH /users/me/preferences
async function saveModeToBackend(newMode) {
try {
await api.patch('/api/v1/users/me/preferences', {
ui_mode: newMode
})
} catch (error) {
console.error('Failed to save UI mode to backend', error)
// Optionally revert local state? For now just log.
}
}
// Initialize
mode.value = getInitialMode()
// Load from backend after store creation (call in component's onMounted)
// We expose loadModeFromBackend for components to call
return {
mode,
isLoading,
isPrivateGarage,
isCorporateFleet,
setMode,
toggleMode,
loadModeFromBackend,
}
})

View File

@@ -1,10 +1,8 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
import router from '../router'
export const useAuthStore = defineStore('auth', () => {
const router = useRouter()
// State
const token = ref(localStorage.getItem('token') || '')
const isAdmin = ref(localStorage.getItem('is_admin') === 'true')
@@ -33,7 +31,8 @@ export const useAuthStore = defineStore('auth', () => {
params.append('password', password)
// Call real backend API
const response = await fetch('http://localhost:8000/api/v1/auth/login', {
const apiBaseUrl = import.meta.env.VITE_API_BASE_URL || 'https://dev.servicefinder.hu';
const response = await fetch(`${apiBaseUrl}/api/v1/auth/login`, {
method: 'POST',
body: params,
headers: {
@@ -60,16 +59,16 @@ export const useAuthStore = defineStore('auth', () => {
// We need to decode the JWT token to get user role and info
// For now, we'll make a separate API call to get user info
// Or we can parse the JWT token (simple base64 decode)
let userRole = 'user'
let isAdmin = false
let roleValue = 'user'
let adminFlag = false
try {
// Decode JWT token to get payload
const tokenParts = accessToken.split('.')
if (tokenParts.length === 3) {
const payload = JSON.parse(atob(tokenParts[1]))
userRole = payload.role || 'user'
isAdmin = userRole === 'admin' || userRole === 'superadmin'
roleValue = payload.role || 'user'
adminFlag = roleValue === 'admin' || roleValue === 'superadmin'
console.log('AuthStore: Decoded JWT payload', payload)
}
} catch (decodeError) {
@@ -81,15 +80,15 @@ export const useAuthStore = defineStore('auth', () => {
// Save to localStorage
localStorage.setItem('token', accessToken)
localStorage.setItem('refresh_token', refreshToken)
localStorage.setItem('is_admin', isAdmin.toString())
localStorage.setItem('is_admin', adminFlag.toString())
localStorage.setItem('user_email', email)
localStorage.setItem('user_role', userRole)
localStorage.setItem('user_role', roleValue)
// Update store state
token.value = accessToken
isAdmin.value = isAdmin
isAdmin.value = adminFlag
userEmail.value = email
userRole.value = userRole
userRole.value = roleValue
console.log('AuthStore: State updated, redirecting to /profile-select')
@@ -102,49 +101,11 @@ export const useAuthStore = defineStore('auth', () => {
throw error
}
return { success: true, token: accessToken, isAdmin, role: userRole }
return { success: true, token: accessToken, isAdmin: adminFlag, role: roleValue }
} catch (error) {
console.error('AuthStore: Login failed', error)
// Fallback to mock login for development if API is not available
if (error.message.includes('Failed to fetch') || error.message.includes('NetworkError')) {
console.warn('AuthStore: API unavailable, falling back to mock login for development')
// Determine user role based on email
let mockIsAdmin = false
let mockRole = 'user'
if (email === 'superadmin@profibot.hu') {
mockIsAdmin = true
mockRole = 'admin'
} else if (email === 'tester_pro@profibot.hu') {
mockIsAdmin = true // Tester has admin privileges for testing
mockRole = 'tester'
}
// Set mock token
const mockToken = 'mock_jwt_token_' + Date.now()
// Save to localStorage
localStorage.setItem('token', mockToken)
localStorage.setItem('is_admin', mockIsAdmin.toString())
localStorage.setItem('user_email', email)
localStorage.setItem('user_role', mockRole)
// Update store state
token.value = mockToken
isAdmin.value = mockIsAdmin
userEmail.value = email
userRole.value = mockRole
// Redirect
await router.push('/profile-select')
return { success: true, token: mockToken, isAdmin: mockIsAdmin, role: mockRole }
}
throw error
throw error // Re-throw the error instead of falling back to mock
}
}
@@ -173,7 +134,8 @@ export const useAuthStore = defineStore('auth', () => {
}
try {
const response = await fetch('http://localhost:8000/api/v1/users/me', {
const apiBaseUrl = import.meta.env.VITE_API_BASE_URL || 'https://dev.servicefinder.hu';
const response = await fetch(`${apiBaseUrl}/api/v1/users/me`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token.value}`,

View File

@@ -0,0 +1,28 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import api from '@/services/api'
export const useExpenseStore = defineStore('expense', () => {
const isLoading = ref(false)
const error = ref(null)
async function createExpense(expenseData) {
isLoading.value = true
error.value = null
try {
const response = await api.post('/api/v1/expenses/', expenseData)
return response.data
} catch (err) {
error.value = err.response?.data?.detail || err.message
throw err
} finally {
isLoading.value = false
}
}
return {
isLoading,
error,
createExpense,
}
})

View File

@@ -0,0 +1,158 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import api from '@/services/api'
export const useGamificationStore = defineStore('gamification', () => {
// State
const achievements = ref([])
const badges = ref([])
const userStats = ref(null)
const isLoading = ref(false)
const error = ref(null)
// Getters
const earnedAchievements = computed(() =>
achievements.value.filter(a => a.is_earned)
)
const lockedAchievements = computed(() =>
achievements.value.filter(a => !a.is_earned)
)
const totalAchievements = computed(() => achievements.value.length)
const earnedCount = computed(() => earnedAchievements.value.length)
const progressPercentage = computed(() => {
if (totalAchievements.value === 0) return 0
return Math.round((earnedCount.value / totalAchievements.value) * 100)
})
const earnedBadges = computed(() =>
badges.value.filter(b => b.is_earned)
)
// Helper function for API calls (using centralized api instance)
async function apiFetch(url, options = {}) {
try {
const response = await api.get(url, options)
return response.data
} catch (error) {
if (error.response) {
throw new Error(`API error ${error.response.status}: ${JSON.stringify(error.response.data)}`)
}
throw error
}
}
// Actions
async function fetchAchievements() {
isLoading.value = true
error.value = null
try {
const data = await apiFetch('/api/v1/gamification/achievements')
achievements.value = data.achievements || []
return data
} catch (err) {
console.error('Failed to fetch achievements:', err)
error.value = err.message
// No mock fallback - let the error propagate
achievements.value = []
throw err
} finally {
isLoading.value = false
}
}
async function fetchBadges() {
isLoading.value = true
error.value = null
try {
const data = await apiFetch('/api/v1/gamification/my-badges')
badges.value = data.map(badge => ({
id: badge.badge_id,
title: badge.badge_name,
description: badge.badge_description,
icon_url: badge.badge_icon_url,
is_earned: true,
earned_date: badge.earned_at,
category: 'badge'
}))
return data
} catch (err) {
console.error('Failed to fetch badges:', err)
error.value = err.message
// No mock fallback - propagate error
badges.value = []
throw err
} finally {
isLoading.value = false
}
}
async function fetchUserStats() {
isLoading.value = true
error.value = null
try {
const data = await apiFetch('/api/v1/gamification/me')
userStats.value = data
return data
} catch (err) {
console.error('Failed to fetch user stats:', err)
error.value = err.message
// No mock fallback - propagate error
userStats.value = null
throw err
} finally {
isLoading.value = false
}
}
async function fetchAllGamificationData() {
await Promise.all([
fetchAchievements(),
fetchBadges(),
fetchUserStats()
])
}
async function earnAchievement(id) {
// In a real implementation, this would call an API endpoint
// For now, we'll just update local state
const achievement = achievements.value.find(a => a.id === id)
if (achievement && !achievement.is_earned) {
achievement.is_earned = true
achievement.earned_date = new Date().toISOString().split('T')[0]
}
}
function resetAchievements() {
achievements.value.forEach(a => {
a.is_earned = false
a.earned_date = null
})
}
// Initialize store with data - RE-ENABLED after token fix
fetchAllGamificationData()
return {
achievements,
badges,
userStats,
earnedAchievements,
lockedAchievements,
totalAchievements,
earnedCount,
progressPercentage,
earnedBadges,
isLoading,
error,
fetchAchievements,
fetchBadges,
fetchUserStats,
fetchAllGamificationData,
earnAchievement,
resetAchievements
}
})

View File

@@ -30,14 +30,15 @@ export const useGarageStore = defineStore('garage', () => {
try {
// Transform frontend vehicle data to API schema
// For draft vehicles (2-step creation), VIN can be null
const payload = {
vin: vehicle.vin || `TEMP${Date.now()}`,
vin: vehicle.vin || null, // Send null for draft vehicles
license_plate: vehicle.licensePlate || 'N/A',
catalog_id: vehicle.catalogId || null,
organization_id: vehicle.organizationId || 1 // Default org ID
}
const response = await fetch('http://localhost:8000/api/v1/assets/vehicles', {
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL || 'https://dev.servicefinder.hu'}/api/v1/assets/vehicles`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
@@ -55,11 +56,10 @@ export const useGarageStore = defineStore('garage', () => {
const data = await response.json()
console.log('GarageStore: Vehicle created successfully', data)
// Transform API response to frontend format and add to local state
const transformedVehicle = transformApiResponse(data)[0]
vehicles.value.push(transformedVehicle)
// After successful save, fetch fresh data from server to ensure consistency
await fetchVehicles()
return transformedVehicle
return data
} catch (err) {
console.error('GarageStore: Error adding vehicle', err)
throw err
@@ -98,7 +98,7 @@ export const useGarageStore = defineStore('garage', () => {
// Call real backend API
// First try the assets endpoint for user's vehicles
const response = await fetch('http://localhost:8000/api/v1/assets/vehicles', {
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL || 'https://dev.servicefinder.hu'}/api/v1/assets/vehicles`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
@@ -110,7 +110,7 @@ export const useGarageStore = defineStore('garage', () => {
// If 404, try alternative endpoint
if (response.status === 404) {
// Try user assets endpoint
const userResponse = await fetch('http://localhost:8000/api/v1/users/me/assets', {
const userResponse = await fetch(`${import.meta.env.VITE_API_BASE_URL || 'https://dev.servicefinder.hu'}/api/v1/users/me/assets`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,

View File

@@ -0,0 +1,261 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useQuizStore = defineStore('quiz', () => {
// State
const userPoints = ref(0)
const currentStreak = ref(0)
const lastPlayedDate = ref(null)
const questions = ref([])
const isLoading = ref(false)
const error = ref(null)
// Getters
const canPlayToday = computed(() => {
if (!lastPlayedDate.value) return true
const last = new Date(lastPlayedDate.value)
const now = new Date()
// Reset at midnight (different calendar day)
const lastDay = last.toDateString()
const today = now.toDateString()
return lastDay !== today
})
const totalQuestions = computed(() => questions.value.length)
// Helper function to get auth token
function getAuthToken() {
if (typeof window !== 'undefined') {
// Try both token keys for compatibility
return localStorage.getItem('token') || localStorage.getItem('auth_token')
}
return null
}
// Helper function for API calls
async function apiFetch(url, options = {}) {
const token = getAuthToken()
const headers = {
'Content-Type': 'application/json',
...options.headers
}
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL || 'https://dev.servicefinder.hu'}${url}`, {
...options,
headers
})
if (!response.ok) {
const errorText = await response.text()
throw new Error(`API error ${response.status}: ${errorText}`)
}
return response.json()
}
// Actions
async function fetchQuizStats() {
isLoading.value = true
error.value = null
try {
const data = await apiFetch('/api/v1/gamification/quiz/stats')
userPoints.value = data.total_quiz_points || 0
currentStreak.value = data.current_streak || 0
lastPlayedDate.value = data.last_played || null
return data
} catch (err) {
console.error('Failed to fetch quiz stats:', err)
error.value = err.message
// Fallback to localStorage if API fails
userPoints.value = getStoredPoints()
currentStreak.value = getStoredStreak()
lastPlayedDate.value = getStoredLastPlayedDate()
return {
total_quiz_points: userPoints.value,
current_streak: currentStreak.value,
last_played: lastPlayedDate.value,
can_play_today: canPlayToday.value
}
} finally {
isLoading.value = false
}
}
async function fetchDailyQuiz() {
isLoading.value = true
error.value = null
try {
const data = await apiFetch('/api/v1/gamification/quiz/daily')
questions.value = data.questions || []
return data
} catch (err) {
console.error('Failed to fetch daily quiz:', err)
error.value = err.message
// Fallback to mock questions if API fails
questions.value = getMockQuestions()
return {
questions: questions.value,
total_questions: questions.value.length,
date: new Date().toISOString().split('T')[0]
}
} finally {
isLoading.value = false
}
}
async function answerQuestion(questionId, selectedOptionIndex) {
try {
const response = await apiFetch('/api/v1/gamification/quiz/answer', {
method: 'POST',
body: JSON.stringify({
question_id: questionId,
selected_option: selectedOptionIndex
})
})
if (response.is_correct) {
userPoints.value += response.points_awarded
currentStreak.value += 1
persistState() // Update localStorage as fallback
} else {
currentStreak.value = 0
}
return response
} catch (err) {
console.error('Failed to submit quiz answer:', err)
error.value = err.message
// Fallback to local logic
return answerQuestionLocal(questionId, selectedOptionIndex)
}
}
async function completeDailyQuiz() {
try {
await apiFetch('/api/v1/gamification/quiz/complete', {
method: 'POST'
})
lastPlayedDate.value = new Date().toISOString()
persistState()
} catch (err) {
console.error('Failed to complete daily quiz:', err)
error.value = err.message
// Fallback to local storage
lastPlayedDate.value = new Date().toISOString()
persistState()
}
}
// Local fallback functions
function answerQuestionLocal(questionId, selectedOptionIndex) {
const question = questions.value.find(q => q.id === questionId)
if (!question) return { is_correct: false, correct_answer: -1, explanation: 'Question not found' }
const isCorrect = selectedOptionIndex === question.correctAnswer
if (isCorrect) {
userPoints.value += 10
currentStreak.value += 1
} else {
currentStreak.value = 0
}
persistState()
return {
is_correct: isCorrect,
correct_answer: question.correctAnswer,
points_awarded: isCorrect ? 10 : 0,
explanation: question.explanation
}
}
function resetStreak() {
currentStreak.value = 0
persistState()
}
function addPoints(points) {
userPoints.value += points
persistState()
}
// SSR-safe localStorage persistence (fallback only)
function persistState() {
if (typeof window !== 'undefined') {
localStorage.setItem('quiz_points', userPoints.value.toString())
localStorage.setItem('quiz_streak', currentStreak.value.toString())
localStorage.setItem('quiz_last_played', lastPlayedDate.value)
}
}
function getStoredPoints() {
if (typeof window !== 'undefined') {
return parseInt(localStorage.getItem('quiz_points') || '0')
}
return 0
}
function getStoredStreak() {
if (typeof window !== 'undefined') {
return parseInt(localStorage.getItem('quiz_streak') || '0')
}
return 0
}
function getStoredLastPlayedDate() {
if (typeof window !== 'undefined') {
return localStorage.getItem('quiz_last_played') || null
}
return null
}
function getMockQuestions() {
return [
{
id: 1,
question: 'Melyik alkatrész felelős a motor levegőüzemanyag keverékének szabályozásáért?',
options: ['Generátor', 'Lambdaszonda', 'Féktárcsa', 'Olajszűrő'],
correctAnswer: 1,
explanation: 'A lambdaszonda méri a kipufogógáz oxigéntartalmát, és ezen alapul a befecskendezés.'
},
{
id: 2,
question: 'Mennyi ideig érvényes egy gépjármű műszaki vizsgája Magyarországon?',
options: ['1 év', '2 év', '4 év', '6 év'],
correctAnswer: 1,
explanation: 'A személygépkocsik műszaki vizsgája 2 évre érvényes, kivéve az újonnan forgalomba helyezett autókat.'
},
{
id: 3,
question: 'Melyik anyag NEM része a hibrid autók akkumulátorának?',
options: ['Lítium', 'Nikkel', 'Ólom', 'Kobalt'],
correctAnswer: 2,
explanation: 'A hibrid és elektromos autók akkumulátoraiban általában lítium, nikkel és kobalt található, ólom az ólomsavas akkukban van.'
}
]
}
// Initialize store with stats - DISABLED for debugging
// fetchQuizStats()
console.log('🚨 Quiz store: Auto-fetch DISABLED for debugging')
return {
userPoints,
currentStreak,
lastPlayedDate,
questions,
canPlayToday,
totalQuestions,
isLoading,
error,
fetchQuizStats,
fetchDailyQuiz,
answerQuestion,
completeDailyQuiz,
resetStreak,
addPoints
}
})

View File

@@ -0,0 +1,43 @@
import { defineStore } from 'pinia'
export const useThemeStore = defineStore('theme', {
state: () => ({
currentTheme: 'luxury_showroom', // 'luxury_showroom' or 'rusty_workshop'
}),
getters: {
isLuxury: (state) => state.currentTheme === 'luxury_showroom',
isWorkshop: (state) => state.currentTheme === 'rusty_workshop',
themeClasses: (state) => {
if (state.currentTheme === 'luxury_showroom') {
return {
background: 'bg-gradient-to-br from-slate-900 via-slate-800 to-gray-900',
text: 'text-amber-100',
accent: 'text-amber-400',
border: 'border-amber-700',
card: 'bg-slate-800/70 backdrop-blur-lg border border-amber-700/30',
button: 'bg-amber-700 hover:bg-amber-600 text-white',
}
} else {
return {
background: 'bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900',
text: 'text-orange-100',
accent: 'text-orange-400',
border: 'border-orange-800',
card: 'bg-gray-800/90 border border-dashed border-orange-700/50',
button: 'bg-orange-800 hover:bg-orange-700 text-white',
}
}
},
},
actions: {
toggleTheme() {
this.currentTheme = this.currentTheme === 'luxury_showroom' ? 'rusty_workshop' : 'luxury_showroom'
},
setTheme(theme) {
if (['luxury_showroom', 'rusty_workshop'].includes(theme)) {
this.currentTheme = theme
}
},
},
persist: true, // optional: if using pinia-plugin-persistedstate
})