"use client" import { useEffect, useMemo, useState, useCallback, Suspense } from 'react' import { useRouter, useSearchParams } from 'next/navigation' import { api, TelemetryDto, DailyReportDto } from '@/lib/api' import { persianToGregorian, getCurrentPersianDay, getCurrentPersianYear, getCurrentPersianMonth, getPreviousPersianDay, getNextPersianDay } from '@/lib/persian-date' import { BarChart3, ChevronRight, ChevronLeft, Calendar as CalendarIcon, Bell } from 'lucide-react' import Link from 'next/link' import Loading from '@/components/Loading' import { SummaryTab, ChartsTab, WeatherTab, AnalysisTab, TABS, TabType, WeatherData, ensureDateFormat, formatPersianDate, QOM_LAT, QOM_LON, detectDataGaps, DataGap } from '@/components/daily-report' function DailyReportContent() { const router = useRouter() const searchParams = useSearchParams() const [telemetry, setTelemetry] = useState([]) const [loading, setLoading] = useState(true) const [activeTab, setActiveTab] = useState('summary') const [dailyReport, setDailyReport] = useState(null) const [analysisLoading, setAnalysisLoading] = useState(false) const [analysisError, setAnalysisError] = useState(null) const [weatherData, setWeatherData] = useState(null) const [weatherLoading, setWeatherLoading] = useState(false) const [weatherError, setWeatherError] = useState(null) const [expandedDayIndex, setExpandedDayIndex] = useState(null) const [chartStartMinute, setChartStartMinute] = useState(0) // 00:00 const [chartEndMinute, setChartEndMinute] = useState(1439) // 23:59 const deviceId = Number(searchParams.get('deviceId') ?? '1') const dateParam = searchParams.get('date') ?? formatPersianDate(getCurrentPersianYear(), getCurrentPersianMonth(), getCurrentPersianDay()) const selectedDate = useMemo(() => { if (!dateParam) return null try { const decodedDate = decodeURIComponent(dateParam) // Ensure date is in yyyy/MM/dd format return ensureDateFormat(decodedDate) } catch (error) { console.error('Error decoding date parameter:', error) return null } }, [dateParam]) // Navigate to previous day const goToPreviousDay = useCallback(() => { if (!selectedDate) return const prevDay = getPreviousPersianDay(selectedDate) if (prevDay) { router.push(`/daily-report?deviceId=${deviceId}&date=${encodeURIComponent(prevDay)}`) } }, [selectedDate, deviceId, router]) // Navigate to next day const goToNextDay = useCallback(() => { if (!selectedDate) return const nextDay = getNextPersianDay(selectedDate) if (nextDay) { router.push(`/daily-report?deviceId=${deviceId}&date=${encodeURIComponent(nextDay)}`) } }, [selectedDate, deviceId, router]) // Navigate to calendar to select a date const goToCalendar = useCallback(() => { router.push(`/calendar?deviceId=${deviceId}`) }, [deviceId, router]) const loadData = useCallback(async () => { if (!selectedDate) { setLoading(false) return } setLoading(true) try { const [year, month, day] = selectedDate.split('/').map(Number) const startDate = persianToGregorian(year, month, day) startDate.setHours(0, 0, 0, 0) const endDate = new Date(startDate) endDate.setHours(23, 59, 59, 999) const startUtc = startDate.toISOString() const endUtc = endDate.toISOString() const result = await api.listTelemetry({ deviceId, startUtc, endUtc, pageSize: 100000 }) setTelemetry(result.items) } catch (error) { console.error('Error loading telemetry:', error) } finally { setLoading(false) } }, [deviceId, selectedDate]) const loadAnalysis = useCallback(async () => { if (!selectedDate || dailyReport) return setAnalysisLoading(true) setAnalysisError(null) try { const report = await api.getDailyReport(deviceId, selectedDate) setDailyReport(report) } catch (error) { console.error('Error loading analysis:', error) setAnalysisError('خطا در دریافت تحلیل. لطفاً دوباره تلاش کنید.') } finally { setAnalysisLoading(false) } }, [deviceId, selectedDate, dailyReport]) const loadWeather = useCallback(async () => { if (weatherData) return setWeatherLoading(true) setWeatherError(null) try { if (!selectedDate) { setWeatherError('تاریخ انتخاب نشده است') return } // تبدیل تاریخ شمسی به میلادی const [year, month, day] = selectedDate.split('/').map(Number) const gregorianDate = persianToGregorian(year, month, day) // بررسی اینکه تاریخ امروز است یا گذشته const today = new Date() today.setHours(0, 0, 0, 0) gregorianDate.setHours(0, 0, 0, 0) const isPast = gregorianDate.getTime() < today.getTime() let weather: WeatherData if (isPast) { // استفاده از Historical API برای روزهای گذشته const dateStr = gregorianDate.toISOString().split('T')[0] // YYYY-MM-DD const response = await fetch( `https://archive-api.open-meteo.com/v1/archive?latitude=${QOM_LAT}&longitude=${QOM_LON}&start_date=${dateStr}&end_date=${dateStr}&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max,sunshine_duration&timezone=Asia/Tehran` ) if (!response.ok) { throw new Error('Failed to fetch historical weather data') } const data = await response.json() // ساختار داده برای روزهای گذشته (بدون current و hourly) weather = { current: { temperature: data.daily.temperature_2m_max?.[0] || 0, humidity: 0, // Historical API رطوبت ندارد windSpeed: data.daily.wind_speed_10m_max?.[0] || 0, weatherCode: data.daily.weather_code?.[0] || 0, }, hourly: [], // برای گذشته hourly نداریم daily: [{ date: data.daily.time?.[0] || dateStr, tempMax: data.daily.temperature_2m_max?.[0] || 0, tempMin: data.daily.temperature_2m_min?.[0] || 0, weatherCode: data.daily.weather_code?.[0] || 0, precipitation: data.daily.precipitation_sum?.[0] || 0, precipitationProbability: 0, uvIndexMax: 0, sunshineDuration: data.daily.sunshine_duration?.[0] || 0, windSpeedMax: data.daily.wind_speed_10m_max?.[0] || 0, }] } } else { // استفاده از Forecast API برای امروز و آینده const response = await fetch( `https://api.open-meteo.com/v1/forecast?latitude=${QOM_LAT}&longitude=${QOM_LON}¤t=temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m&hourly=temperature_2m,relative_humidity_2m,weather_code,precipitation&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_probability_max,uv_index_max,sunshine_duration,wind_speed_10m_max&timezone=Asia/Tehran&forecast_days=7` ) if (!response.ok) { throw new Error('Failed to fetch weather data') } const data = await response.json() // Get only today's hourly data (first 24 hours) const todayHourly = data.hourly.time.slice(0, 24).map((time: string, i: number) => ({ time, temperature: data.hourly.temperature_2m[i], humidity: data.hourly.relative_humidity_2m[i], weatherCode: data.hourly.weather_code[i], precipitation: data.hourly.precipitation[i], })) weather = { current: { temperature: data.current.temperature_2m, humidity: data.current.relative_humidity_2m, windSpeed: data.current.wind_speed_10m, weatherCode: data.current.weather_code, }, hourly: todayHourly, daily: data.daily.time.map((date: string, i: number) => ({ date, tempMax: data.daily.temperature_2m_max[i], tempMin: data.daily.temperature_2m_min[i], weatherCode: data.daily.weather_code[i], precipitation: data.daily.precipitation_sum[i], precipitationProbability: data.daily.precipitation_probability_max[i], uvIndexMax: data.daily.uv_index_max[i], sunshineDuration: data.daily.sunshine_duration[i], windSpeedMax: data.daily.wind_speed_10m_max[i], })) } } setWeatherData(weather) } catch (error) { console.error('Error loading weather:', error) setWeatherError('خطا در دریافت اطلاعات آب و هوا. لطفاً دوباره تلاش کنید.') } finally { setWeatherLoading(false) } }, [weatherData, selectedDate]) useEffect(() => { // Reset states when date or device changes setDailyReport(null) setWeatherData(null) setAnalysisError(null) setWeatherError(null) loadData() }, [loadData, deviceId, selectedDate]) // Load analysis when switching to analysis tab useEffect(() => { if (activeTab === 'analysis') { loadAnalysis() } }, [activeTab, loadAnalysis]) // Load weather when switching to weather tab useEffect(() => { if (activeTab === 'weather') { loadWeather() } }, [activeTab, loadWeather]) const sortedTelemetry = useMemo(() => { return [...telemetry].sort((a, b) => { const aTime = a.serverTimestampUtc || a.timestampUtc const bTime = b.serverTimestampUtc || b.timestampUtc return new Date(aTime).getTime() - new Date(bTime).getTime() }) }, [telemetry]) // Data arrays const soil = useMemo(() => sortedTelemetry.map(t => Number(t.soilPercent ?? 0)), [sortedTelemetry]) const temp = useMemo(() => sortedTelemetry.map(t => Number(t.temperatureC ?? 0)), [sortedTelemetry]) const hum = useMemo(() => sortedTelemetry.map(t => Number(t.humidityPercent ?? 0)), [sortedTelemetry]) const gas = useMemo(() => sortedTelemetry.map(t => Number(t.gasPPM ?? 0)), [sortedTelemetry]) const lux = useMemo(() => sortedTelemetry.map(t => Number(t.lux ?? 0)), [sortedTelemetry]) // Min/Max calculations (not currently used but kept for potential future use) // const tempMinMax = useMemo(() => { // const min = Math.min(...temp) // const max = Math.max(...temp) // return { // min: min < 0 ? Math.floor(min / 10) * 10 : 0, // max: max > 40 ? Math.floor(max / 10) * 10 : 40 // } // }, [temp]) // const luxMinMax = useMemo(() => { // const max = Math.max(...lux) // return { // min: 0, // max: max > 2000 ? Math.floor(max / 1000) * 1000 : 2000 // } // }, [lux]) // Detect data gaps in the full day data const dataGaps = useMemo(() => { const timestamps = sortedTelemetry.map(t => t.serverTimestampUtc || t.timestampUtc) return detectDataGaps(timestamps, 30) // 30 minutes threshold }, [sortedTelemetry]) // Filtered telemetry for charts based on minute range const filteredTelemetryForCharts = useMemo(() => { return sortedTelemetry.filter(t => { const timestamp = t.serverTimestampUtc || t.timestampUtc const date = new Date(timestamp) const minuteOfDay = date.getHours() * 60 + date.getMinutes() return minuteOfDay >= chartStartMinute && minuteOfDay <= chartEndMinute }) }, [sortedTelemetry, chartStartMinute, chartEndMinute]) // Detect gaps in filtered data const filteredDataGaps = useMemo(() => { const timestamps = filteredTelemetryForCharts.map(t => t.serverTimestampUtc || t.timestampUtc) return detectDataGaps(timestamps, 30) }, [filteredTelemetryForCharts]) // Filtered chart labels const chartLabels = useMemo(() => { return filteredTelemetryForCharts.map(t => { const timestamp = t.serverTimestampUtc || t.timestampUtc const date = new Date(timestamp) const hours = date.getHours().toString().padStart(2, '0') const minutes = date.getMinutes().toString().padStart(2, '0') const seconds = date.getSeconds().toString().padStart(2, '0') return `${hours}:${minutes}:${seconds}` }) }, [filteredTelemetryForCharts]) // Helper function to insert nulls for gaps const insertGapsInData = (data: number[], timestamps: string[], gaps: DataGap[]): (number | null)[] => { if (gaps.length === 0 || data.length < 2) return data const result: (number | null)[] = [] for (let i = 0; i < data.length; i++) { result.push(data[i]) // Check if there's a gap after this point if (i < data.length - 1) { const currentTime = new Date(timestamps[i]) const nextTime = new Date(timestamps[i + 1]) const currentMinute = currentTime.getHours() * 60 + currentTime.getMinutes() const nextMinute = nextTime.getHours() * 60 + nextTime.getMinutes() // Find if any gap exists between current and next const hasGap = gaps.some(gap => currentMinute <= gap.startMinute && nextMinute >= gap.endMinute ) if (hasGap) { result.push(null) // Insert null to break the line } } } return result } // Filtered data arrays for charts (with gaps as null) const filteredTimestamps = useMemo(() => filteredTelemetryForCharts.map(t => t.serverTimestampUtc || t.timestampUtc), [filteredTelemetryForCharts] ) const chartSoil = useMemo(() => { const data = filteredTelemetryForCharts.map(t => Number(t.soilPercent ?? 0)) return insertGapsInData(data, filteredTimestamps, filteredDataGaps) }, [filteredTelemetryForCharts, filteredTimestamps, filteredDataGaps]) const chartTemp = useMemo(() => { const data = filteredTelemetryForCharts.map(t => Number(t.temperatureC ?? 0)) return insertGapsInData(data, filteredTimestamps, filteredDataGaps) }, [filteredTelemetryForCharts, filteredTimestamps, filteredDataGaps]) const chartHum = useMemo(() => { const data = filteredTelemetryForCharts.map(t => Number(t.humidityPercent ?? 0)) return insertGapsInData(data, filteredTimestamps, filteredDataGaps) }, [filteredTelemetryForCharts, filteredTimestamps, filteredDataGaps]) const chartGas = useMemo(() => { const data = filteredTelemetryForCharts.map(t => Number(t.gasPPM ?? 0)) return insertGapsInData(data, filteredTimestamps, filteredDataGaps) }, [filteredTelemetryForCharts, filteredTimestamps, filteredDataGaps]) const chartLux = useMemo(() => { const data = filteredTelemetryForCharts.map(t => Number(t.lux ?? 0)) return insertGapsInData(data, filteredTimestamps, filteredDataGaps) }, [filteredTelemetryForCharts, filteredTimestamps, filteredDataGaps]) // Min/Max calculations for filtered charts (filter out nulls) const chartTempMinMax = useMemo(() => { const validTemps = chartTemp.filter((t): t is number => t !== null) if (validTemps.length === 0) return { min: 0, max: 40 } const min = Math.min(...validTemps) const max = Math.max(...validTemps) return { min: min < 0 ? Math.floor(min / 10) * 10 : 0, max: max > 40 ? Math.floor(max / 10) * 10 : 40 } }, [chartTemp]) const chartLuxMinMax = useMemo(() => { const validLux = chartLux.filter((l): l is number => l !== null) if (validLux.length === 0) return { min: 0, max: 2000 } const max = Math.max(...validLux) return { min: 0, max: max > 2000 ? Math.floor(max / 1000) * 1000 : 2000 } }, [chartLux]) if (loading) { return } if (!selectedDate) { return (
تاریخ انتخاب نشده است
) } return (
{/* Header */}

گزارش روزانه {selectedDate}

مشاهده خلاصه و نمودارهای روز

تنظیمات هشدار
{/* Date Navigation Buttons */}
{/* Tabs */}
{/* Segmented Control for Mobile */}
{TABS.map(tab => ( ))}
{/* Desktop Tabs */}
{TABS.map(tab => ( ))}
{/* Summary Tab */} {activeTab === 'summary' && ( )} {/* Charts Tab */} {activeTab === 'charts' && ( )} {/* Weather Tab */} {activeTab === 'weather' && ( { setWeatherData(null) setWeatherError(null) loadWeather() }} expandedDayIndex={expandedDayIndex} onDayToggle={setExpandedDayIndex} selectedDate={selectedDate} /> )} {/* Analysis Tab */} {activeTab === 'analysis' && ( { setDailyReport(null) setAnalysisError(null) loadAnalysis() }} /> )}
) } export default function DailyReportPage() { return (

در حال بارگذاری گزارش...

}>
) }