Initial commit: Lageplan v1.0 - Next.js 15.5, React 19

This commit is contained in:
Pepe Ziberi
2026-02-21 11:57:44 +01:00
commit adf3dc8c1d
167 changed files with 34265 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
'use client'
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'
export interface User {
id: string
email: string
name: string
role: 'SERVER_ADMIN' | 'TENANT_ADMIN' | 'OPERATOR' | 'VIEWER'
tenantId?: string
tenantSlug?: string
}
export interface TenantInfo {
id: string
name: string
slug: string
plan: string
subscriptionStatus: string
trialEndsAt: string | null
subscriptionEndsAt: string | null
maxUsers: number
maxProjects: number
logoUrl: string | null
}
interface AuthContextType {
user: User | null
tenant: TenantInfo | null
loading: boolean
login: (email: string, password: string) => Promise<{ success: boolean; error?: string }>
logout: () => Promise<void>
canEdit: () => boolean
isAdmin: () => boolean
isServerAdmin: () => boolean
}
const AuthContext = createContext<AuthContextType | undefined>(undefined)
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [tenant, setTenant] = useState<TenantInfo | null>(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
checkAuth()
}, [])
const checkAuth = async () => {
try {
const res = await fetch('/api/auth/me')
if (res.ok) {
const data = await res.json()
setUser(data.user)
setTenant(data.tenant || null)
}
} catch {
// Expected 401 for unauthenticated visitors — no console error
} finally {
setLoading(false)
}
}
const login = async (email: string, password: string) => {
try {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
})
const data = await res.json()
if (res.ok && data.user) {
setUser(data.user)
return { success: true }
}
return { success: false, error: data.error || 'Login fehlgeschlagen' }
} catch (error) {
return { success: false, error: 'Verbindungsfehler' }
}
}
const logout = async () => {
try {
await fetch('/api/auth/logout', { method: 'POST' })
setUser(null)
setTenant(null)
} catch (error) {
console.error('Logout failed:', error)
}
}
const canEdit = () => {
return user?.role === 'SERVER_ADMIN' || user?.role === 'TENANT_ADMIN' || user?.role === 'OPERATOR'
}
const isAdmin = () => {
return user?.role === 'SERVER_ADMIN' || user?.role === 'TENANT_ADMIN'
}
const isServerAdmin = () => {
return user?.role === 'SERVER_ADMIN'
}
return (
<AuthContext.Provider value={{ user, tenant, loading, login, logout, canEdit, isAdmin, isServerAdmin }}>
{children}
</AuthContext.Provider>
)
}
export function useAuth() {
const context = useContext(AuthContext)
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider')
}
return context
}

View File

@@ -0,0 +1,13 @@
'use client'
import { useEffect } from 'react'
export function ServiceWorkerRegister() {
useEffect(() => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').catch(() => {})
}
}, [])
return null
}