fix(settings): use useEffect for jira field init, fix token save UX
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Replace render-body setState with useEffect so field initialisation is idiomatic React and never races with user input. Also clarifies placeholder text: empty token field = keep current, not clear it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
Settings,
|
Settings,
|
||||||
@@ -820,21 +820,24 @@ function ProfileSection() {
|
|||||||
const [jiraApiToken, setJiraApiToken] = useState<string>("");
|
const [jiraApiToken, setJiraApiToken] = useState<string>("");
|
||||||
const [showToken, setShowToken] = useState(false);
|
const [showToken, setShowToken] = useState(false);
|
||||||
const [dirty, setDirty] = useState(false);
|
const [dirty, setDirty] = useState(false);
|
||||||
const [initialised, setInitialised] = useState(false);
|
|
||||||
|
|
||||||
// Sync from server on first load
|
// Initialise editable fields from server on first successful load
|
||||||
if (me && !initialised) {
|
useEffect(() => {
|
||||||
|
if (me) {
|
||||||
setJiraAccountId(me.jira_account_id ?? "");
|
setJiraAccountId(me.jira_account_id ?? "");
|
||||||
setJiraEmail(me.jira_email ?? "");
|
setJiraEmail(me.jira_email ?? "");
|
||||||
setInitialised(true);
|
// 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({
|
const saveMut = useMutation({
|
||||||
mutationFn: updateMyPreferences,
|
mutationFn: updateMyPreferences,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
qc.invalidateQueries({ queryKey: ["me-prefs"] });
|
qc.invalidateQueries({ queryKey: ["me-prefs"] });
|
||||||
setDirty(false);
|
setDirty(false);
|
||||||
setJiraApiToken("");
|
setJiraApiToken(""); // clear token field after save — it's now persisted
|
||||||
setToast({ msg: "Profile settings saved", type: "success" });
|
setToast({ msg: "Profile settings saved", type: "success" });
|
||||||
},
|
},
|
||||||
onError: () => setToast({ msg: "Failed to save", type: "error" }),
|
onError: () => setToast({ msg: "Failed to save", type: "error" }),
|
||||||
@@ -845,9 +848,10 @@ function ProfileSection() {
|
|||||||
jira_account_id: jiraAccountId || null,
|
jira_account_id: jiraAccountId || null,
|
||||||
jira_email: jiraEmail || null,
|
jira_email: jiraEmail || null,
|
||||||
};
|
};
|
||||||
// Only send token if user typed something (empty string clears it)
|
// Only send token when the user has typed something new
|
||||||
if (jiraApiToken !== "") {
|
// (empty field = "keep current token unchanged")
|
||||||
payload.jira_api_token = jiraApiToken;
|
if (jiraApiToken.trim() !== "") {
|
||||||
|
payload.jira_api_token = jiraApiToken.trim();
|
||||||
}
|
}
|
||||||
saveMut.mutate(payload);
|
saveMut.mutate(payload);
|
||||||
};
|
};
|
||||||
@@ -936,7 +940,7 @@ function ProfileSection() {
|
|||||||
setJiraApiToken(e.target.value);
|
setJiraApiToken(e.target.value);
|
||||||
setDirty(true);
|
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"
|
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"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user