add ability to customize whitch paremeters to show
Some checks failed
Deploy MyApp on Same Server / build-and-deploy (push) Failing after 1s

This commit is contained in:
2026-02-19 06:52:45 +03:30
parent 3644d57206
commit e435630edb
5 changed files with 118 additions and 64 deletions

View File

@@ -28,6 +28,7 @@ function DailyReportContent() {
const [activeTab, setActiveTab] = useState<TabType>('summary') const [activeTab, setActiveTab] = useState<TabType>('summary')
const [forecastWeather, setForecastWeather] = useState<WeatherData | null>(null) const [forecastWeather, setForecastWeather] = useState<WeatherData | null>(null)
const [forecastWeatherLoading, setForecastWeatherLoading] = useState(false) const [forecastWeatherLoading, setForecastWeatherLoading] = useState(false)
const [visibleParams, setVisibleParams] = useState<string[] | null>(null)
// Map summary param to chart key // Map summary param to chart key
const paramToChartKey = useCallback((param: string): string => { const paramToChartKey = useCallback((param: string): string => {
@@ -113,6 +114,16 @@ function DailyReportContent() {
const result = await api.listTelemetry({ deviceId, startUtc, endUtc, pageSize: 100000 }) const result = await api.listTelemetry({ deviceId, startUtc, endUtc, pageSize: 100000 })
setTelemetry(result.items) setTelemetry(result.items)
console.log(result.items)
// Load visible params from server (controls which parameters are shown)
try {
const params = await api.getDisplayParameters(deviceId)
setVisibleParams(params ?? null)
} catch (err) {
console.error('Error loading display parameters:', err)
setVisibleParams(['gas', 'temperature', 'humidity', 'lux'])
}
} catch (error) { } catch (error) {
console.error('Error loading telemetry:', error) console.error('Error loading telemetry:', error)
} finally { } finally {
@@ -243,20 +254,20 @@ function DailyReportContent() {
className="md:mx-0 mx-[-1rem] md:rounded-xl rounded-none" className="md:mx-0 mx-[-1rem] md:rounded-xl rounded-none"
> >
{{ {{
summary: <SummaryTab temperature={temp} humidity={hum} soil={soil} gas={gas} lux={lux} forecastWeather={forecastWeather} forecastWeatherLoading={forecastWeatherLoading} onCardClick={handleCardClick} />, summary: <SummaryTab temperature={temp} humidity={hum} soil={soil} gas={gas} lux={lux} forecastWeather={forecastWeather} forecastWeatherLoading={forecastWeatherLoading} onCardClick={handleCardClick} visibleParams={visibleParams} />,
charts: ( charts: (
<Suspense fallback={<Loading message="در حال بارگذاری نمودارها..." />}> <Suspense fallback={<Loading message="در حال بارگذاری نمودارها..." />}>
<ChartsTab sortedTelemetry={sortedTelemetry} dataGaps={dataGaps} /> <ChartsTab sortedTelemetry={sortedTelemetry} dataGaps={dataGaps} visibleParams={visibleParams} />
</Suspense> </Suspense>
), ),
weather: selectedDate ? ( weather: selectedDate ? (
<Suspense fallback={<Loading message="در حال بارگذاری اطلاعات آب و هوا..." />}> <Suspense fallback={<Loading message="در حال بارگذاری اطلاعات آب و هوا..." />}>
<WeatherTab selectedDate={selectedDate} /> <WeatherTab selectedDate={selectedDate} />
</Suspense> </Suspense>
) : null, ) : null,
analysis: selectedDate ? ( analysis: selectedDate ? (
<Suspense fallback={<Loading message="در حال بارگذاری تحلیل..." />}> <Suspense fallback={<Loading message="در حال بارگذاری تحلیل..." />}>
<AnalysisTab deviceId={deviceId} selectedDate={selectedDate} /> <AnalysisTab deviceId={deviceId} selectedDate={selectedDate} />
</Suspense> </Suspense>
) : null, ) : null,
}} }}

View File

@@ -10,11 +10,22 @@ import { useTelemetryCharts } from '@/features/daily-report/hooks/useTelemetryCh
type ChartsTabProps = { type ChartsTabProps = {
sortedTelemetry: TelemetryDto[] sortedTelemetry: TelemetryDto[]
dataGaps?: DataGap[] dataGaps?: DataGap[]
visibleParams?: string[] | null
}
// Map server param keys to chart keys
const paramToChartKey: Record<string, string> = {
temperature: 'temp',
humidity: 'hum',
gas: 'gas',
soil: 'soil',
lux: 'lux',
} }
export const ChartsTab = memo(function ChartsTab({ export const ChartsTab = memo(function ChartsTab({
sortedTelemetry, sortedTelemetry,
dataGaps = [], dataGaps = [],
visibleParams,
}: ChartsTabProps) { }: ChartsTabProps) {
const [chartStartMinute, setChartStartMinute] = useState(0) const [chartStartMinute, setChartStartMinute] = useState(0)
const [chartEndMinute, setChartEndMinute] = useState(1439) const [chartEndMinute, setChartEndMinute] = useState(1439)
@@ -57,6 +68,12 @@ export const ChartsTab = memo(function ChartsTab({
filteredDataGaps, filteredDataGaps,
}) })
// If server returned a visibility list, compute allowed chart keys
const allowedChartKeys = useMemo(() => {
if (!visibleParams || visibleParams.length === 0) return null
return new Set(visibleParams.map(p => paramToChartKey[p] ?? p))
}, [visibleParams])
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<TimeRangeSelector <TimeRangeSelector
@@ -76,24 +93,26 @@ export const ChartsTab = memo(function ChartsTab({
/> />
) : ( ) : (
<div className="grid gap-6 md:grid-cols-2"> <div className="grid gap-6 md:grid-cols-2">
{charts.map(chart => ( {charts
<Panel key={chart.key} title={chart.title} id={`chart-${chart.key}`}> .filter(chart => !allowedChartKeys || allowedChartKeys.has(chart.key))
<LineChart .map(chart => (
labels={chartLabels} <Panel key={chart.key} title={chart.title} id={`chart-${chart.key}`}>
series={[ <LineChart
{ labels={chartLabels}
label: chart.seriesLabel, series={[
data: chart.data, {
borderColor: chart.color, label: chart.seriesLabel,
backgroundColor: chart.bgColor, data: chart.data,
fill: true, borderColor: chart.color,
}, backgroundColor: chart.bgColor,
]} fill: true,
yAxisMin={chart.yAxisMin} },
yAxisMax={chart.yAxisMax} ]}
/> yAxisMin={chart.yAxisMin}
</Panel> yAxisMax={chart.yAxisMax}
))} />
</Panel>
))}
</div> </div>
)} )}
</div> </div>

View File

@@ -15,9 +15,10 @@ type SummaryTabProps = {
forecastWeather?: WeatherData | null forecastWeather?: WeatherData | null
forecastWeatherLoading?: boolean forecastWeatherLoading?: boolean
onCardClick?: (param: string) => void onCardClick?: (param: string) => void
visibleParams?: string[] | null
} }
export function SummaryTab({ temperature, humidity, soil, gas, lux, forecastWeather, forecastWeatherLoading = false, onCardClick }: SummaryTabProps) { export function SummaryTab({ temperature, humidity, soil, gas, lux, forecastWeather, forecastWeatherLoading = false, onCardClick, visibleParams }: SummaryTabProps) {
const [isAlertsDialogOpen, setIsAlertsDialogOpen] = useState(false) const [isAlertsDialogOpen, setIsAlertsDialogOpen] = useState(false)
const alertsCount = useMemo(() => { const alertsCount = useMemo(() => {
@@ -99,46 +100,60 @@ export function SummaryTab({ temperature, humidity, soil, gas, lux, forecastWeat
{/* Summary Cards Grid */} {/* Summary Cards Grid */}
<div className="grid gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3"> <div className="grid gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
<SummaryCard {(!visibleParams || visibleParams.length === 0 || visibleParams.includes('temperature')) && (
param="temperature" <SummaryCard
currentValue={temperatureSummary.current} param="temperature"
minValue={temperatureSummary.min} currentValue={temperatureSummary.current}
maxValue={temperatureSummary.max} minValue={temperatureSummary.min}
data={temperature} maxValue={temperatureSummary.max}
onClick={() => onCardClick?.('temperature')} data={temperature}
/> onClick={() => onCardClick?.('temperature')}
<SummaryCard />
param="humidity" )}
currentValue={humiditySummary.current}
minValue={humiditySummary.min} {(!visibleParams || visibleParams.length === 0 || visibleParams.includes('humidity')) && (
maxValue={humiditySummary.max} <SummaryCard
data={humidity} param="humidity"
onClick={() => onCardClick?.('humidity')} currentValue={humiditySummary.current}
/> minValue={humiditySummary.min}
<SummaryCard maxValue={humiditySummary.max}
param="gas" data={humidity}
currentValue={gasSummary.current} onClick={() => onCardClick?.('humidity')}
minValue={gasSummary.min} />
maxValue={gasSummary.max} )}
data={gas}
onClick={() => onCardClick?.('gas')} {(!visibleParams || visibleParams.length === 0 || visibleParams.includes('gas')) && (
/> <SummaryCard
<SummaryCard param="gas"
param="soil" currentValue={gasSummary.current}
currentValue={soilSummary.current} minValue={gasSummary.min}
minValue={soilSummary.min} maxValue={gasSummary.max}
maxValue={soilSummary.max} data={gas}
data={soil} onClick={() => onCardClick?.('gas')}
onClick={() => onCardClick?.('soil')} />
/> )}
<SummaryCard
param="lux" {(!visibleParams || visibleParams.length === 0 || visibleParams.includes('soil')) && (
currentValue={luxSummary.current} <SummaryCard
minValue={luxSummary.min} param="soil"
maxValue={luxSummary.max} currentValue={soilSummary.current}
data={lux} minValue={soilSummary.min}
onClick={() => onCardClick?.('lux')} maxValue={soilSummary.max}
/> data={soil}
onClick={() => onCardClick?.('soil')}
/>
)}
{(!visibleParams || visibleParams.length === 0 || visibleParams.includes('lux')) && (
<SummaryCard
param="lux"
currentValue={luxSummary.current}
minValue={luxSummary.min}
maxValue={luxSummary.max}
data={lux}
onClick={() => onCardClick?.('lux')}
/>
)}
</div> </div>
{/* Alerts Dialog */} {/* Alerts Dialog */}

View File

@@ -83,6 +83,10 @@ export const api = {
getDailyReport: (deviceId: number, persianDate: string) => getDailyReport: (deviceId: number, persianDate: string) =>
http<DailyReportDto>(`${API_BASE}/api/DailyReport?deviceId=${deviceId}&persianDate=${encodeURIComponent(persianDate)}`), http<DailyReportDto>(`${API_BASE}/api/DailyReport?deviceId=${deviceId}&persianDate=${encodeURIComponent(persianDate)}`),
// NEW: get displayable parameters from server
getDisplayParameters: (deviceId: number) =>
http<string[]>(`${API_BASE}/api/display-parameters?deviceId=${deviceId}`),
// Alert Conditions // Alert Conditions
getAlertConditions: (deviceId: number) => { getAlertConditions: (deviceId: number) => {
return http<AlertConditionDto[]>(`${API_BASE}/api/alertconditions/device/${deviceId}`) return http<AlertConditionDto[]>(`${API_BASE}/api/alertconditions/device/${deviceId}`)

View File

@@ -24,6 +24,11 @@ export type TelemetryDto = {
persianMonth: number persianMonth: number
persianDate: string persianDate: string
deviceName?: string deviceName?: string
power?: number
oldPower?: number
batteryVoltage?: number
batteryPercent?: number
voltage?: number
serverTimestampUtc?: string serverTimestampUtc?: string
} }