Compare commits
2 Commits
62e164ad05
...
acd924da07
| Author | SHA1 | Date | |
|---|---|---|---|
| acd924da07 | |||
|
|
cc118d0913 |
@@ -144,7 +144,7 @@ describe('WellDashboardPage', () => {
|
|||||||
|
|
||||||
expect(getProjectFullConfig).toHaveBeenCalledWith('project-1')
|
expect(getProjectFullConfig).toHaveBeenCalledWith('project-1')
|
||||||
expect(fetchAssetTelemetryHistory).toHaveBeenCalledWith('well-1', 1, 100)
|
expect(fetchAssetTelemetryHistory).toHaveBeenCalledWith('well-1', 1, 100)
|
||||||
expect(fetchAssetTelemetry).not.toHaveBeenCalled()
|
expect(fetchAssetTelemetry).toHaveBeenCalledWith('well-1', 100)
|
||||||
expect(getAlarms).toHaveBeenCalledWith('project-1')
|
expect(getAlarms).toHaveBeenCalledWith('project-1')
|
||||||
expect(container.textContent).toContain('Pozo Norte')
|
expect(container.textContent).toContain('Pozo Norte')
|
||||||
expect(container.textContent).toContain('Activo')
|
expect(container.textContent).toContain('Activo')
|
||||||
@@ -564,10 +564,61 @@ describe('WellDashboardPage', () => {
|
|||||||
expect(lowFrequencySection?.textContent).not.toContain('No hay variables de baja frecuencia')
|
expect(lowFrequencySection?.textContent).not.toContain('No hay variables de baja frecuencia')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('includes LF capture fields from raw telemetry when history lacks origin metadata', async () => {
|
||||||
|
vi.mocked(fetchDashboardSummary).mockResolvedValue({
|
||||||
|
data: [{
|
||||||
|
asset_id: 'well-1',
|
||||||
|
asset: 'Pozo Norte',
|
||||||
|
source: 'SCADA',
|
||||||
|
frequency_class: 'HF',
|
||||||
|
last_updated: '2026-06-22T10:05:00Z',
|
||||||
|
telemetry: { values: { presion: 121 } },
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
vi.mocked(fetchAssetTelemetryHistory).mockResolvedValue({ records: [
|
||||||
|
{
|
||||||
|
asset_id: 'well-1',
|
||||||
|
time: '2026-06-22T10:00:00Z',
|
||||||
|
data: { values: { caudal_bopd: 84 } },
|
||||||
|
},
|
||||||
|
] })
|
||||||
|
vi.mocked(fetchAssetTelemetry).mockResolvedValue([
|
||||||
|
{
|
||||||
|
asset_id: 'well-1',
|
||||||
|
time: '2026-06-22T10:00:00Z',
|
||||||
|
source: 'PWA',
|
||||||
|
data: { values: { presion: 120, caudal_bopd: 84 } },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
root.render(
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<MemoryRouter initialEntries={['/app/admin/wells/well-1']}>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/app/admin/wells/:wellId" element={<WellDashboardPage />} />
|
||||||
|
</Routes>
|
||||||
|
</MemoryRouter>
|
||||||
|
</QueryClientProvider>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
await flushQueries()
|
||||||
|
await waitForText(container, 'Caudal bopd')
|
||||||
|
|
||||||
|
const lowFrequencySection = container.querySelector('section[aria-label="Variables de baja frecuencia"]')
|
||||||
|
|
||||||
|
expect(fetchAssetTelemetry).toHaveBeenCalledWith('well-1', 100)
|
||||||
|
expect(lowFrequencySection?.textContent).toContain('Caudal bopd')
|
||||||
|
expect(lowFrequencySection?.textContent).toContain('84')
|
||||||
|
expect(lowFrequencySection?.textContent).toContain('Captura PWA')
|
||||||
|
expect(lowFrequencySection?.textContent).not.toContain('No hay variables de baja frecuencia')
|
||||||
|
})
|
||||||
|
|
||||||
it('shows loading copy before rendering empty high and low frequency states', async () => {
|
it('shows loading copy before rendering empty high and low frequency states', async () => {
|
||||||
vi.mocked(getProjectFullConfig).mockReturnValue(new Promise(() => undefined) as ReturnType<typeof getProjectFullConfig>)
|
vi.mocked(getProjectFullConfig).mockReturnValue(new Promise(() => undefined) as ReturnType<typeof getProjectFullConfig>)
|
||||||
vi.mocked(fetchDashboardSummary).mockReturnValue(new Promise(() => undefined) as ReturnType<typeof fetchDashboardSummary>)
|
vi.mocked(fetchDashboardSummary).mockReturnValue(new Promise(() => undefined) as ReturnType<typeof fetchDashboardSummary>)
|
||||||
vi.mocked(fetchAssetTelemetryHistory).mockReturnValue(new Promise(() => undefined) as ReturnType<typeof fetchAssetTelemetryHistory>)
|
vi.mocked(fetchAssetTelemetryHistory).mockReturnValue(new Promise(() => undefined) as ReturnType<typeof fetchAssetTelemetryHistory>)
|
||||||
|
vi.mocked(fetchAssetTelemetry).mockReturnValue(new Promise(() => undefined) as ReturnType<typeof fetchAssetTelemetry>)
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
root.render(
|
root.render(
|
||||||
@@ -619,6 +670,6 @@ describe('WellDashboardPage', () => {
|
|||||||
expect(highFrequencySection?.textContent).not.toContain('presion')
|
expect(highFrequencySection?.textContent).not.toContain('presion')
|
||||||
expect(highFrequencySection?.textContent).not.toContain('Temperatura motor')
|
expect(highFrequencySection?.textContent).not.toContain('Temperatura motor')
|
||||||
expect(fetchAssetTelemetryHistory).toHaveBeenCalledWith('well-1', 1, 100)
|
expect(fetchAssetTelemetryHistory).toHaveBeenCalledWith('well-1', 1, 100)
|
||||||
expect(fetchAssetTelemetry).not.toHaveBeenCalled()
|
expect(fetchAssetTelemetry).toHaveBeenCalledWith('well-1', 100)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YA
|
|||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { useAuthStore } from '@/features/auth/stores/auth-store'
|
import { useAuthStore } from '@/features/auth/stores/auth-store'
|
||||||
import { getAlarms } from '@/features/admin/alerts/api/alerts-api'
|
import { getAlarms } from '@/features/admin/alerts/api/alerts-api'
|
||||||
import { fetchAssetTelemetryHistory, fetchDashboardSummary, type TelemetryHistoryRecord } from '../api/dashboard-api'
|
import { fetchAssetTelemetry, fetchAssetTelemetryHistory, fetchDashboardSummary, type TelemetryHistoryRecord } from '../api/dashboard-api'
|
||||||
import { DashboardEmptyState } from '../components/DashboardEmptyState'
|
import { DashboardEmptyState } from '../components/DashboardEmptyState'
|
||||||
import { TelemetryChart } from '../components/TelemetryChart'
|
import { TelemetryChart } from '../components/TelemetryChart'
|
||||||
import type { AssetData } from '../utils/telemetry-normalization'
|
import type { AssetData } from '../utils/telemetry-normalization'
|
||||||
@@ -269,6 +269,12 @@ export default function WellDashboardPage() {
|
|||||||
queryFn: () => fetchAssetTelemetryHistory(wellId!, 1, 100),
|
queryFn: () => fetchAssetTelemetryHistory(wellId!, 1, 100),
|
||||||
enabled: Boolean(token && wellId),
|
enabled: Boolean(token && wellId),
|
||||||
})
|
})
|
||||||
|
const rawTelemetryQuery = useQuery({
|
||||||
|
queryKey: ['well-dashboard-raw-telemetry', wellId],
|
||||||
|
queryFn: () => fetchAssetTelemetry(wellId!, 100),
|
||||||
|
enabled: Boolean(token && wellId),
|
||||||
|
refetchInterval: 30000,
|
||||||
|
})
|
||||||
|
|
||||||
const wellConfig = useMemo(() => {
|
const wellConfig = useMemo(() => {
|
||||||
return projectConfigQuery.data?.assets.find(({ asset }) => asset.id === wellId && asset.asset_type === 'POZO') ?? null
|
return projectConfigQuery.data?.assets.find(({ asset }) => asset.id === wellId && asset.asset_type === 'POZO') ?? null
|
||||||
@@ -284,6 +290,12 @@ export default function WellDashboardPage() {
|
|||||||
|
|
||||||
return records.map(normalizeHistoryRecord)
|
return records.map(normalizeHistoryRecord)
|
||||||
}, [historyQuery.data])
|
}, [historyQuery.data])
|
||||||
|
const rawTelemetry = useMemo(() => {
|
||||||
|
const raw = rawTelemetryQuery.data as TelemetryHistoryRecord[] | { records?: TelemetryHistoryRecord[] } | undefined
|
||||||
|
const records = Array.isArray(raw) ? raw : Array.isArray(raw?.records) ? raw.records : []
|
||||||
|
|
||||||
|
return records.map(normalizeHistoryRecord)
|
||||||
|
}, [rawTelemetryQuery.data])
|
||||||
const latestAssetData = useMemo<AssetData | null>(() => {
|
const latestAssetData = useMemo<AssetData | null>(() => {
|
||||||
const asset = summaryQuery.data?.data.find((item) => item.asset_id === wellId)
|
const asset = summaryQuery.data?.data.find((item) => item.asset_id === wellId)
|
||||||
if (asset) return asset
|
if (asset) return asset
|
||||||
@@ -331,14 +343,14 @@ export default function WellDashboardPage() {
|
|||||||
const remainingHighFrequencySignals = Math.max(highFrequencySignals.length - visibleHighFrequencySignals.length, 0)
|
const remainingHighFrequencySignals = Math.max(highFrequencySignals.length - visibleHighFrequencySignals.length, 0)
|
||||||
const lowFrequencySignals = useMemo(() => variableSignals.filter((signal) => signal.frequencyClass === 'low'), [variableSignals])
|
const lowFrequencySignals = useMemo(() => variableSignals.filter((signal) => signal.frequencyClass === 'low'), [variableSignals])
|
||||||
const lowFrequencyCaptureSignals = useMemo(() => {
|
const lowFrequencyCaptureSignals = useMemo(() => {
|
||||||
if (!latestAssetData && history.length === 0) return []
|
if (!latestAssetData && history.length === 0 && rawTelemetry.length === 0) return []
|
||||||
|
|
||||||
const catalogAliases = new Set(enabledVariables.map((variable) => variable.alias))
|
const catalogAliases = new Set(enabledVariables.map((variable) => variable.alias))
|
||||||
const captureFieldsByAlias = new Map<string, ReturnType<typeof deriveCaptureFields>[number]>()
|
const captureFieldsByAlias = new Map<string, ReturnType<typeof deriveCaptureFields>[number]>()
|
||||||
const captureCandidates: AssetData[] = []
|
const captureCandidates: AssetData[] = []
|
||||||
|
|
||||||
if (latestAssetData) captureCandidates.push(latestAssetData)
|
if (latestAssetData) captureCandidates.push(latestAssetData)
|
||||||
for (const record of history) {
|
for (const record of [...rawTelemetry, ...history]) {
|
||||||
if (!record.data || !wellId) continue
|
if (!record.data || !wellId) continue
|
||||||
if (record.asset_id && record.asset_id !== wellId) continue
|
if (record.asset_id && record.asset_id !== wellId) continue
|
||||||
|
|
||||||
@@ -372,7 +384,7 @@ export default function WellDashboardPage() {
|
|||||||
sparklinePath: '',
|
sparklinePath: '',
|
||||||
sparklineValues: [] as number[],
|
sparklineValues: [] as number[],
|
||||||
}))
|
}))
|
||||||
}, [enabledVariables, history, latestAssetData, resolvedProjectId, wellId, wellName])
|
}, [enabledVariables, history, latestAssetData, rawTelemetry, resolvedProjectId, wellId, wellName])
|
||||||
const allLowFrequencySignals = useMemo(() => [...lowFrequencySignals, ...lowFrequencyCaptureSignals], [lowFrequencyCaptureSignals, lowFrequencySignals])
|
const allLowFrequencySignals = useMemo(() => [...lowFrequencySignals, ...lowFrequencyCaptureSignals], [lowFrequencyCaptureSignals, lowFrequencySignals])
|
||||||
const gaugeSignals = useMemo(() => {
|
const gaugeSignals = useMemo(() => {
|
||||||
return variableSignals
|
return variableSignals
|
||||||
@@ -410,6 +422,7 @@ export default function WellDashboardPage() {
|
|||||||
.filter((point): point is { time: string; value: number } => typeof point.value === 'number' && Number.isFinite(point.value))
|
.filter((point): point is { time: string; value: number } => typeof point.value === 'number' && Number.isFinite(point.value))
|
||||||
}, [history, selectedSignal])
|
}, [history, selectedSignal])
|
||||||
const loading = projectConfigQuery.isLoading || summaryQuery.isLoading || historyQuery.isLoading
|
const loading = projectConfigQuery.isLoading || summaryQuery.isLoading || historyQuery.isLoading
|
||||||
|
const lowFrequencyLoading = loading || rawTelemetryQuery.isLoading
|
||||||
const hasProject = Boolean(resolvedProjectId)
|
const hasProject = Boolean(resolvedProjectId)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -766,7 +779,7 @@ export default function WellDashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
<span className="text-[11px] font-semibold uppercase tracking-[0.18em] text-[#9aa8ba]">{allLowFrequencySignals.length} señales</span>
|
<span className="text-[11px] font-semibold uppercase tracking-[0.18em] text-[#9aa8ba]">{allLowFrequencySignals.length} señales</span>
|
||||||
</div>
|
</div>
|
||||||
{loading && allLowFrequencySignals.length === 0 ? (
|
{lowFrequencyLoading && allLowFrequencySignals.length === 0 ? (
|
||||||
<p className="rounded-2xl border border-[#2a3038] bg-[#080b0f] p-4 text-sm text-[#9aa8ba]">Cargando variables de baja frecuencia…</p>
|
<p className="rounded-2xl border border-[#2a3038] bg-[#080b0f] p-4 text-sm text-[#9aa8ba]">Cargando variables de baja frecuencia…</p>
|
||||||
) : allLowFrequencySignals.length === 0 ? (
|
) : allLowFrequencySignals.length === 0 ? (
|
||||||
<p className="rounded-2xl border border-[#2a3038] bg-[#080b0f] p-4 text-sm text-[#9aa8ba]">No hay variables de baja frecuencia ni capturas LF para este pozo.</p>
|
<p className="rounded-2xl border border-[#2a3038] bg-[#080b0f] p-4 text-sm text-[#9aa8ba]">No hay variables de baja frecuencia ni capturas LF para este pozo.</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user