mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
fix(medications): convert API-endpoint dialog status fetch to useQuery (#144)
The dialog's "load endpoint status" effect was triggering a react-hooks/set-state-in-effect lint error: setLoadingStatus(true) fired synchronously inside an effect, which the rule flags as a cascading-render risk. Convert the imperative useCallback + useEffect pair to a single useQuery — same fetch shape, but loading/data state now lives in TanStack Query and the effect goes away. The "Reload status" menu item calls refetch(); the toggle path uses queryClient.setQueryData to keep the displayed state in sync after a PUT, so users still see the new active-token count without a second roundtrip. Errors from the GET render through the same callout that toggle errors do. No user-visible change. Restores lint to 0 errors on main.
This commit is contained in:
1 parent
21bd46d60f
commit
6bb00556d6
1 file changed
+52
-45
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
@@ -485,20 +485,52 @@ function ApiEndpointDialog({
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const { t } = useTranslations();
|
||||
const queryClient = useQueryClient();
|
||||
type ExampleType = "curl" | "wget" | "fetch" | "powershell";
|
||||
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
const [activeTokenCount, setActiveTokenCount] = useState(0);
|
||||
const [loadingStatus, setLoadingStatus] = useState(false);
|
||||
const apiEndpointKey = ["medication-api-endpoint", medication?.id];
|
||||
|
||||
type ApiEndpointStatus = { enabled: boolean; activeTokenCount: number };
|
||||
|
||||
const {
|
||||
data: status,
|
||||
isFetching: loadingStatus,
|
||||
refetch: refetchStatus,
|
||||
error: statusError,
|
||||
} = useQuery<ApiEndpointStatus>({
|
||||
queryKey: apiEndpointKey,
|
||||
queryFn: async () => {
|
||||
const res = await fetch(
|
||||
`/api/medications/${medication!.id}/api-endpoint`,
|
||||
);
|
||||
const json = await res.json();
|
||||
if (!res.ok) {
|
||||
throw new Error(json.error || t("medications.statusLoadFailed"));
|
||||
}
|
||||
return {
|
||||
enabled: json.data.enabled === true,
|
||||
activeTokenCount: json.data.activeTokenCount ?? 0,
|
||||
};
|
||||
},
|
||||
enabled: !!medication,
|
||||
staleTime: 0,
|
||||
gcTime: 0,
|
||||
});
|
||||
|
||||
const enabled = status?.enabled ?? false;
|
||||
const activeTokenCount = status?.activeTokenCount ?? 0;
|
||||
|
||||
const [token, setToken] = useState<string | null>(null);
|
||||
const [toggling, setToggling] = useState(false);
|
||||
const [msg, setMsg] = useState<string | null>(null);
|
||||
const [copied, setCopied] = useState<string | null>(null);
|
||||
const [exampleType, setExampleType] = useState<ExampleType>("curl");
|
||||
|
||||
const displayMsg =
|
||||
msg ?? (statusError instanceof Error ? statusError.message : null);
|
||||
|
||||
function handleClose() {
|
||||
setEnabled(false);
|
||||
setActiveTokenCount(0);
|
||||
queryClient.removeQueries({ queryKey: apiEndpointKey });
|
||||
setToken(null);
|
||||
setMsg(null);
|
||||
setCopied(null);
|
||||
@@ -506,27 +538,6 @@ function ApiEndpointDialog({
|
||||
onClose();
|
||||
}
|
||||
|
||||
const loadStatus = useCallback(async () => {
|
||||
if (!medication) return;
|
||||
setLoadingStatus(true);
|
||||
setMsg(null);
|
||||
try {
|
||||
const res = await fetch(`/api/medications/${medication.id}/api-endpoint`);
|
||||
if (res.ok) {
|
||||
const json = await res.json();
|
||||
setEnabled(json.data.enabled === true);
|
||||
setActiveTokenCount(json.data.activeTokenCount ?? 0);
|
||||
} else {
|
||||
const json = await res.json();
|
||||
setMsg(json.error || t("medications.statusLoadFailed"));
|
||||
}
|
||||
} catch {
|
||||
setMsg(t("medications.statusLoadFailed"));
|
||||
} finally {
|
||||
setLoadingStatus(false);
|
||||
}
|
||||
}, [medication, t]);
|
||||
|
||||
async function toggleEndpoint(nextEnabled: boolean) {
|
||||
if (!medication) return;
|
||||
setToggling(true);
|
||||
@@ -547,15 +558,16 @@ function ApiEndpointDialog({
|
||||
return;
|
||||
}
|
||||
|
||||
setEnabled(json.data.enabled === true);
|
||||
if (typeof json.data.activeTokenCount === "number") {
|
||||
setActiveTokenCount(json.data.activeTokenCount);
|
||||
} else if (
|
||||
!json.data.enabled &&
|
||||
typeof json.data.revokedTokenCount === "number"
|
||||
) {
|
||||
setActiveTokenCount(0);
|
||||
}
|
||||
const nextStatus: ApiEndpointStatus = {
|
||||
enabled: json.data.enabled === true,
|
||||
activeTokenCount:
|
||||
typeof json.data.activeTokenCount === "number"
|
||||
? json.data.activeTokenCount
|
||||
: !json.data.enabled
|
||||
? 0
|
||||
: (status?.activeTokenCount ?? 0),
|
||||
};
|
||||
queryClient.setQueryData<ApiEndpointStatus>(apiEndpointKey, nextStatus);
|
||||
|
||||
if (json.data.token) {
|
||||
setToken(json.data.token);
|
||||
@@ -579,11 +591,6 @@ function ApiEndpointDialog({
|
||||
setTimeout(() => setCopied(null), 2000);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!medication) return;
|
||||
void loadStatus();
|
||||
}, [medication, loadStatus]);
|
||||
|
||||
const baseUrl =
|
||||
typeof window !== "undefined" ? window.location.origin : "https://...";
|
||||
const endpoint = `${baseUrl}/api/ingest/medication`;
|
||||
@@ -756,11 +763,11 @@ function ApiEndpointDialog({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{msg && (
|
||||
{displayMsg && (
|
||||
<p
|
||||
className={`text-sm ${msg === t("medications.apiEndpointActivated") || msg === t("medications.apiEndpointDeactivated") ? "text-dracula-green" : "text-destructive"}`}
|
||||
className={`text-sm ${displayMsg === t("medications.apiEndpointActivated") || displayMsg === t("medications.apiEndpointDeactivated") ? "text-dracula-green" : "text-destructive"}`}
|
||||
>
|
||||
{msg}
|
||||
{displayMsg}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -780,7 +787,7 @@ function ApiEndpointDialog({
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
void loadStatus();
|
||||
void refetchStatus();
|
||||
}}
|
||||
disabled={loadingStatus || toggling}
|
||||
>
|
||||
|
||||
Reference in new issue
Block a user