fix build bugs
Some checks failed
Deploy MyApp on Same Server / build-and-deploy (push) Failing after 1s
Some checks failed
Deploy MyApp on Same Server / build-and-deploy (push) Failing after 1s
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useEffect, useState, useCallback } from 'react'
|
import React, { useEffect, useState, useCallback, Suspense } from 'react'
|
||||||
import { useSearchParams } from 'next/navigation'
|
import { useSearchParams } from 'next/navigation'
|
||||||
import {
|
import {
|
||||||
api,
|
api,
|
||||||
@@ -23,11 +23,11 @@ import {
|
|||||||
Check,
|
Check,
|
||||||
Phone,
|
Phone,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
Clock,
|
|
||||||
Moon,
|
Moon,
|
||||||
Zap,
|
Zap,
|
||||||
Maximize2,
|
Maximize2,
|
||||||
ArrowLeftRight
|
ArrowLeftRight,
|
||||||
|
LucideIcon
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import Loading from '@/components/Loading'
|
import Loading from '@/components/Loading'
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ type ComparisonType = 0 | 1 | 2 | 3
|
|||||||
type NotificationType = 0 | 1
|
type NotificationType = 0 | 1
|
||||||
type TimeType = 0 | 1 | 2
|
type TimeType = 0 | 1 | 2
|
||||||
|
|
||||||
const SENSOR_TYPES: { value: SensorType; label: string; icon: any; unit: string }[] = [
|
const SENSOR_TYPES: { value: SensorType; label: string; icon: LucideIcon; unit: string }[] = [
|
||||||
{ value: 0, label: 'دما', icon: Thermometer, unit: '°C' },
|
{ value: 0, label: 'دما', icon: Thermometer, unit: '°C' },
|
||||||
{ value: 1, label: 'رطوبت هوا', icon: Droplets, unit: '%' },
|
{ value: 1, label: 'رطوبت هوا', icon: Droplets, unit: '%' },
|
||||||
{ value: 2, label: 'رطوبت خاک', icon: Leaf, unit: '%' },
|
{ value: 2, label: 'رطوبت خاک', icon: Leaf, unit: '%' },
|
||||||
@@ -44,25 +44,25 @@ const SENSOR_TYPES: { value: SensorType; label: string; icon: any; unit: string
|
|||||||
{ value: 4, label: 'نور', icon: Sun, unit: 'Lux' }
|
{ value: 4, label: 'نور', icon: Sun, unit: 'Lux' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const COMPARISON_TYPES: { value: ComparisonType; label: string; icon: any; needsMax: boolean }[] = [
|
const COMPARISON_TYPES: { value: ComparisonType; label: string; icon: LucideIcon | (() => React.JSX.Element); needsMax: boolean }[] = [
|
||||||
{ value: 0, label: 'بزرگتر از', icon: () => <span className="text-2xl font-bold">></span>, needsMax: false },
|
{ value: 0, label: 'بزرگتر از', icon: () => <span className="text-2xl font-bold">></span>, needsMax: false },
|
||||||
{ value: 1, label: 'کوچکتر از', icon: () => <span className="text-2xl font-bold"><</span>, needsMax: false },
|
{ value: 1, label: 'کوچکتر از', icon: () => <span className="text-2xl font-bold"><</span>, needsMax: false },
|
||||||
{ value: 2, label: 'بین', icon: ArrowLeftRight, needsMax: true },
|
{ value: 2, label: 'بین', icon: ArrowLeftRight, needsMax: true },
|
||||||
{ value: 3, label: 'خارج از محدوده', icon: Maximize2, needsMax: true }
|
{ value: 3, label: 'خارج از محدوده', icon: Maximize2, needsMax: true }
|
||||||
]
|
]
|
||||||
|
|
||||||
const NOTIFICATION_TYPES: { value: NotificationType; label: string; icon: any }[] = [
|
const NOTIFICATION_TYPES: { value: NotificationType; label: string; icon: LucideIcon }[] = [
|
||||||
{ value: 0, label: 'تماس تلفنی', icon: Phone },
|
{ value: 0, label: 'تماس تلفنی', icon: Phone },
|
||||||
{ value: 1, label: 'پیامک', icon: MessageSquare }
|
{ value: 1, label: 'پیامک', icon: MessageSquare }
|
||||||
]
|
]
|
||||||
|
|
||||||
const TIME_TYPES: { value: TimeType; label: string; icon: any; description: string }[] = [
|
const TIME_TYPES: { value: TimeType; label: string; icon: LucideIcon; description: string }[] = [
|
||||||
{ value: 0, label: 'روز', icon: Sun, description: 'فقط در روز بررسی شود' },
|
{ value: 0, label: 'روز', icon: Sun, description: 'فقط در روز بررسی شود' },
|
||||||
{ value: 1, label: 'شب', icon: Moon, description: 'فقط در شب بررسی شود' },
|
{ value: 1, label: 'شب', icon: Moon, description: 'فقط در شب بررسی شود' },
|
||||||
{ value: 2, label: 'همیشه', icon: Zap, description: 'در هر زمان بررسی شود' }
|
{ value: 2, label: 'همیشه', icon: Zap, description: 'در هر زمان بررسی شود' }
|
||||||
]
|
]
|
||||||
|
|
||||||
export default function AlertSettingsPage() {
|
function AlertSettingsContent() {
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
const deviceId = Number(searchParams.get('deviceId') ?? '1')
|
const deviceId = Number(searchParams.get('deviceId') ?? '1')
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ export default function AlertSettingsPage() {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
if (formData.rules.length === 0) {
|
if (formData.rules.length === 0) {
|
||||||
alert('لطفاً حداقل یک قانون اضافه کنید')
|
window.alert('لطفاً حداقل یک قانون اضافه کنید')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,14 +157,14 @@ export default function AlertSettingsPage() {
|
|||||||
closeModal()
|
closeModal()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error saving alert:', error)
|
console.error('Error saving alert:', error)
|
||||||
alert('خطا در ذخیره هشدار. لطفاً دوباره تلاش کنید.')
|
window.alert('خطا در ذخیره هشدار. لطفاً دوباره تلاش کنید.')
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false)
|
setSaving(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDelete = async (id: number) => {
|
const handleDelete = async (id: number) => {
|
||||||
if (!confirm('آیا از حذف این هشدار اطمینان دارید؟')) {
|
if (!window.confirm('آیا از حذف این هشدار اطمینان دارید؟')) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ export default function AlertSettingsPage() {
|
|||||||
await loadAlerts()
|
await loadAlerts()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting alert:', error)
|
console.error('Error deleting alert:', error)
|
||||||
alert('خطا در حذف هشدار. لطفاً دوباره تلاش کنید.')
|
window.alert('خطا در حذف هشدار. لطفاً دوباره تلاش کنید.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ export default function AlertSettingsPage() {
|
|||||||
await loadAlerts()
|
await loadAlerts()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error toggling alert:', error)
|
console.error('Error toggling alert:', error)
|
||||||
alert('خطا در تغییر وضعیت هشدار.')
|
window.alert('خطا در تغییر وضعیت هشدار.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ export default function AlertSettingsPage() {
|
|||||||
setFormData({ ...formData, rules: newRules })
|
setFormData({ ...formData, rules: newRules })
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSensorIcon = (type: SensorType) => {
|
const getSensorIcon = (type: SensorType): LucideIcon => {
|
||||||
const sensor = SENSOR_TYPES.find(s => s.value === type)
|
const sensor = SENSOR_TYPES.find(s => s.value === type)
|
||||||
return sensor ? sensor.icon : AlertTriangle
|
return sensor ? sensor.icon : AlertTriangle
|
||||||
}
|
}
|
||||||
@@ -724,3 +724,18 @@ export default function AlertSettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function AlertSettingsPage() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={
|
||||||
|
<div className="min-h-screen flex items-center justify-center">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="w-16 h-16 border-4 border-orange-500 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
|
||||||
|
<p className="text-gray-600">در حال بارگذاری...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<AlertSettingsContent />
|
||||||
|
</Suspense>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useEffect, useMemo, useState, useCallback } from 'react'
|
import { useEffect, useMemo, useState, useCallback, Suspense } from 'react'
|
||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { api, TelemetryDto, DailyReportDto } from '@/lib/api'
|
import { api, TelemetryDto, DailyReportDto } from '@/lib/api'
|
||||||
import { persianToGregorian, getCurrentPersianDay, getCurrentPersianYear, getCurrentPersianMonth, getPreviousPersianDay, getNextPersianDay } from '@/lib/persian-date'
|
import { persianToGregorian, getCurrentPersianDay, getCurrentPersianYear, getCurrentPersianMonth, getPreviousPersianDay, getNextPersianDay } from '@/lib/persian-date'
|
||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
DataGap
|
DataGap
|
||||||
} from '@/components/daily-report'
|
} from '@/components/daily-report'
|
||||||
|
|
||||||
export default function DailyReportPage() {
|
function DailyReportContent() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
const [telemetry, setTelemetry] = useState<TelemetryDto[]>([])
|
const [telemetry, setTelemetry] = useState<TelemetryDto[]>([])
|
||||||
@@ -269,23 +269,23 @@ export default function DailyReportPage() {
|
|||||||
const gas = useMemo(() => sortedTelemetry.map(t => Number(t.gasPPM ?? 0)), [sortedTelemetry])
|
const gas = useMemo(() => sortedTelemetry.map(t => Number(t.gasPPM ?? 0)), [sortedTelemetry])
|
||||||
const lux = useMemo(() => sortedTelemetry.map(t => Number(t.lux ?? 0)), [sortedTelemetry])
|
const lux = useMemo(() => sortedTelemetry.map(t => Number(t.lux ?? 0)), [sortedTelemetry])
|
||||||
|
|
||||||
// Min/Max calculations
|
// Min/Max calculations (not currently used but kept for potential future use)
|
||||||
const tempMinMax = useMemo(() => {
|
// const tempMinMax = useMemo(() => {
|
||||||
const min = Math.min(...temp)
|
// const min = Math.min(...temp)
|
||||||
const max = Math.max(...temp)
|
// const max = Math.max(...temp)
|
||||||
return {
|
// return {
|
||||||
min: min < 0 ? Math.floor(min / 10) * 10 : 0,
|
// min: min < 0 ? Math.floor(min / 10) * 10 : 0,
|
||||||
max: max > 40 ? Math.floor(max / 10) * 10 : 40
|
// max: max > 40 ? Math.floor(max / 10) * 10 : 40
|
||||||
}
|
// }
|
||||||
}, [temp])
|
// }, [temp])
|
||||||
|
|
||||||
const luxMinMax = useMemo(() => {
|
// const luxMinMax = useMemo(() => {
|
||||||
const max = Math.max(...lux)
|
// const max = Math.max(...lux)
|
||||||
return {
|
// return {
|
||||||
min: 0,
|
// min: 0,
|
||||||
max: max > 2000 ? Math.floor(max / 1000) * 1000 : 2000
|
// max: max > 2000 ? Math.floor(max / 1000) * 1000 : 2000
|
||||||
}
|
// }
|
||||||
}, [lux])
|
// }, [lux])
|
||||||
|
|
||||||
// Detect data gaps in the full day data
|
// Detect data gaps in the full day data
|
||||||
const dataGaps = useMemo(() => {
|
const dataGaps = useMemo(() => {
|
||||||
@@ -591,3 +591,18 @@ export default function DailyReportPage() {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function DailyReportPage() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={
|
||||||
|
<div className="min-h-screen flex items-center justify-center p-4">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="w-16 h-16 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
|
||||||
|
<p className="text-gray-600">در حال بارگذاری گزارش...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<DailyReportContent />
|
||||||
|
</Suspense>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useEffect, useState, useMemo } from 'react'
|
import { useEffect, useState, useMemo, Suspense } from 'react'
|
||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { api } from '@/lib/api'
|
import { api } from '@/lib/api'
|
||||||
import { getCurrentPersianYear, getCurrentPersianMonth, getPersianMonthStartWeekday, getPersianMonthDays } from '@/lib/persian-date'
|
import { getCurrentPersianYear, getCurrentPersianMonth, getPersianMonthStartWeekday, getPersianMonthDays } from '@/lib/persian-date'
|
||||||
@@ -9,7 +9,7 @@ import Loading from '@/components/Loading'
|
|||||||
|
|
||||||
const monthNames = ['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند']
|
const monthNames = ['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند']
|
||||||
|
|
||||||
export default function DayDetailsPage() {
|
function DayDetailsContent() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
const [items, setItems] = useState<{ persianDate: string; count: number }[]>([])
|
const [items, setItems] = useState<{ persianDate: string; count: number }[]>([])
|
||||||
@@ -164,3 +164,18 @@ export default function DayDetailsPage() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function DayDetailsPage() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={
|
||||||
|
<div className="min-h-screen flex items-center justify-center p-4">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="w-16 h-16 border-4 border-blue-500 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
|
||||||
|
<p className="text-gray-600">در حال بارگذاری...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<DayDetailsContent />
|
||||||
|
</Suspense>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,10 +50,19 @@ export function Panel({ title, children }: { title: string; children: React.Reac
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LineChart({ labels, series, yAxisMin, yAxisMax, dataGaps = [] }: Props) {
|
export function LineChart({ labels, series, yAxisMin, yAxisMax }: Props) {
|
||||||
// Find gap annotations based on null values in data
|
// Find gap annotations based on null values in data
|
||||||
const gapAnnotations = React.useMemo(() => {
|
const gapAnnotations = React.useMemo(() => {
|
||||||
const annotations: any = {}
|
const annotations: Record<string, {
|
||||||
|
type: 'box';
|
||||||
|
xMin: number;
|
||||||
|
xMax: number;
|
||||||
|
backgroundColor: string;
|
||||||
|
borderColor: string;
|
||||||
|
borderWidth: number;
|
||||||
|
borderDash: number[];
|
||||||
|
label: { display: boolean };
|
||||||
|
}> = {}
|
||||||
let gapCount = 0
|
let gapCount = 0
|
||||||
|
|
||||||
// Find nulls in the first series data
|
// Find nulls in the first series data
|
||||||
@@ -84,7 +93,8 @@ export function LineChart({ labels, series, yAxisMin, yAxisMax, dataGaps = [] }:
|
|||||||
return annotations
|
return annotations
|
||||||
}, [series])
|
}, [series])
|
||||||
|
|
||||||
// Calculate hour range and determine which hours to show
|
// Calculate hour range and determine which hours to show (not currently used but kept for potential future use)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const hourConfig = React.useMemo(() => {
|
const hourConfig = React.useMemo(() => {
|
||||||
const validLabels = labels.filter(l => l)
|
const validLabels = labels.filter(l => l)
|
||||||
if (validLabels.length === 0) {
|
if (validLabels.length === 0) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BarChart3, AlertTriangle } from 'lucide-react'
|
import { AlertTriangle } from 'lucide-react'
|
||||||
import { toPersianDigits, DataGap } from './utils'
|
import { toPersianDigits, DataGap } from './utils'
|
||||||
|
|
||||||
type TimeRangeSelectorProps = {
|
type TimeRangeSelectorProps = {
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ export function WeatherTab({
|
|||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="overflow-x-auto pb-2">
|
<div className="overflow-x-auto pb-2">
|
||||||
<div className="flex gap-2" style={{ minWidth: 'max-content' }}>
|
<div className="flex gap-2" style={{ minWidth: 'max-content' }}>
|
||||||
{weatherData.hourly.map((hour, index) => {
|
{weatherData.hourly.map((hour) => {
|
||||||
const hourNum = new Date(hour.time).getHours()
|
const hourNum = new Date(hour.time).getHours()
|
||||||
const isNow = hourNum === new Date().getHours()
|
const isNow = hourNum === new Date().getHours()
|
||||||
const IconComponent = getWeatherInfo(hour.weatherCode).icon
|
const IconComponent = getWeatherInfo(hour.weatherCode).icon
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Thermometer, Sun, Droplets, Wind, Leaf, AlertTriangle } from 'lucide-react'
|
import { Thermometer, Sun, Droplets, Wind, Leaf } from 'lucide-react'
|
||||||
import { WeatherData, GreenhouseAlert } from './types'
|
import { WeatherData, GreenhouseAlert } from './types'
|
||||||
import { toPersianDigits } from './utils'
|
import { toPersianDigits } from './utils'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user