M8-R09: add meter close unbind and transfer workflows
This commit is contained in:
@@ -244,6 +244,8 @@ export type MeterSourcePatch = components['schemas']['MeterSourcePatch']
|
||||
export type MeterSourceChannelResponse = components['schemas']['MeterSourceChannelResponse']
|
||||
export type BindingResponse = components['schemas']['BindingResponse']
|
||||
export type BindingCreate = components['schemas']['BindingCreate']
|
||||
export type BindingTransferRequest = components['schemas']['BindingTransferRequest']
|
||||
export type MeterCloseRequest = components['schemas']['MeterCloseRequest']
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Query: list all energy contracts
|
||||
@@ -477,18 +479,9 @@ export function useDeclareMeter() {
|
||||
return useMutation({
|
||||
mutationFn: (body: MeterDeclareRequest) =>
|
||||
apiClient.POST('/api/energy/meters', { body }),
|
||||
onSuccess: (_data, variables) => {
|
||||
void qc.invalidateQueries({ queryKey: ['energy-meters'] })
|
||||
if (variables.source_channel_uuid) {
|
||||
void qc.invalidateQueries({ queryKey: ['energy-source-channels'] })
|
||||
void qc.invalidateQueries({ queryKey: ['energy-meter-bindings'] })
|
||||
void qc.invalidateQueries({ queryKey: ['energy-sources'] })
|
||||
void qc.invalidateQueries({ queryKey: ['expose-catalog'] })
|
||||
}
|
||||
// Invalidate cost-related queries: a new meter may trigger recompute server-side.
|
||||
void qc.invalidateQueries({ queryKey: ['energy-costs'] })
|
||||
void qc.invalidateQueries({ queryKey: ['energy-costs-summary'] })
|
||||
},
|
||||
// A meter_swap can hand a binding over even when the optional channel was
|
||||
// omitted from this request, so every lifecycle write shares this boundary.
|
||||
onSuccess: () => invalidateLifecycleQueries(qc),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -504,12 +497,7 @@ export function useUpdateMeter() {
|
||||
params: { path: { meter_id: id } },
|
||||
body,
|
||||
}),
|
||||
onSuccess: () => {
|
||||
void qc.invalidateQueries({ queryKey: ['energy-meters'] })
|
||||
// Retroactive started_at correction triggers recompute server-side.
|
||||
void qc.invalidateQueries({ queryKey: ['energy-costs'] })
|
||||
void qc.invalidateQueries({ queryKey: ['energy-costs-summary'] })
|
||||
},
|
||||
onSuccess: () => invalidateLifecycleQueries(qc),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -530,21 +518,26 @@ export function useSource(uuid: string | null) {
|
||||
const res = await apiClient.GET('/api/energy/sources/{source_uuid}', { params: { path: { source_uuid: uuid! } } }); return res.data
|
||||
}, refetchInterval: 3_000 })
|
||||
}
|
||||
function invalidateSourceQueries(qc: ReturnType<typeof useQueryClient>) {
|
||||
function invalidateLifecycleQueries(qc: ReturnType<typeof useQueryClient>) {
|
||||
void qc.invalidateQueries({ queryKey: ['energy-sources'] }); void qc.invalidateQueries({ queryKey: ['energy-source'] });
|
||||
void qc.invalidateQueries({ queryKey: ['energy-source-channels'] }); void qc.invalidateQueries({ queryKey: ['energy-meters'] });
|
||||
void qc.invalidateQueries({ queryKey: ['energy-channel-readings'] }); void qc.invalidateQueries({ queryKey: ['energy-meter-bindings'] })
|
||||
void qc.invalidateQueries({ queryKey: ['expose-catalog'] })
|
||||
void qc.invalidateQueries({ queryKey: ['energy-costs'] }); void qc.invalidateQueries({ queryKey: ['energy-costs-summary'] })
|
||||
void qc.invalidateQueries({ queryKey: ['meter-costs', 'thermal'] })
|
||||
void qc.invalidateQueries({ queryKey: ['meter-cost-summary', 'thermal'] })
|
||||
}
|
||||
export function useCreateSource() { const qc = useQueryClient(); return useMutation({ mutationFn: (body: MeterSourceCreate) => apiClient.POST('/api/energy/sources', { body }), onSuccess: () => invalidateSourceQueries(qc) }) }
|
||||
export function useUpdateSource() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ uuid, body }: { uuid: string; body: MeterSourcePatch }) => apiClient.PATCH('/api/energy/sources/{source_uuid}', { params: { path: { source_uuid: uuid } }, body }), onSuccess: () => invalidateSourceQueries(qc) }) }
|
||||
export function useDeleteSource() { const qc = useQueryClient(); return useMutation({ mutationFn: (uuid: string) => apiClient.DELETE('/api/energy/sources/{source_uuid}', { params: { path: { source_uuid: uuid } } }), onSuccess: () => invalidateSourceQueries(qc) }) }
|
||||
export function useDiscoverSource() { const qc = useQueryClient(); return useMutation({ mutationFn: (uuid: string) => apiClient.POST('/api/energy/sources/{source_uuid}/discover', { params: { path: { source_uuid: uuid } } }), onSuccess: () => invalidateSourceQueries(qc) }) }
|
||||
export function useCreateSource() { const qc = useQueryClient(); return useMutation({ mutationFn: (body: MeterSourceCreate) => apiClient.POST('/api/energy/sources', { body }), onSuccess: () => invalidateLifecycleQueries(qc) }) }
|
||||
export function useUpdateSource() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ uuid, body }: { uuid: string; body: MeterSourcePatch }) => apiClient.PATCH('/api/energy/sources/{source_uuid}', { params: { path: { source_uuid: uuid } }, body }), onSuccess: () => invalidateLifecycleQueries(qc) }) }
|
||||
export function useDeleteSource() { const qc = useQueryClient(); return useMutation({ mutationFn: (uuid: string) => apiClient.DELETE('/api/energy/sources/{source_uuid}', { params: { path: { source_uuid: uuid } } }), onSuccess: () => invalidateLifecycleQueries(qc) }) }
|
||||
export function useDiscoverSource() { const qc = useQueryClient(); return useMutation({ mutationFn: (uuid: string) => apiClient.POST('/api/energy/sources/{source_uuid}/discover', { params: { path: { source_uuid: uuid } } }), onSuccess: () => invalidateLifecycleQueries(qc) }) }
|
||||
export function useSourceChannels(uuid: string | null) { return useQuery({ queryKey: ['energy-source-channels', uuid], enabled: !!uuid, queryFn: async () => { const res = await apiClient.GET('/api/energy/sources/{source_uuid}/channels', { params: { path: { source_uuid: uuid! } } }); return res.data }, refetchInterval: 3_000 }) }
|
||||
export function useChannelReadings(sourceUuid: string | null, channelUuid: string | null) { return useQuery({ queryKey: ['energy-channel-readings', sourceUuid, channelUuid], enabled: !!sourceUuid && !!channelUuid, queryFn: async () => { const res = await apiClient.GET('/api/energy/sources/{source_uuid}/channels/{channel_uuid}/readings', { params: { path: { source_uuid: sourceUuid!, channel_uuid: channelUuid! }, query: { limit: 60 } } }); return res.data }, refetchInterval: 5_000 }) }
|
||||
export function useMeterBindings(id: number | null) { return useQuery({ queryKey: ['energy-meter-bindings', id], enabled: id != null, queryFn: async () => { const res = await apiClient.GET('/api/energy/meters/{meter_id}/bindings', { params: { path: { meter_id: id! } } }); return res.data } }) }
|
||||
export function useCreateBinding() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ id, body }: { id: number; body: BindingCreate }) => apiClient.POST('/api/energy/meters/{meter_id}/bindings', { params: { path: { meter_id: id } }, body }), onSuccess: () => invalidateSourceQueries(qc) }) }
|
||||
export function useCloseBinding() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ uuid, ended_at }: { uuid: string; ended_at: string }) => apiClient.PATCH('/api/energy/bindings/{binding_uuid}', { params: { path: { binding_uuid: uuid } }, body: { ended_at } }), onSuccess: () => invalidateSourceQueries(qc) }) }
|
||||
export function useCreateBinding() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ id, body }: { id: number; body: BindingCreate }) => apiClient.POST('/api/energy/meters/{meter_id}/bindings', { params: { path: { meter_id: id } }, body }), onSuccess: () => invalidateLifecycleQueries(qc) }) }
|
||||
export function useCloseBinding() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ uuid, ended_at }: { uuid: string; ended_at: string }) => apiClient.PATCH('/api/energy/bindings/{binding_uuid}', { params: { path: { binding_uuid: uuid } }, body: { ended_at } }), onSuccess: () => invalidateLifecycleQueries(qc) }) }
|
||||
export function useCloseMeter() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ id, ended_at }: { id: number; ended_at: string }) => apiClient.POST('/api/energy/meters/{meter_id}/close', { params: { path: { meter_id: id } }, body: { ended_at } satisfies MeterCloseRequest }), onSuccess: () => invalidateLifecycleQueries(qc) }) }
|
||||
export function useTransferBinding() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ id, body }: { id: number; body: BindingTransferRequest }) => apiClient.POST('/api/energy/meters/{meter_id}/bindings/transfer', { params: { path: { meter_id: id } }, body }), onSuccess: () => invalidateLifecycleQueries(qc) }) }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Query: time-range readings for a device (window + limit — never full-table)
|
||||
|
||||
Reference in New Issue
Block a user