Added emergency, jobinfo and beginner admin page

This commit is contained in:
2026-03-13 00:37:01 +01:00 Verified
parent d317eccc9e
commit 66bc2152dc
8 changed files with 258 additions and 49 deletions
+18 -15
View File
@@ -1,38 +1,38 @@
// src/App.tsx // src/App.tsx
import { Calendar, FileText, Home as HomeIcon, Leaf, Map, ShieldAlert } from 'lucide-react'; import { BrowserRouter as Router, Routes, Route, Link, useLocation } from 'react-router-dom';
import { Link, Route, BrowserRouter as Router, Routes, useLocation } from 'react-router-dom'; import { Calendar, FileText, Home as HomeIcon, Map, Leaf, Briefcase, LifeBuoy, Lock } from 'lucide-react';
import { Documents } from './pages/Documents';
import { FAQ } from './pages/FAQ';
import { Home } from './pages/Home'; import { Home } from './pages/Home';
import { JobInfo } from './pages/JobInfo'; import { JobInfo } from './pages/JobInfo';
import { FAQ } from './pages/FAQ';
import { Documents } from './pages/Documents';
import { Schedule } from './pages/Schedule'; import { Schedule } from './pages/Schedule';
import { Emergency } from './pages/Emergency';
import { Admin } from './pages/admin/AdminDashboard';
const Admin = () => <div className="p-6"><h2>Admin Dashboard (Kräver pinkod)</h2></div>; const NavItem = ({ to, icon: Icon, label, className = "" }: { to: string, icon: any, label: string, className?: string }) => {
const NavItem = ({ to, icon: Icon, label }: { to: string, icon: any, label: string }) => {
const location = useLocation(); const location = useLocation();
const isActive = location.pathname === to; const isActive = location.pathname === to;
return ( return (
<Link <Link
to={to} to={to}
className={`flex items-center px-4 py-3 whitespace-nowrap text-sm font-bold transition-all border-b-[3px] ${isActive className={`flex flex-col items-center justify-center px-4 py-2 min-w-18 transition-colors border-b-[3px] ${className} ${isActive
? 'border-gold text-gold bg-ebony/30' ? 'border-gold text-gold bg-ebony/30'
: 'border-transparent text-eggshell/80 hover:text-eggshell hover:bg-eggshell/5' : 'border-transparent text-eggshell/80 hover:text-eggshell hover:bg-eggshell/5'
}`} }`}
> >
<Icon size={18} className="mr-2" strokeWidth={isActive ? 2.5 : 2} /> <Icon size={20} className="mb-1" strokeWidth={isActive ? 2.5 : 2} />
{label} <span className="text-[10px] font-bold uppercase tracking-wider">{label}</span>
</Link> </Link>
); );
}; };
function AppContent() { function AppContent() {
return ( return (
<div className="min-h-screen bg-eggshell font-sans text-ebony selection:bg-gold selection:text-ebony"> <div className="min-h-screen bg-eggshell font-sans text-ebony selection:bg-gold selection:text-ebony pb-10">
{/* Creative, vibrant header using only your colors */} {/* Header & Navigation */}
<header className="sticky top-0 z-50 bg-ebony shadow-md"> <header className="sticky top-0 z-50 bg-ebony shadow-md">
<div className="max-w-5xl mx-auto"> <div className="max-w-5xl mx-auto">
{/* App Title Bar */} {/* App Title Bar */}
@@ -46,10 +46,12 @@ function AppContent() {
{/* Scrollable Tab Navigation */} {/* Scrollable Tab Navigation */}
<nav className="flex overflow-x-auto scrollbar-hide border-t border-eggshell/10"> <nav className="flex overflow-x-auto scrollbar-hide border-t border-eggshell/10">
<NavItem to="/" icon={HomeIcon} label="Hem" /> <NavItem to="/" icon={HomeIcon} label="Hem" />
<NavItem to="/faq" icon={Map} label="FAQ" />
<NavItem to="/schedule" icon={Calendar} label="Schema" /> <NavItem to="/schedule" icon={Calendar} label="Schema" />
<NavItem to="/documents" icon={FileText} label="Info & Dok" /> <NavItem to="/faq" icon={Map} label="FAQ" />
<NavItem to="/admin" icon={ShieldAlert} label="Admin" /> <NavItem to="/info" icon={Briefcase} label="Info" />
<NavItem to="/documents" icon={FileText} label="Filer" />
<NavItem to="/emergency" icon={LifeBuoy} label="Nödläge" className='text-emergency' />
<NavItem to="/admin" icon={Lock} label="Admin" className="md:ml-auto" />
</nav> </nav>
</div> </div>
</header> </header>
@@ -62,6 +64,7 @@ function AppContent() {
<Route path="/faq" element={<FAQ />} /> <Route path="/faq" element={<FAQ />} />
<Route path="/documents" element={<Documents />} /> <Route path="/documents" element={<Documents />} />
<Route path="/schedule" element={<Schedule />} /> <Route path="/schedule" element={<Schedule />} />
<Route path="/emergency" element={<Emergency />} />
<Route path="/admin" element={<Admin />} /> <Route path="/admin" element={<Admin />} />
</Routes> </Routes>
</main> </main>
+1 -1
View File
@@ -32,7 +32,7 @@
"a": "Vid fyren, 350m. Här finns info, broschyrer, personal, akvarier, fågelskådning, tumlarspaning, och Kullabergsguiderna." "a": "Vid fyren, 350m. Här finns info, broschyrer, personal, akvarier, fågelskådning, tumlarspaning, och Kullabergsguiderna."
}, },
{ {
"q": "Tumalarsafari/Kullabergsguiderna?", "q": "Tumalarsafari/ Kullabergsguiderna?",
"a": "Skicka till Naturum, infodisk på insidan." "a": "Skicka till Naturum, infodisk på insidan."
}, },
{ {
+1
View File
@@ -11,6 +11,7 @@
--color-slate-teal: #456466; --color-slate-teal: #456466;
--color-seafoam: #5B8C8A; --color-seafoam: #5B8C8A;
--color-emergency: #EB514C;
} }
body { body {
+1 -1
View File
@@ -33,7 +33,7 @@ export const Documents: React.FC = () => {
className="group flex items-center justify-between bg-white/60 backdrop-blur-sm p-5 rounded-2xl border border-white hover:-translate-y-1 hover:shadow-xl hover:shadow-moss/10 transition-all duration-300" className="group flex items-center justify-between bg-white/60 backdrop-blur-sm p-5 rounded-2xl border border-white hover:-translate-y-1 hover:shadow-xl hover:shadow-moss/10 transition-all duration-300"
> >
<div className="flex items-center text-slate-teal"> <div className="flex items-center text-slate-teal">
<div className="bg-gradient-to-br from-seafoam to-slate-teal p-3 rounded-xl mr-4 text-eggshell shadow-inner">{doc.icon}</div> <div className="bg-linear-to-br from-seafoam to-slate-teal p-3 rounded-xl mr-4 text-eggshell shadow-inner">{doc.icon}</div>
<span className="font-bold text-sm">{doc.title}</span> <span className="font-bold text-sm">{doc.title}</span>
</div> </div>
<Download size={20} className="text-moss group-hover:text-slate-teal transition-colors" /> <Download size={20} className="text-moss group-hover:text-slate-teal transition-colors" />
+87
View File
@@ -0,0 +1,87 @@
// src/pages/Emergency.tsx
import React from 'react';
import { Phone, AlertTriangle, MapPin, HeartPulse } from 'lucide-react';
export const Emergency: React.FC = () => {
return (
<div className="space-y-6 animate-fade-in w-full mx-auto">
<div>
<h1 className="text-3xl font-black text-emergency tracking-tight uppercase flex items-center">
<AlertTriangle className="mr-3 text-emergency" size={32} />
Vid Nödsituation
</h1>
<p className="text-ebony font-medium mt-2 ml-11">Agera lugnt, stanna kvar platsen och tillkalla hjälp.</p>
</div>
{/* 112 Card */}
<div className="bg-emergency/20 border-l-4 border-emergency p-6 rounded-r-xl shadow-sm">
<h2 className="text-2xl font-black text-ebony flex items-center mb-2">
<Phone className="mr-3 text-emergency" size={24} />
Ring 112
</h2>
<p className="text-ebony/80 font-medium mb-4">
Vid olycka, brand eller livshotande tillstånd. Berätta vem du är och vad som har hänt.
</p>
<div className="bg-eggshell/50 p-4 rounded-lg border border-emergency/30">
<h3 className="font-bold text-ebony flex items-center mb-1 text-sm uppercase tracking-wider">
<MapPin className="mr-2 text-emergency" size={16} />
Uppge din position
</h3>
<p className="text-ebony font-medium text-sm">
Använd appen <strong>112</strong> eller GPS i telefonen. Säg att du befinner dig i Kullabergs Naturreservat. Var specifik (t.ex. "Nära fyren" eller "Vid Josefinelust").
</p>
</div>
</div>
{/* First Aid / HLR Card */}
<div className="bg-eggshell border-2 border-slate-teal/20 p-6 rounded-xl shadow-sm">
<h2 className="text-xl font-black text-slate-teal flex items-center mb-4 uppercase tracking-wide">
<HeartPulse className="mr-3 text-seafoam" size={24} />
Första Hjälpen & Hjärtstartare
</h2>
<ul className="space-y-3 text-ebony font-medium">
<li className="flex items-start">
<span className="bg-seafoam text-eggshell font-bold px-2 py-0.5 rounded mr-3 text-sm">1</span>
Säkra platsen se till att varken du eller personen utsätts för mer fara.
</li>
<li className="flex items-start">
<span className="bg-seafoam text-eggshell font-bold px-2 py-0.5 rounded mr-3 text-sm shrink-0">2</span>
{/* Mobile View: Standard wrapping text (hides on medium screens and up) */}
<p className="text-ebony font-medium">
Finns hjärtstartare? Ja, närmaste hjärtstartare finns inne <strong className="text-ebony font-black">Naturum Kullaberg</strong> (vid fyren) under deras öppettider.
</p>
</li>
</ul>
</div>
{/* Internal Contact */}
<div className="bg-eggshell border-2 border-slate-teal/20 p-6 rounded-xl shadow-sm">
<h2 className="text-xl font-black text-slate-teal mb-2 uppercase tracking-wide">
Intern Rapportering
</h2>
<p className="text-ebony/80 font-medium mb-4 text-sm">
När situationen är under kontroll, meddela alltid arbetsledaren om vad som inträffat.
</p>
<div>
<div className="flex justify-between items-center pb-4 border-b border-moss/20">
<span className="text-base font-bold text-ebony">William Söderberg</span>
<div className='flex gap-2 flex-wrap justify-end'>
<a href="tel:0722470291" className="text-sm text-slate-teal font-mono font-bold bg-seafoam/20 hover:bg-seafoam/40 px-3 py-1.5 rounded-lg transition-colors">072-247 02 91</a>
<a href="tel:078389355810604" className="text-sm text-slate-teal font-mono font-bold bg-seafoam/20 hover:bg-seafoam/40 px-3 py-1.5 rounded-lg transition-colors">078-389 35 58 10 604</a>
</div>
</div>
<div className="flex justify-between items-center pt-4">
<span className="text-base font-bold text-ebony">Oliver Nilsson</span>
<div className='flex gap-2 flex-wrap justify-end'>
<a href="tel:0727177440" className="text-sm text-slate-teal font-mono font-bold bg-seafoam/20 hover:bg-seafoam/40 px-3 py-1.5 rounded-lg transition-colors">072-717 74 40</a>
<a href="tel:078389355810605" className="text-sm text-slate-teal font-mono font-bold bg-seafoam/20 hover:bg-seafoam/40 px-3 py-1.5 rounded-lg transition-colors">078-389 35 58 10 605</a>
</div>
</div>
</div>
</div>
</div>
);
};
+3 -3
View File
@@ -16,9 +16,9 @@ export const FAQ: React.FC = () => {
<div className="mb-8"> <div className="mb-8">
<h1 className="text-3xl font-black text-slate-teal tracking-tight flex items-center"> <h1 className="text-3xl font-black text-slate-teal tracking-tight flex items-center">
<MessageCircleQuestionMark className="mr-3 text-seafoam" size={32} /> <MessageCircleQuestionMark className="mr-3 text-seafoam" size={32} />
Veckoschema Vanliga Frågor (FAQ)
</h1> </h1>
<p className="text-moss font-medium mt-2 ml-11">Här hittar du när vi bemannar Kullaberg.</p> <p className="text-moss font-medium mt-2 ml-11">Använd den här guiden för att snabbt svara turisternas vanligaste frågor.</p>
</div> </div>
<div className="columns-1 lg:columns-2 gap-6 space-y-6"> <div className="columns-1 lg:columns-2 gap-6 space-y-6">
@@ -42,7 +42,7 @@ export const FAQ: React.FC = () => {
{isOpen ? <ChevronUp size={16} /> : <ChevronDown size={16} />} {isOpen ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
</div> </div>
</button> </button>
<div className={`transition-all duration-300 ease-in-out ${isOpen ? 'max-h-[500px] opacity-100' : 'max-h-0 opacity-0 overflow-hidden'}`}> <div className={`transition-all duration-300 ease-in-out ${isOpen ? 'max-h-125 opacity-100' : 'max-h-0 opacity-0 overflow-hidden'}`}>
<div className="p-4 pt-0 text-ebony font-medium text-sm leading-relaxed border-t border-ebony/5 mx-4 mt-2"> <div className="p-4 pt-0 text-ebony font-medium text-sm leading-relaxed border-t border-ebony/5 mx-4 mt-2">
{item.a} {item.a}
</div> </div>
+48 -29
View File
@@ -1,43 +1,62 @@
// src/pages/Info.tsx // src/pages/JobInfo.tsx
import React from 'react'; import React from 'react';
import { Briefcase, CheckCircle2, Navigation } from 'lucide-react';
export const JobInfo: React.FC = () => { export const JobInfo: React.FC = () => {
return ( return (
<div className="p-4 max-w-md mx-auto space-y-6"> <div className="space-y-8 animate-fade-in w-full mx-auto">
<h1 className="text-2xl font-bold text-green-800">Jobbinfo & Regler</h1> <div>
<h1 className="text-3xl font-black text-slate-teal tracking-tight flex items-center">
<Briefcase className="mr-3 text-seafoam" size={32} />
Jobbinfo & Regler
</h1>
<p className="text-moss font-medium mt-2 ml-11">Läs igenom detta noggrant inför dina arbetspass.</p>
</div>
<section className="bg-white p-4 rounded-xl shadow-sm border border-gray-100"> <section className="bg-eggshell border-2 border-slate-teal/20 p-6 rounded-2xl shadow-sm">
<h2 className="text-lg font-bold text-gray-800 mb-2">Förväntningar</h2> <h2 className="text-lg font-black text-ebony uppercase tracking-widest mb-3 flex items-center">
<p className="text-sm text-gray-600 mb-2"> <CheckCircle2 className="mr-2 text-moss" size={20} /> Förväntningar
</h2>
<p className="text-sm text-ebony/80 font-medium mb-4 leading-relaxed">
Vi förväntar oss att du tar ansvar, visar respekt för både natur och varandra, samt gör ditt bästa med dina arbetsuppgifter. Vi förväntar oss att du tar ansvar, visar respekt för både natur och varandra, samt gör ditt bästa med dina arbetsuppgifter.
</p> </p>
<ul className="list-disc pl-5 text-sm text-gray-600 space-y-1"> <div className="bg-moss/10 rounded-xl p-4 space-y-2 border border-moss/20">
<li><strong>Period 1:</strong> Start 23 juni kl 08:10 (Buss station Mölle)</li> <div className="flex justify-between items-center">
<li><strong>Period 2:</strong> Start 7 juli kl 08:10</li> <span className="font-bold text-ebony text-sm">Period 1</span>
<span className="text-xs font-bold bg-eggshell text-moss px-2 py-1 rounded">23 juni kl 08:10 (Buss Mölle)</span>
</div>
<div className="flex justify-between items-center border-t border-moss/20 pt-2">
<span className="font-bold text-ebony text-sm">Period 2</span>
<span className="text-xs font-bold bg-eggshell text-moss px-2 py-1 rounded">7 juli kl 08:10</span>
</div>
</div>
</section>
<section className="bg-eggshell border-2 border-slate-teal/20 p-6 rounded-2xl shadow-sm">
<h2 className="text-lg font-black text-ebony uppercase tracking-widest mb-3 flex items-center">
<Navigation className="mr-2 text-moss" size={20} /> Daglig Utrustning
</h2>
<ul className="grid grid-cols-1 md:grid-cols-2 gap-3">
{['Bra promenadskor (mycket gång!)', 'Oömma/väderanpassade kläder', 'En bekväm ryggsäck', 'Egen lunch (ingen kyl/mikro)', 'Minst 1 liter vatten', 'Myggmedel & Solkräm'].map((item, idx) => (
<li key={idx} className="flex items-center text-sm font-bold text-ebony/80 bg-seafoam/10 p-3 rounded-lg border border-seafoam/20">
<div className="w-2 h-2 rounded-full bg-slate-teal mr-3 shrink-0"></div>
{item}
</li>
))}
</ul> </ul>
</section> </section>
<section className="bg-white p-4 rounded-xl shadow-sm border border-gray-100"> <section className="bg-eggshell border-2 border-slate-teal/20 p-6 rounded-2xl shadow-sm">
<h2 className="text-lg font-bold text-gray-800 mb-2">Daglig Utrustning</h2> <h2 className="text-lg font-black text-ebony uppercase tracking-widest mb-3 flex items-center">
<ul className="list-disc pl-5 text-sm text-gray-600 space-y-2"> <Briefcase className="mr-2 text-moss" size={20} /> Exempel uppgifter
<li>Bra promenadskor (du kommer mycket!)</li> </h2>
<li>Oömma och väderanpassade kläder</li> <ul className="space-y-2 text-sm font-bold text-ebony/80">
<li>En bekväm ryggsäck</li> {['Parkeringsguide', 'Naturvägledare för besökare', 'Plocka skräp & ta bort invasiva växter', 'Städa anläggningar', 'Allmänt underhåll (olja skyltar, fixa staket)'].map((task, idx) => (
<li>Egen lunch (ingen tillgång till kyl/mikro)</li> <li key={idx} className="flex items-center">
<li>Minst 1 liter vatten</li> <span className="text-seafoam mr-2"></span> {task}
<li>Myggmedel & Solkräm</li> </li>
</ul> ))}
</section>
<section className="bg-white p-4 rounded-xl shadow-sm border border-gray-100">
<h2 className="text-lg font-bold text-gray-800 mb-2">Exempel uppgifter</h2>
<ul className="list-disc pl-5 text-sm text-gray-600 space-y-1">
<li>Parkeringsguide</li>
<li>Naturvägledare för besökare</li>
<li>Plocka skräp & ta bort invasiva växter</li>
<li>Städa anläggningar</li>
<li>Allmänt underhåll (olja skyltar, fixa staket)</li>
</ul> </ul>
</section> </section>
</div> </div>
+99
View File
@@ -0,0 +1,99 @@
// src/pages/admin/AdminDashboard.tsx
import React, { useState } from 'react';
import { Lock, Unlock, Upload, Users } from 'lucide-react';
export const Admin: React.FC = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [pin, setPin] = useState('');
const [error, setError] = useState('');
const CORRECT_PIN = "1234"; // You can change this later
const handleLogin = (e: React.FormEvent) => {
e.preventDefault();
if (pin === CORRECT_PIN) {
setIsAuthenticated(true);
setError('');
} else {
setError('Ogiltig pinkod.');
setPin('');
}
};
// --- VIEW: LOCKED ---
if (!isAuthenticated) {
return (
<div className="flex flex-col items-center justify-center py-20 animate-fade-in px-4">
<div className="bg-eggshell border-2 border-slate-teal/20 p-8 rounded-2xl shadow-lg w-full max-w-sm text-center">
<div className="bg-slate-teal/10 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
<Lock size={32} className="text-slate-teal" />
</div>
<h2 className="text-2xl font-black text-ebony tracking-wide uppercase mb-2">Admin Login</h2>
<p className="text-sm font-medium text-ebony/70 mb-6">Ange pinkod för att hantera schema och närvaro.</p>
<form onSubmit={handleLogin} className="space-y-4">
<input
type="password"
inputMode="numeric"
value={pin}
onChange={(e) => setPin(e.target.value)}
placeholder="••••"
className="w-full bg-eggshell/50 border-2 border-slate-teal/30 text-center text-2xl tracking-[0.5em] text-ebony font-mono p-3 rounded-xl focus:outline-none focus:border-slate-teal focus:ring-1 focus:ring-slate-teal transition-all"
autoFocus
/>
{error && <p className="text-goldenrod font-bold text-sm">{error}</p>}
<button
type="submit"
className="w-full bg-slate-teal text-eggshell font-black uppercase tracking-widest py-3 rounded-xl hover:bg-ebony transition-colors shadow-md shadow-slate-teal/20"
>
Lås upp
</button>
</form>
</div>
</div>
);
}
// --- VIEW: UNLOCKED (ADMIN DASHBOARD) ---
return (
<div className="space-y-8 animate-fade-in w-full">
<div className="flex justify-between items-end border-b-2 border-slate-teal/20 pb-4">
<div>
<h1 className="text-3xl font-black text-slate-teal tracking-tight uppercase flex items-center">
<Unlock className="mr-3 text-seafoam" size={28} />
Admin Dashboard
</h1>
<p className="text-moss font-bold mt-2 ml-10">Hantera personal, scheman och närvaro.</p>
</div>
<button
onClick={() => setIsAuthenticated(false)}
className="text-sm font-bold text-ebony/60 hover:text-goldenrod transition-colors"
>
Logga ut
</button>
</div>
{/* Attendance Tracker Section Shell */}
<section className="bg-eggshell border-2 border-slate-teal/20 rounded-2xl p-6 shadow-sm">
<div className="flex justify-between items-center mb-6">
<h2 className="text-xl font-black text-ebony uppercase tracking-widest flex items-center">
<Users className="mr-3 text-slate-teal" size={24} />
Närvarorapport
</h2>
{/* Fake Upload Button for now */}
<button className="flex items-center bg-seafoam/20 hover:bg-seafoam/40 text-slate-teal font-bold px-4 py-2 rounded-lg transition-colors border border-seafoam/30">
<Upload size={18} className="mr-2" />
Ladda upp CSV
</button>
</div>
<div className="bg-slate-teal/5 border-2 border-dashed border-slate-teal/30 rounded-xl p-10 text-center flex flex-col items-center justify-center">
<p className="text-ebony/60 font-medium mb-2">Ingen data uppladdad ännu.</p>
<p className="text-sm text-ebony/40 font-medium">Ladda upp Timeedit CSV-filen för att visa interaktiv närvarolista här.</p>
</div>
</section>
</div>
);
};