This commit is contained in:
lumijiez
2025-05-21 20:06:52 +03:00
parent 6a0a3b747a
commit 13cc494111
17 changed files with 3170 additions and 1283 deletions

View File

@@ -1,103 +1,144 @@
import Image from "next/image";
'use client'
import { useState, useEffect } from 'react'
import { Card } from '@/components/ui/card'
import { BatteryStatus } from '@/components/BatteryStatus'
import { PowerStats } from '@/components/PowerStats'
import { SystemStatus } from '@/components/SystemStatus'
import { SolarChart } from '@/components/SolarChart'
import { LastRefresh } from '@/components/LastRefresh'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { motion, AnimatePresence } from 'framer-motion'
export default function Dashboard() {
const [data, setData] = useState([])
const [timeRange, setTimeRange] = useState('1d')
const [loading, setLoading] = useState(true)
const fetchData = async () => {
try {
const response = await fetch(`/api/solar-data?timeRange=${timeRange}`)
const newData = await response.json()
setData(newData)
} catch (error) {
console.error('Error fetching data:', error)
} finally {
setLoading(false)
}
}
useEffect(() => {
fetchData()
const interval = setInterval(fetchData, 20000)
return () => clearInterval(interval)
}, [timeRange])
if (loading) {
return (
<div className="flex items-center justify-center min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
<motion.div
initial={{ scale: 0.5, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 0.5 }}
className="relative"
>
<div className="w-32 h-32 border-4 border-blue-500 border-t-transparent rounded-full animate-spin"></div>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-24 h-24 border-4 border-emerald-500 border-t-transparent rounded-full animate-spin" style={{ animationDirection: 'reverse' }}></div>
</div>
</motion.div>
</div>
)
}
const latestData = data[data.length - 1]?.data || {}
const lastUpdateTime = data[data.length - 1]?.timestamp?.$date || data[data.length - 1]?.timestamp
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
}
const itemVariants = {
hidden: { y: 20, opacity: 0 },
visible: {
y: 0,
opacity: 1,
transition: {
duration: 0.5
}
}
}
export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2 tracking-[-.01em]">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
src/app/page.js
</code>
.
</li>
<li className="tracking-[-.01em]">
Save and see your changes instantly.
</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900 text-white">
<div className="max-w-[1400px] mx-auto p-4 sm:p-6 space-y-6">
<div className="flex flex-col items-center">
<motion.div
initial={{ y: -20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.5 }}
className="text-center mb-4"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
<h1 className="text-4xl sm:text-5xl font-bold bg-gradient-to-r from-blue-400 via-emerald-400 to-blue-400 bg-clip-text text-transparent animate-gradient mb-4">
Solar Management Dashboard
</h1>
<p className="text-gray-400 text-lg">Real-time monitoring of your solar system</p>
</motion.div>
{lastUpdateTime && <LastRefresh timestamp={lastUpdateTime} />}
</div>
</main>
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
<motion.div
variants={containerVariants}
initial="hidden"
animate="visible"
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
<motion.div variants={itemVariants} className="h-full">
<BatteryStatus data={latestData} />
</motion.div>
<motion.div variants={itemVariants} className="h-full">
<PowerStats data={latestData} />
</motion.div>
<motion.div variants={itemVariants} className="h-full">
<SystemStatus data={latestData} />
</motion.div>
</motion.div>
<motion.div
variants={containerVariants}
initial="hidden"
animate="visible"
className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
<motion.div variants={itemVariants} className="h-full">
<Card className="h-full p-4 sm:p-6 bg-gray-800/50 backdrop-blur-sm border-gray-700 hover:bg-gray-800/70 transition-all duration-300 transform hover:scale-[1.02] hover:shadow-2xl hover:shadow-blue-500/10">
<SolarChart data={data} type="power" />
</Card>
</motion.div>
<motion.div variants={itemVariants} className="h-full">
<Card className="h-full p-4 sm:p-6 bg-gray-800/50 backdrop-blur-sm border-gray-700 hover:bg-gray-800/70 transition-all duration-300 transform hover:scale-[1.02] hover:shadow-2xl hover:shadow-emerald-500/10">
<SolarChart data={data} type="battery" />
</Card>
</motion.div>
<motion.div variants={itemVariants} className="h-full">
<Card className="h-full p-4 sm:p-6 bg-gray-800/50 backdrop-blur-sm border-gray-700 hover:bg-gray-800/70 transition-all duration-300 transform hover:scale-[1.02] hover:shadow-2xl hover:shadow-purple-500/10">
<SolarChart data={data} type="voltage" />
</Card>
</motion.div>
<motion.div variants={itemVariants} className="h-full">
<Card className="h-full p-4 sm:p-6 bg-gray-800/50 backdrop-blur-sm border-gray-700 hover:bg-gray-800/70 transition-all duration-300 transform hover:scale-[1.02] hover:shadow-2xl hover:shadow-red-500/10">
<SolarChart data={data} type="temperature" />
</Card>
</motion.div>
</motion.div>
</div>
</div>
);
)
}