Files
GreenHomeUI/src/app/calendar/page.tsx
alireza 9da69c96b1
Some checks failed
Deploy MyApp on Same Server / build-and-deploy (push) Has been cancelled
refactor(alert-settings): integrate AppShell layout and update cache version
- Replace custom page layout with AppShell component for consistent UI
- Remove unused PageHeader and UpdateAlertConditionDto imports
- Update service worker cache names and version.json to new timestamp
2026-05-11 22:19:30 +03:30

132 lines
5.3 KiB
TypeScript

"use client"
import { useEffect, useMemo, useState } from 'react'
import { useRouter } from 'next/navigation'
import { api } from '@/lib/api'
import { getCurrentPersianYear } from '@/lib/date/persian-date'
import { Calendar as CalendarIcon, Database, TrendingUp } from 'lucide-react'
import Loading from '@/components/Loading'
import { Card } from '@/components/common'
import { YearSelector } from '@/components/calendar'
import { MonthCard } from '@/components/cards'
import { StatsCard } from '@/components/cards'
import { AppShell } from '@/components/layout'
const monthNames = ['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند']
function useQueryParam(name: string) {
const [value, setValue] = useState<string | null>(null)
useEffect(() => {
if (typeof window !== 'undefined') {
const params = new URLSearchParams(window.location.search)
setValue(params.get(name))
}
}, [name])
return value
}
export default function CalendarPage() {
const router = useRouter()
const deviceIdParam = useQueryParam('deviceId')
const [deviceId, setDeviceId] = useState<number>(1)
const [year, setYear] = useState<number>(getCurrentPersianYear())
const [activeMonths, setActiveMonths] = useState<number[]>([])
const [monthDays, setMonthDays] = useState<Record<number, { days: number; records: number }>>({})
const [loading, setLoading] = useState(true)
const years = useMemo(() => Array.from({ length: 10 }, (_, i) => getCurrentPersianYear() - 2 + i), [])
// وقتی deviceIdParam تغییر کرد، deviceId را به روز کن
useEffect(() => {
if (deviceIdParam) {
setDeviceId(Number(deviceIdParam))
}
}, [deviceIdParam])
useEffect(() => {
let mounted = true
setMonthDays({})
setActiveMonths([])
setLoading(true)
api.activeMonths(deviceId, year)
.then(async (months) => {
if (!mounted) return
setActiveMonths(months)
const entries = await Promise.all(months.map(async m => {
const days = await api.monthDays(deviceId, year, m)
const records = days.reduce((s, d) => s + d.count, 0)
return [m, { days: days.length, records }] as const
}))
if (!mounted) return
setMonthDays(Object.fromEntries(entries))
})
.catch(console.error)
.finally(() => {
if (mounted) setLoading(false)
})
return () => { mounted = false }
}, [deviceId, year])
const totalDays = useMemo(() => Object.values(monthDays).reduce((s, v) => s + (v?.days ?? 0), 0), [monthDays])
const totalRecords = useMemo(() => Object.values(monthDays).reduce((s, v) => s + (v?.records ?? 0), 0), [monthDays])
if (loading) {
return <Loading message="در حال بارگذاری تقویم..." />
}
return (
<AppShell
pageTitle="انتخاب سال و ماه"
pageIcon={CalendarIcon}
iconGradient="from-green-500 to-green-600"
>
<div className="max-w-6xl mx-auto">
{/* Main Card */}
<Card padding="lg">
{/* Year Selector */}
<YearSelector
years={years}
selectedYear={year}
onYearChange={setYear}
className="mb-6"
/>
{/* Summary Stats */}
<div className="bg-gradient-to-r from-green-50 to-emerald-50 border border-green-200 rounded-xl p-4 mb-6">
<div className="flex items-center justify-center gap-6 flex-wrap">
<StatsCard
icon={Database}
label="روز دارای داده"
value={totalDays}
/>
<StatsCard
icon={TrendingUp}
label="رکورد"
value={totalRecords}
/>
</div>
</div>
{/* Months Grid */}
<div className="grid gap-4 md:grid-cols-3 xl:grid-cols-4">
{monthNames.map((name, idx) => {
const m = idx + 1
const isActive = activeMonths.includes(m)
const stats = monthDays[m]
return (
<MonthCard
key={m}
name={name}
isActive={isActive}
stats={stats}
onClick={() => isActive && router.replace(`/day-details?deviceId=${deviceId}&year=${year}&month=${m}`)}
/>
)
})}
</div>
</Card>
</div>
</AppShell>
)
}