feat: dochazka Excel export + auto-reload + 162 filter
- Add dochazka Excel export using direct ZIP/XML manipulation of template (preserves styles.xml byte-for-byte to avoid Excel "repaired styles" warning) - Calculate per-person stravné doplatek, transport (AUV), and indiv1 (closure count + Janouš internet) per sichtovnice.py logic - Filter exported people to TEMPLATE_NAMES (12 fixed template rows) - Add server version polling + auto-reload on deploy - Add FPD check modal for monthly hour validation - Add "162" filter button to hide first 5 TKB people from view Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,18 +2,33 @@ import { useState } from 'react'
|
||||
|
||||
const VALID_USER = 'tkb'
|
||||
const VALID_PASS = 'sluzby'
|
||||
const VIEWER_USER = 'prohlizec'
|
||||
const VIEWER_PASS = 'pohled'
|
||||
const AUTH_KEY = 'tkb_auth'
|
||||
const ROLE_KEY = 'tkb_role'
|
||||
|
||||
export type UserRole = 'editor' | 'viewer'
|
||||
|
||||
export function isAuthenticated(): boolean {
|
||||
return sessionStorage.getItem(AUTH_KEY) === 'true'
|
||||
return localStorage.getItem(AUTH_KEY) === 'true'
|
||||
}
|
||||
|
||||
export function setAuthenticated(): void {
|
||||
sessionStorage.setItem(AUTH_KEY, 'true')
|
||||
export function getAuthRole(): UserRole {
|
||||
return localStorage.getItem(ROLE_KEY) === 'viewer' ? 'viewer' : 'editor'
|
||||
}
|
||||
|
||||
function setAuthenticated(role: UserRole): void {
|
||||
localStorage.setItem(AUTH_KEY, 'true')
|
||||
localStorage.setItem(ROLE_KEY, role)
|
||||
}
|
||||
|
||||
export function clearAuthentication(): void {
|
||||
localStorage.removeItem(AUTH_KEY)
|
||||
localStorage.removeItem(ROLE_KEY)
|
||||
}
|
||||
|
||||
interface LoginProps {
|
||||
onLogin: () => void
|
||||
onLogin: (role: UserRole) => void
|
||||
}
|
||||
|
||||
export function Login({ onLogin }: LoginProps) {
|
||||
@@ -24,8 +39,11 @@ export function Login({ onLogin }: LoginProps) {
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (username === VALID_USER && password === VALID_PASS) {
|
||||
setAuthenticated()
|
||||
onLogin()
|
||||
setAuthenticated('editor')
|
||||
onLogin('editor')
|
||||
} else if (username === VIEWER_USER && password === VIEWER_PASS) {
|
||||
setAuthenticated('viewer')
|
||||
onLogin('viewer')
|
||||
} else {
|
||||
setError(true)
|
||||
setTimeout(() => setError(false), 3000)
|
||||
@@ -39,13 +57,13 @@ export function Login({ onLogin }: LoginProps) {
|
||||
className="bg-slate-800 border border-slate-700 rounded-lg p-8 w-80 shadow-xl"
|
||||
>
|
||||
<h1 className="text-lg font-semibold text-slate-100 mb-1 text-center">
|
||||
TKB Plan sluzeb
|
||||
TKB Plán služeb
|
||||
</h1>
|
||||
<p className="text-sm text-slate-400 mb-6 text-center">
|
||||
Planovani smen a pohotovosti
|
||||
Plánování směn a pohotovostí
|
||||
</p>
|
||||
|
||||
<label className="block text-xs text-slate-400 mb-1">Uzivatel</label>
|
||||
<label className="block text-xs text-slate-400 mb-1">Uživatel</label>
|
||||
<input
|
||||
type="text"
|
||||
value={username}
|
||||
@@ -66,7 +84,7 @@ export function Login({ onLogin }: LoginProps) {
|
||||
|
||||
{error && (
|
||||
<div className="mb-4 text-xs text-red-400 text-center">
|
||||
Nespravne prihlasovaci udaje
|
||||
Nesprávné přihlašovací údaje
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -75,7 +93,7 @@ export function Login({ onLogin }: LoginProps) {
|
||||
className="w-full py-2 rounded bg-blue-600 hover:bg-blue-500 text-white text-sm
|
||||
font-medium transition-colors cursor-pointer"
|
||||
>
|
||||
Prihlasit
|
||||
Přihlásit
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user