Initial commit: TimeClock v3 - Node.js/Vue.js Zeiterfassung

Features:
- Backend: Node.js/Express mit MySQL/MariaDB
- Frontend: Vue.js 3 mit Composition API
- UTC-Zeithandling für korrekte Zeiterfassung
- Timewish-basierte Überstundenberechnung
- Wochenübersicht mit Urlaubs-/Krankheits-/Feiertagshandling
- Bereinigtes Arbeitsende (Generell/Woche)
- Überstunden-Offset für historische Daten
- Fixed Layout mit scrollbarem Content
- Kompakte UI mit grünem Theme
This commit is contained in:
Torsten Schulz (local)
2025-10-17 14:11:28 +02:00
commit e95bb4cb76
86 changed files with 19530 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '../stores/authStore'
// Views
import Entries from '../views/Entries.vue'
import Stats from '../views/Stats.vue'
import Login from '../views/Login.vue'
import Register from '../views/Register.vue'
import PasswordForgot from '../views/PasswordForgot.vue'
import PasswordReset from '../views/PasswordReset.vue'
import OAuthCallback from '../views/OAuthCallback.vue'
import WeekOverview from '../views/WeekOverview.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
// Auth-Routes (öffentlich)
{
path: '/login',
name: 'login',
component: Login,
meta: { requiresGuest: true }
},
{
path: '/register',
name: 'register',
component: Register,
meta: { requiresGuest: true }
},
{
path: '/password-forgot',
name: 'password-forgot',
component: PasswordForgot,
meta: { requiresGuest: true }
},
{
path: '/password-reset',
name: 'password-reset',
component: PasswordReset,
meta: { requiresGuest: true }
},
{
path: '/oauth-callback',
name: 'oauth-callback',
component: OAuthCallback
},
// Geschützte Routes
{
path: '/',
redirect: '/bookings/week'
},
{
path: '/bookings/week',
name: 'week-overview',
component: WeekOverview,
meta: { requiresAuth: true }
},
{
path: '/entries',
name: 'entries',
component: Entries,
meta: { requiresAuth: true }
},
{
path: '/stats',
name: 'stats',
component: Stats,
meta: { requiresAuth: true }
}
]
})
// Navigation Guards
router.beforeEach(async (to, from, next) => {
const authStore = useAuthStore()
// Session-Wiederherstellung beim ersten Laden
if (!authStore.isAuthenticated && authStore.loadToken()) {
try {
await authStore.fetchCurrentUser()
} catch (error) {
console.error('Session-Wiederherstellung fehlgeschlagen:', error)
authStore.clearAuth()
}
}
const requiresAuth = to.matched.some(record => record.meta.requiresAuth)
const requiresGuest = to.matched.some(record => record.meta.requiresGuest)
if (requiresAuth && !authStore.isAuthenticated) {
// Geschützte Route aber nicht eingeloggt -> Login
next({ name: 'login', query: { redirect: to.fullPath } })
} else if (requiresGuest && authStore.isAuthenticated) {
// Guest-Route aber bereits eingeloggt -> Wochenübersicht
next({ name: 'week-overview' })
} else {
// Alles OK
next()
}
})
export default router