Lorem ipsum dolor sit ametrem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa.
import React, { useState, useEffect, useMemo } from 'react';
import {
Thermometer, Zap, Battery, Flame, Share2, X, Copy, Activity,
Wifi, HeartPulse, Smartphone, LogIn, ChevronRight,
Info, ShieldCheck, AlertTriangle, Users, Database, Globe,
RefreshCw, Send, Heart, Droplets
} from 'lucide-react';
import { initializeApp } from 'firebase/app';
import {
getFirestore, doc, setDoc, onSnapshot
} from 'firebase/firestore';
import {
getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged
} from 'firebase/auth';
// --- FIREBASE KONFIGURATION ---
const firebaseConfig = JSON.parse(__firebase_config);
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
// Skapa ett ID som är lätt att dela (t.ex. ditt namn eller ett nummer)
const defaultAppId = typeof __app_id !== 'undefined' ? __app_id : 'MIN-ENERGIDATA-2026';
const App = () => {
const [view, setView] = useState('home');
const [user, setUser] = useState(null);
const [currentSystemId, setCurrentSystemId] = useState(defaultAppId);
const [isSynced, setIsSynced] = useState(false);
const [showShareModal, setShowShareModal] = useState(false);
const [isMaster, setIsMaster] = useState(false);
const [connectId, setConnectId] = useState('');
const [lastSync, setLastSync] = useState(null);
const [temps, setTemps] = useState({
ackTop: 72.4, ackMid: 55.2, ackBot: 30.1,
houseFlow: 40.5, houseReturn: 34.2,
wpFlow: 45.1, wpReturn: 39.8,
roomUpper: 21.2, roomLower: 20.5, outdoor: -2.4,
hanPower: 1.15, humidity: 42
});
useEffect(() => {
const initAuth = async () => {
try {
if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) {
await signInWithCustomToken(auth, __initial_auth_token);
} else {
await signInAnonymously(auth);
}
} catch (e) { console.error("Auth error", e); }
};
initAuth();
const unsubscribe = onAuthStateChanged(auth, setUser);
return () => unsubscribe();
}, []);
useEffect(() => {
if (!user || !currentSystemId) return;
const statusDoc = doc(db, 'artifacts', currentSystemId, 'public', 'data', 'v11', 'status');
const unsubscribe = onSnapshot(statusDoc, (snapshot) => {
if (snapshot.exists()) {
const data = snapshot.data();
if (data && data.temps) {
setTemps(data.temps);
setLastSync(new Date(data.timestamp).toLocaleTimeString());
setIsSynced(true);
const isStale = Date.now() - data.timestamp > 20000;
if (isStale && currentSystemId === defaultAppId) setIsMaster(true);
else setIsMaster(false);
}
} else if (currentSystemId === defaultAppId) {
setIsMaster(true);
}
}, (err) => setIsSynced(false));
return () => unsubscribe();
}, [user, currentSystemId]);
useEffect(() => {
if (!user || !isMaster || currentSystemId !== defaultAppId) return;
const interval = setInterval(async () => {
const nextTemps = { ...temps };
Object.keys(nextTemps).forEach(key => {
const change = (Math.random() - 0.5) * 0.2;
nextTemps[key] = parseFloat((nextTemps[key] + change).toFixed(key === 'hanPower' ? 2 : 1));
});
try {
const statusDoc = doc(db, 'artifacts', defaultAppId, 'public', 'data', 'v11', 'status');
await setDoc(statusDoc, { temps: nextTemps, timestamp: Date.now(), ownerId: user.uid });
} catch (err) { console.error(err); }
}, 5000);
return () => clearInterval(interval);
}, [user, isMaster, temps]);
const chargeInfo = useMemo(() => {
const avg = (Number(temps.ackTop) + Number(temps.ackMid) + Number(temps.ackBot)) / 3;
let p = Math.min(Math.max(((avg - 20) / (80 - 20)) * 100, 0), 100);
return { percent: p.toFixed(1), kwh: ((500 * 4.187 * (avg - 20)) / 3600).toFixed(1) };
}, [temps]);
// Hälso-analys (Diabetes-fokus)
const healthInsight = useMemo(() => {
const temp = (temps.roomUpper + temps.roomLower) / 2;
if (temp > 24.5) return { level: 'VARNING', color: 'text-red-600', bg: 'bg-red-50', advice: 'Hög innetemp påverkar vätskebalans och insulinkänslighet. Viktigt att dricka vatten.' };
return { level: 'OPTIMALT', color: 'text-emerald-600', bg: 'bg-emerald-50', advice: 'Stabilt klimat stöder en jämn metabolism och god sömn.' };
}, [temps]);
return (
<div className="min-h-screen bg-slate-50 dark:bg-slate-950 text-slate-900 dark:text-slate-100 font-sans">
<nav className="h-16 border-b bg-white/80 dark:bg-slate-900/80 backdrop-blur-md sticky top-0 z-30 flex items-center px-6 justify-between">
<div className="flex items-center gap-2 cursor-pointer" onClick={() => setView('home')}>
<Zap className="text-blue-600" size={24} fill="currentColor" />
<span className="font-black text-xl tracking-tighter uppercase italic">Energy<span className="text-blue-600">Hub</span></span>
</div>
<div className="flex items-center gap-3">
<button onClick={() => setShowShareModal(true)} className="flex items-center gap-2 bg-blue-600 text-white px-4 py-2 rounded-full text-[10px] font-black tracking-widest uppercase shadow-lg">
<Share2 size={14} /> Dela
</button>
</div>
</nav>
<main className="max-w-4xl mx-auto p-6 space-y-8">
{view === 'home' && (
<div className="py-20 text-center space-y-12">
<div className="space-y-4">
<h1 className="text-6xl font-black tracking-tighter italic">LIVE MONITOR</h1>
<p className="text-slate-500 font-medium">Övervaka ditt hems energi och hälsa i realtid från var som helst i världen.</p>
</div>
<div className="grid md:grid-cols-2 gap-6">
<button onClick={() => {setCurrentSystemId(defaultAppId); setView('dashboard');}} className="p-12 bg-blue-600 text-white rounded-[3rem] text-left shadow-2xl hover:scale-105 transition-all">
<Smartphone size={40} className="mb-6" />
<h3 className="text-2xl font-black italic uppercase">Min Monitor</h3>
<p className="text-blue-100 text-sm">ID: {defaultAppId}</p>
</button>
<div className="p-12 bg-white dark:bg-slate-800 rounded-[3rem] border border-slate-200 text-left space-y-6">
<h3 className="text-xl font-black italic uppercase flex items-center gap-2"><Users size={20}/> Fjärrvvy</h3>
<input
type="text"
placeholder="System-ID..."
className="w-full bg-slate-100 dark:bg-slate-900 p-4 rounded-2xl font-black uppercase"
value={connectId}
onChange={(e) => setConnectId(e.target.value)}
/>
<button onClick={() => { if(connectId) { setCurrentSystemId(connectId.toUpperCase()); setView('dashboard'); } }} className="w-full bg-slate-900 text-white py-4 rounded-2xl font-black">ANSLUT</button>
</div>
</div>
</div>
)}
{view === 'dashboard' && (
<div className="space-y-6 animate-in fade-in duration-500">
{/* DIABETES-VAKT */}
<section className={`p-6 rounded-[2.5rem] border-2 flex items-center gap-6 ${healthInsight.bg} border-current/10`}>
<div className="p-4 bg-white dark:bg-slate-800 rounded-2xl text-rose-500 shadow-sm"><Heart size={28} /></div>
<div className="flex-1">
<span className={`text-[10px] font-black uppercase tracking-widest ${healthInsight.color}`}>{healthInsight.level} - DIABETES-VAKT</span>
<p className="text-sm font-bold text-slate-700 dark:text-slate-300">{healthInsight.advice}</p>
</div>
</section>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="bg-white dark:bg-slate-800 p-8 rounded-[3rem] shadow-xl border border-slate-100 dark:border-slate-700">
<h3 className="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-6">Ackumulatortank</h3>
<div className="text-7xl font-black tracking-tighter mb-4">{chargeInfo.percent}%</div>
<p className="text-emerald-500 font-black text-xs uppercase mb-8">{chargeInfo.kwh} kWh lagrat</p>
<div className="w-full h-4 bg-slate-100 dark:bg-slate-900 rounded-full overflow-hidden p-1 border dark:border-slate-800">
<div className="h-full bg-blue-600 rounded-full transition-all duration-1000" style={{ width: `${chargeInfo.percent}%` }} />
</div>
</div>
<div className="md:col-span-2 grid grid-cols-2 gap-4">
<div className="bg-white dark:bg-slate-800 p-6 rounded-[2.5rem] border border-slate-100 dark:border-slate-700">
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest block mb-2">VP Utgång</span>
<div className="text-3xl font-black text-red-500">{temps.wpFlow.toFixed(1)}°C</div>
</div>
<div className="bg-white dark:bg-slate-800 p-6 rounded-[2.5rem] border border-slate-100 dark:border-slate-700">
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest block mb-2">El-effekt</span>
<div className="text-3xl font-black text-amber-500">{temps.hanPower.toFixed(2)} kW</div>
</div>
<div className="bg-white dark:bg-slate-800 p-6 rounded-[2.5rem] border border-slate-100 dark:border-slate-700">
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest block mb-2">Inne (Snitt)</span>
<div className="text-3xl font-black text-emerald-500">{((temps.roomUpper + temps.roomLower)/2).toFixed(1)}°C</div>
</div>
<div className="bg-white dark:bg-slate-800 p-6 rounded-[2.5rem] border border-slate-100 dark:border-slate-700">
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest block mb-2">Luftfukt.</span>
<div className="text-3xl font-black text-blue-500">{temps.humidity}%</div>
</div>
</div>
</div>
<button onClick={() => setView('home')} className="text-[10px] font-black text-slate-400 uppercase tracking-widest hover:text-blue-600">← Tillbaka till start</button>
</div>
)}
</main>
{/* DELA-MODAL */}
{showShareModal && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/90 backdrop-blur-md">
<div className="bg-white dark:bg-slate-900 w-full max-w-sm rounded-[3rem] p-10 shadow-2xl relative">
<button onClick={() => setShowShareModal(false)} className="absolute top-8 right-8 text-slate-400"><X size={24}/></button>
<div className="text-center space-y-6">
<Globe size={48} className="mx-auto text-blue-600" />
<h2 className="text-2xl font-black italic uppercase">Global Delning</h2>
<p className="text-slate-500 text-xs font-bold leading-tight">Gemini-länkar är privata. För att dela med andra, skicka ditt ID nedan:</p>
<div className="bg-slate-50 dark:bg-slate-800 p-6 rounded-3xl border-2 border-dashed border-slate-200">
<span className="text-[9px] font-black text-slate-400 uppercase block mb-1">Ditt System-ID</span>
<code className="text-2xl font-mono font-black text-blue-600">{defaultAppId}</code>
</div>
<p className="text-[9px] font-bold text-amber-600 uppercase">Se guiden "Hur du publicerar" för att skapa en riktig länk.</p>
<button onClick={() => setShowShareModal(false)} className="w-full bg-slate-900 text-white py-4 rounded-2xl font-black uppercase">Stäng</button>
</div>
</div>
</div>
)}
<footer className="py-20 text-center opacity-20">
<p className="text-[10px] font-black uppercase tracking-[0.5em]">EnergyHub Global Connect • v11.0</p>
</footer>
</div>
);
};
export default App;