fix alert api
Some checks failed
Deploy MyApp on Same Server / build-and-deploy (push) Failing after 1s

This commit is contained in:
2025-12-17 23:20:08 +03:30
parent bb4bdf7462
commit 2481381798
2 changed files with 191 additions and 114 deletions

View File

@@ -6,7 +6,8 @@ import {
AlertConditionDto, AlertConditionDto,
CreateAlertConditionDto, CreateAlertConditionDto,
UpdateAlertConditionDto, UpdateAlertConditionDto,
AlertRuleDto AlertRuleDto,
CreateAlertRuleRequest
} from '@/lib/api' } from '@/lib/api'
import { import {
Bell, Bell,
@@ -77,7 +78,7 @@ function AlertSettingsContent() {
deviceId: deviceId, deviceId: deviceId,
notificationType: 0, notificationType: 0,
timeType: 2, timeType: 2,
isActive: true, isEnabled: true,
rules: [] rules: []
}) })
@@ -103,11 +104,12 @@ function AlertSettingsContent() {
deviceId: deviceId, deviceId: deviceId,
notificationType: 0, notificationType: 0,
timeType: 2, timeType: 2,
isActive: true, isEnabled: true,
rules: [{ rules: [{
sensorType: 0, sensorType: 0,
comparisonType: 0, comparisonType: 0,
threshold: 30 value1: 30,
order: 0
}] }]
}) })
setShowModal(true) setShowModal(true)
@@ -116,11 +118,17 @@ function AlertSettingsContent() {
const openEditModal = (alert: AlertConditionDto) => { const openEditModal = (alert: AlertConditionDto) => {
setEditingAlert(alert) setEditingAlert(alert)
setFormData({ setFormData({
deviceId: alert.deviceId,
notificationType: alert.notificationType, notificationType: alert.notificationType,
timeType: alert.timeType, timeType: alert.timeType,
isActive: alert.isActive, isEnabled: alert.isEnabled,
rules: alert.rules.map(r => ({ ...r })) deviceId: alert.deviceId,
rules: alert.rules.map((r, idx) => ({
sensorType: r.sensorType,
comparisonType: r.comparisonType,
value1: r.value1,
value2: r.value2,
order: idx
}))
}) })
setShowModal(true) setShowModal(true)
} }
@@ -181,11 +189,16 @@ function AlertSettingsContent() {
try { try {
const updateDto: UpdateAlertConditionDto = { const updateDto: UpdateAlertConditionDto = {
id: alert.id, id: alert.id,
deviceId: alert.deviceId,
notificationType: alert.notificationType, notificationType: alert.notificationType,
timeType: alert.timeType, timeType: alert.timeType,
isActive: !alert.isActive, isEnabled: !alert.isEnabled,
rules: alert.rules rules: alert.rules.map((r, idx) => ({
sensorType: r.sensorType,
comparisonType: r.comparisonType,
value1: r.value1,
value2: r.value2,
order: idx
}))
} }
await api.updateAlertCondition(updateDto) await api.updateAlertCondition(updateDto)
await loadAlerts() await loadAlerts()
@@ -203,20 +216,24 @@ function AlertSettingsContent() {
{ {
sensorType: 0, sensorType: 0,
comparisonType: 0, comparisonType: 0,
threshold: 30 value1: 30,
order: formData.rules.length
} }
] ]
}) })
} }
const removeRule = (index: number) => { const removeRule = (index: number) => {
const newRules = formData.rules
.filter((_, i) => i !== index)
.map((r, idx) => ({ ...r, order: idx }))
setFormData({ setFormData({
...formData, ...formData,
rules: formData.rules.filter((_, i) => i !== index) rules: newRules
}) })
} }
const updateRule = (index: number, updates: Partial<AlertRuleDto>) => { const updateRule = (index: number, updates: Partial<CreateAlertRuleRequest>) => {
const newRules = [...formData.rules] const newRules = [...formData.rules]
newRules[index] = { ...newRules[index], ...updates } newRules[index] = { ...newRules[index], ...updates }
setFormData({ ...formData, rules: newRules }) setFormData({ ...formData, rules: newRules })
@@ -255,51 +272,62 @@ function AlertSettingsContent() {
const formatRuleText = (rule: AlertRuleDto) => { const formatRuleText = (rule: AlertRuleDto) => {
const comp = COMPARISON_TYPES.find(c => c.value === rule.comparisonType) const comp = COMPARISON_TYPES.find(c => c.value === rule.comparisonType)
if (comp?.needsMax) { if (comp?.needsMax) {
return `${getComparisonLabel(rule.comparisonType)}: ${rule.threshold} - ${rule.thresholdMax ?? '?'} ${getSensorUnit(rule.sensorType)}` return `${getComparisonLabel(rule.comparisonType)}: ${rule.value1} - ${rule.value2 ?? '?'} ${getSensorUnit(rule.sensorType)}`
} }
const symbol = rule.comparisonType === 0 ? '>' : rule.comparisonType === 1 ? '<' : '?' const symbol = rule.comparisonType === 0 ? '>' : rule.comparisonType === 1 ? '<' : '?'
return `${symbol} ${rule.threshold} ${getSensorUnit(rule.sensorType)}` return `${symbol} ${rule.value1} ${getSensorUnit(rule.sensorType)}`
} }
const generatePreviewText = () => { const generatePreviewText = () => {
if (formData.rules.length === 0) { if (formData.rules.length === 0) {
return 'هنوز هیچ شرطی تعریف نشده است.' return '🤔 هنوز شرطی تعریف نکردی!'
} }
// نوع اطلاع‌رسانی // نوع اطلاع‌رسانی
const notifText = formData.notificationType === 0 ? 'تماس تلفنی' : 'پیامک' const notifText = formData.notificationType === 0 ? 'بهت زنگ بزنم' : 'برات پیامک بفرستم'
// زمان // زمان
const timeText = formData.timeType === 0 const timeText = formData.timeType === 0
? 'در روز' ? 'فقط توی روز'
: formData.timeType === 1 : formData.timeType === 1
? 'در شب' ? 'فقط توی شب'
: 'در هر زمان' : ''
// قوانین // قوانین
const rulesText = formData.rules.map(rule => { const rulesText = formData.rules.map(rule => {
const sensorName = getSensorLabel(rule.sensorType) const sensorName = getSensorLabel(rule.sensorType)
const unit = getSensorUnit(rule.sensorType)
const comp = COMPARISON_TYPES.find(c => c.value === rule.comparisonType) const comp = COMPARISON_TYPES.find(c => c.value === rule.comparisonType)
if (comp?.needsMax) { if (comp?.needsMax) {
// بین یا خارج از محدوده // بین یا خارج از محدوده
const compText = rule.comparisonType === 2 ? 'بین' : 'خارج از' if (rule.comparisonType === 2) {
return `${sensorName} ${compText} ${rule.threshold} تا ${rule.thresholdMax ?? '؟'} ${getSensorUnit(rule.sensorType)}` return `${sensorName} بین ${rule.value1} تا ${rule.value2 ?? '؟'} ${unit} باشه`
} else {
return `${sensorName} از بازه ${rule.value1} تا ${rule.value2 ?? '؟'} ${unit} خارج شه`
}
} else { } else {
// بزرگتر یا کوچکتر // بزرگتر یا کوچکتر
const compText = rule.comparisonType === 0 ? 'بیشتر از' : 'کمتر از' const compText = rule.comparisonType === 0 ? 'از' : 'از'
return `${sensorName} ${compText} ${rule.threshold} ${getSensorUnit(rule.sensorType)}` const direction = rule.comparisonType === 0 ? 'بالاتر' : 'پایین‌تر'
return `${sensorName} ${direction} ${compText} ${rule.value1} ${unit} بره`
} }
}) })
// ساخت جمله نهایی // ساخت جمله نهایی
let baseText = ''
if (rulesText.length === 1) { if (rulesText.length === 1) {
return `ارسال ${notifText} برای زمانی که ${timeText} ${rulesText[0]} باشد.` baseText = `وقتی ${rulesText[0]}`
} else { } else {
const lastRule = rulesText[rulesText.length - 1] const lastRule = rulesText[rulesText.length - 1]
const otherRules = rulesText.slice(0, -1).join(' و ') const otherRules = rulesText.slice(0, -1).join(' و ')
return `ارسال ${notifText} برای زمانی که ${timeText} ${otherRules} و ${lastRule} باشند.` baseText = `وقتی ${otherRules} و ${lastRule}`
} }
// اضافه کردن زمان اگر مشخص شده
const timePrefix = timeText ? `${timeText}، ` : ''
return `${timePrefix}${baseText}، ${notifText} 📱`
} }
if (loading) { if (loading) {
@@ -358,7 +386,7 @@ function AlertSettingsContent() {
) : ( ) : (
<div className="divide-y divide-gray-200"> <div className="divide-y divide-gray-200">
{alerts.map((alert) => ( {alerts.map((alert) => (
<div key={alert.id} className={`p-6 hover:bg-gray-50 transition-colors ${!alert.isActive ? 'opacity-50' : ''}`}> <div key={alert.id} className={`p-6 hover:bg-gray-50 transition-colors ${!alert.isEnabled ? 'opacity-50' : ''}`}>
<div className="flex items-start justify-between gap-4"> <div className="flex items-start justify-between gap-4">
<div className="flex-1 space-y-3"> <div className="flex-1 space-y-3">
{/* Header */} {/* Header */}
@@ -388,12 +416,12 @@ function AlertSettingsContent() {
<button <button
onClick={() => toggleActive(alert)} onClick={() => toggleActive(alert)}
className={`inline-flex items-center gap-1 px-3 py-1 rounded-full text-sm font-medium transition-colors ${ className={`inline-flex items-center gap-1 px-3 py-1 rounded-full text-sm font-medium transition-colors ${
alert.isActive alert.isEnabled
? 'bg-green-100 text-green-800 hover:bg-green-200' ? 'bg-green-100 text-green-800 hover:bg-green-200'
: 'bg-gray-100 text-gray-800 hover:bg-gray-200' : 'bg-gray-100 text-gray-800 hover:bg-gray-200'
}`} }`}
> >
{alert.isActive ? ( {alert.isEnabled ? (
<> <>
<Check className="w-3 h-3" /> <Check className="w-3 h-3" />
فعال فعال
@@ -468,7 +496,25 @@ function AlertSettingsContent() {
</div> </div>
{/* Modal Body */} {/* Modal Body */}
<form onSubmit={handleSubmit} className="p-6 space-y-6"> <form onSubmit={handleSubmit} className="space-y-6">
{/* Preview - Sticky at top */}
<div className="sticky top-[73px] z-10 bg-gradient-to-r from-blue-500 to-indigo-600 shadow-lg">
<div className="px-6 py-4">
<div className="flex items-start gap-3">
<div className="flex-shrink-0 w-10 h-10 bg-white/20 rounded-lg flex items-center justify-center backdrop-blur-sm">
<Bell className="w-5 h-5 text-white" />
</div>
<div className="flex-1">
<div className="text-xs font-semibold text-white/90 mb-1.5">📢 پیشنمایش زنده</div>
<div className="text-sm md:text-base text-white leading-relaxed font-medium">
{generatePreviewText()}
</div>
</div>
</div>
</div>
</div>
<div className="p-6 space-y-6">
{/* Notification Type */} {/* Notification Type */}
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-3"> <label className="block text-sm font-medium text-gray-700 mb-3">
@@ -556,20 +602,26 @@ function AlertSettingsContent() {
</button> </button>
</div> </div>
<div className="space-y-4"> <div className="space-y-3">
{formData.rules.map((rule, index) => { {formData.rules.map((rule, index) => {
const needsMax = COMPARISON_TYPES.find(c => c.value === rule.comparisonType)?.needsMax const needsMax = COMPARISON_TYPES.find(c => c.value === rule.comparisonType)?.needsMax
const selectedSensor = SENSOR_TYPES.find(s => s.value === rule.sensorType)
const SensorIcon = selectedSensor?.icon
const selectedComp = COMPARISON_TYPES.find(c => c.value === rule.comparisonType)
return ( return (
<div key={index} className="bg-gray-50 rounded-lg p-4 space-y-4 relative"> <div key={index} className="bg-gradient-to-br from-orange-50 to-red-50 border-2 border-orange-200 rounded-xl p-4 space-y-3 relative shadow-sm">
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-2">
<span className="text-sm font-medium text-gray-700"> <div className="flex items-center gap-2">
{formData.rules.length === 1 ? 'شرط' : `شرط ${index + 1}`} <span className="text-xs font-bold text-orange-700 bg-white px-2.5 py-1 rounded-full shadow-sm">
</span> {formData.rules.length === 1 ? '⚡ شرط' : `⚡ شرط ${index + 1}`}
</span>
</div>
{formData.rules.length > 1 && ( {formData.rules.length > 1 && (
<button <button
type="button" type="button"
onClick={() => removeRule(index)} onClick={() => removeRule(index)}
className="p-1 text-red-600 hover:bg-red-50 rounded transition-colors" className="p-2 text-red-600 hover:bg-white/50 rounded-lg transition-colors"
title="حذف این شرط" title="حذف این شرط"
> >
<Trash2 className="w-4 h-4" /> <Trash2 className="w-4 h-4" />
@@ -577,12 +629,12 @@ function AlertSettingsContent() {
)} )}
</div> </div>
{/* Sensor Type */} {/* Sensor Type - Dropdown Style */}
<div> <div>
<label className="block text-xs font-medium text-gray-600 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">
کدام سنسور را میخواهید بررسی کنید؟ 🎯 کدام سنسور را بررسی کنیم؟
</label> </label>
<div className="grid grid-cols-3 md:grid-cols-5 gap-2"> <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-2">
{SENSOR_TYPES.map(sensor => { {SENSOR_TYPES.map(sensor => {
const Icon = sensor.icon const Icon = sensor.icon
const isSelected = rule.sensorType === sensor.value const isSelected = rule.sensorType === sensor.value
@@ -591,24 +643,26 @@ function AlertSettingsContent() {
key={sensor.value} key={sensor.value}
type="button" type="button"
onClick={() => updateRule(index, { sensorType: sensor.value })} onClick={() => updateRule(index, { sensorType: sensor.value })}
className={`flex flex-col items-center gap-1 p-2 rounded border transition-all ${ className={`flex flex-col items-center gap-2 p-3 rounded-lg border-2 transition-all ${
isSelected isSelected
? 'border-orange-500 bg-orange-50' ? 'border-orange-500 bg-white shadow-md scale-105'
: 'border-gray-200 hover:border-gray-300' : 'border-white/50 bg-white/50 hover:border-orange-300 hover:bg-white'
}`} }`}
> >
<Icon className={`w-4 h-4 ${isSelected ? 'text-orange-500' : 'text-gray-400'}`} /> <Icon className={`w-6 h-6 ${isSelected ? 'text-orange-500' : 'text-gray-400'}`} />
<span className="text-xs">{sensor.label}</span> <span className={`text-xs font-medium ${isSelected ? 'text-orange-700' : 'text-gray-600'}`}>
{sensor.label}
</span>
</button> </button>
) )
})} })}
</div> </div>
</div> </div>
{/* Comparison Type */} {/* Comparison Type - Card Style */}
<div> <div>
<label className="block text-xs font-medium text-gray-600 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">
چه زمانی باید هشدار داد؟ 📊 چه موقع هشدار بدهیم؟
</label> </label>
<div className="grid grid-cols-2 md:grid-cols-4 gap-2"> <div className="grid grid-cols-2 md:grid-cols-4 gap-2">
{COMPARISON_TYPES.map(comp => { {COMPARISON_TYPES.map(comp => {
@@ -619,50 +673,72 @@ function AlertSettingsContent() {
key={comp.value} key={comp.value}
type="button" type="button"
onClick={() => updateRule(index, { comparisonType: comp.value })} onClick={() => updateRule(index, { comparisonType: comp.value })}
className={`flex flex-col items-center gap-1 p-3 rounded border transition-all ${ className={`flex flex-col items-center gap-2 p-3 rounded-lg border-2 transition-all ${
isSelected isSelected
? 'border-orange-500 bg-orange-50' ? 'border-orange-500 bg-white shadow-md scale-105'
: 'border-gray-200 hover:border-gray-300' : 'border-white/50 bg-white/50 hover:border-orange-300 hover:bg-white'
}`} }`}
> >
<Icon className={`w-5 h-5 ${isSelected ? 'text-orange-500' : 'text-gray-400'}`} /> {'length' in Icon && Icon.length === 0 ? (
<div className="text-xs text-center leading-tight">{comp.label}</div> <span className={`text-xl ${isSelected ? 'text-orange-500' : 'text-gray-400'}`}>
{(Icon as () => React.JSX.Element)()}
</span>
) : (
(() => {
const IconComponent = Icon as LucideIcon
return <IconComponent className={`w-6 h-6 ${isSelected ? 'text-orange-500' : 'text-gray-400'}`} />
})()
)}
<span className={`text-xs font-medium text-center ${isSelected ? 'text-orange-700' : 'text-gray-600'}`}>
{comp.label}
</span>
</button> </button>
) )
})} })}
</div> </div>
</div> </div>
{/* Threshold */} {/* Threshold Values */}
<div className="grid grid-cols-2 gap-3"> <div>
<div> <label className="block text-sm font-medium text-gray-700 mb-2">
<label className="block text-xs font-medium text-gray-600 mb-2"> 🔢 مقدار آستانه
{needsMax ? 'از چه مقداری' : 'چه مقداری'} ({getSensorUnit(rule.sensorType)}) </label>
</label> <div className="flex gap-2">
<input <div className="flex-1">
type="number" <div className="relative">
step="0.01" <input
value={rule.threshold} type="number"
onChange={(e) => updateRule(index, { threshold: Number(e.target.value) })} step="0.01"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-transparent text-sm" value={rule.value1}
required onChange={(e) => updateRule(index, { value1: Number(e.target.value) })}
/> className="w-full px-4 py-3 pr-16 border-2 border-white bg-white rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-orange-500 text-base font-medium"
</div> placeholder={needsMax ? 'از مقدار...' : 'مقدار...'}
{needsMax && ( required
<div> />
<label className="block text-xs font-medium text-gray-600 mb-2"> <span className="absolute left-3 top-1/2 -translate-y-1/2 text-sm text-gray-500 font-medium">
تا چه مقداری ({getSensorUnit(rule.sensorType)}) {getSensorUnit(rule.sensorType)}
</label> </span>
<input </div>
type="number"
step="0.01"
value={rule.thresholdMax ?? ''}
onChange={(e) => updateRule(index, { thresholdMax: Number(e.target.value) })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-transparent text-sm"
required
/>
</div> </div>
)} {needsMax && (
<div className="flex-1">
<div className="relative">
<input
type="number"
step="0.01"
value={rule.value2 ?? ''}
onChange={(e) => updateRule(index, { value2: Number(e.target.value) })}
className="w-full px-4 py-3 pr-16 border-2 border-white bg-white rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-orange-500 text-base font-medium"
placeholder="تا مقدار..."
required
/>
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-sm text-gray-500 font-medium">
{getSensorUnit(rule.sensorType)}
</span>
</div>
</div>
)}
</div>
</div> </div>
</div> </div>
) )
@@ -670,31 +746,16 @@ function AlertSettingsContent() {
</div> </div>
</div> </div>
{/* Preview */}
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border-2 border-blue-200 rounded-xl p-5">
<div className="flex items-start gap-3">
<div className="flex-shrink-0 w-10 h-10 bg-blue-500 rounded-lg flex items-center justify-center">
<Bell className="w-5 h-5 text-white" />
</div>
<div className="flex-1">
<div className="text-xs font-medium text-blue-600 mb-1">پیشنمایش هشدار</div>
<div className="text-sm text-gray-800 leading-relaxed">
{generatePreviewText()}
</div>
</div>
</div>
</div>
{/* Active Status */} {/* Active Status */}
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<input <input
type="checkbox" type="checkbox"
id="isActive" id="isEnabled"
checked={formData.isActive} checked={formData.isEnabled}
onChange={(e) => setFormData({ ...formData, isActive: e.target.checked })} onChange={(e) => setFormData({ ...formData, isEnabled: e.target.checked })}
className="w-4 h-4 text-orange-600 border-gray-300 rounded focus:ring-orange-500" className="w-4 h-4 text-orange-600 border-gray-300 rounded focus:ring-orange-500"
/> />
<label htmlFor="isActive" className="text-sm font-medium text-gray-700"> <label htmlFor="isEnabled" className="text-sm font-medium text-gray-700">
هشدار فعال باشد هشدار فعال باشد
</label> </label>
</div> </div>
@@ -717,6 +778,7 @@ function AlertSettingsContent() {
{saving ? 'در حال ذخیره...' : editingAlert ? 'ذخیره تغییرات' : 'افزودن هشدار'} {saving ? 'در حال ذخیره...' : editingAlert ? 'ذخیره تغییرات' : 'افزودن هشدار'}
</button> </button>
</div> </div>
</div>
</form> </form>
</div> </div>
</div> </div>

View File

@@ -93,19 +93,32 @@ export type DailyReportDto = {
} }
export type AlertRuleDto = { export type AlertRuleDto = {
id?: number id: number
alertConditionId: number
sensorType: 0 | 1 | 2 | 3 | 4 // Temperature=0, Humidity=1, Soil=2, Gas=3, Lux=4 sensorType: 0 | 1 | 2 | 3 | 4 // Temperature=0, Humidity=1, Soil=2, Gas=3, Lux=4
comparisonType: 0 | 1 | 2 | 3 // GreaterThan=0, LessThan=1, Between=2, OutOfRange=3 comparisonType: 0 | 1 | 2 | 3 // GreaterThan=0, LessThan=1, Between=2, OutOfRange=3
threshold: number value1: number
thresholdMax?: number // برای Between و OutOfRange value2?: number // برای Between و OutOfRange
order: number
}
export type CreateAlertRuleRequest = {
sensorType: 0 | 1 | 2 | 3 | 4
comparisonType: 0 | 1 | 2 | 3
value1: number
value2?: number
order: number
} }
export type AlertConditionDto = { export type AlertConditionDto = {
id: number id: number
deviceId: number deviceId: number
deviceName: string
notificationType: 0 | 1 // Call=0, SMS=1 notificationType: 0 | 1 // Call=0, SMS=1
timeType: 0 | 1 | 2 // Day=0, Night=1, Always=2 timeType: 0 | 1 | 2 // Day=0, Night=1, Always=2
isActive: boolean callCooldownMinutes: number
smsCooldownMinutes: number
isEnabled: boolean
rules: AlertRuleDto[] rules: AlertRuleDto[]
createdAt: string createdAt: string
updatedAt: string updatedAt: string
@@ -115,17 +128,20 @@ export type CreateAlertConditionDto = {
deviceId: number deviceId: number
notificationType: 0 | 1 notificationType: 0 | 1
timeType: 0 | 1 | 2 timeType: 0 | 1 | 2
isActive: boolean callCooldownMinutes?: number
rules: AlertRuleDto[] smsCooldownMinutes?: number
isEnabled: boolean
rules: CreateAlertRuleRequest[]
} }
export type UpdateAlertConditionDto = { export type UpdateAlertConditionDto = {
id: number id: number
deviceId: number
notificationType: 0 | 1 notificationType: 0 | 1
timeType: 0 | 1 | 2 timeType: 0 | 1 | 2
isActive: boolean callCooldownMinutes?: number
rules: AlertRuleDto[] smsCooldownMinutes?: number
isEnabled: boolean
rules: CreateAlertRuleRequest[]
} }
const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? 'https://ghback.nabaksoft.ir' const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? 'https://ghback.nabaksoft.ir'
@@ -196,9 +212,8 @@ export const api = {
http<DailyReportDto>(`${API_BASE}/api/DailyReport?deviceId=${deviceId}&persianDate=${encodeURIComponent(persianDate)}`), http<DailyReportDto>(`${API_BASE}/api/DailyReport?deviceId=${deviceId}&persianDate=${encodeURIComponent(persianDate)}`),
// Alert Conditions // Alert Conditions
getAlertConditions: (deviceId?: number) => { getAlertConditions: (deviceId: number) => {
const params = deviceId ? `?deviceId=${deviceId}` : '' return http<AlertConditionDto[]>(`${API_BASE}/api/alertconditions/device/${deviceId}`)
return http<AlertConditionDto[]>(`${API_BASE}/api/alertconditions${params}`)
}, },
getAlertCondition: (id: number) => getAlertCondition: (id: number) =>
http<AlertConditionDto>(`${API_BASE}/api/alertconditions/${id}`), http<AlertConditionDto>(`${API_BASE}/api/alertconditions/${id}`),