diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx index e6b54b5..ad6e49b 100644 --- a/frontend/src/pages/SettingsPage.tsx +++ b/frontend/src/pages/SettingsPage.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Settings, @@ -820,21 +820,24 @@ function ProfileSection() { const [jiraApiToken, setJiraApiToken] = useState(""); const [showToken, setShowToken] = useState(false); const [dirty, setDirty] = useState(false); - const [initialised, setInitialised] = useState(false); - // Sync from server on first load - if (me && !initialised) { - setJiraAccountId(me.jira_account_id ?? ""); - setJiraEmail(me.jira_email ?? ""); - setInitialised(true); - } + // Initialise editable fields from server on first successful load + useEffect(() => { + if (me) { + setJiraAccountId(me.jira_account_id ?? ""); + setJiraEmail(me.jira_email ?? ""); + // Never pre-fill the token — we only know whether it is set, not its value + } + // Only run when `me` transitions from undefined → data (i.e., first load) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [!!me]); const saveMut = useMutation({ mutationFn: updateMyPreferences, onSuccess: () => { qc.invalidateQueries({ queryKey: ["me-prefs"] }); setDirty(false); - setJiraApiToken(""); + setJiraApiToken(""); // clear token field after save — it's now persisted setToast({ msg: "Profile settings saved", type: "success" }); }, onError: () => setToast({ msg: "Failed to save", type: "error" }), @@ -845,9 +848,10 @@ function ProfileSection() { jira_account_id: jiraAccountId || null, jira_email: jiraEmail || null, }; - // Only send token if user typed something (empty string clears it) - if (jiraApiToken !== "") { - payload.jira_api_token = jiraApiToken; + // Only send token when the user has typed something new + // (empty field = "keep current token unchanged") + if (jiraApiToken.trim() !== "") { + payload.jira_api_token = jiraApiToken.trim(); } saveMut.mutate(payload); }; @@ -936,7 +940,7 @@ function ProfileSection() { setJiraApiToken(e.target.value); setDirty(true); }} - placeholder={me?.jira_token_set ? "•••••••••••• (leave blank to keep current)" : "Paste your Atlassian API token"} + placeholder={me?.jira_token_set ? "Leave blank to keep current token" : "Paste your Atlassian API token here"} className="w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2 pr-10 text-sm text-gray-200 placeholder-gray-600 focus:border-cyan-500 focus:outline-none" />