refactor(settings): move Jira connection test from admin Jira tab to Profile tab
Aegis CI / lint-and-test (push) Waiting to run
Snyk Security Scan / Python vulnerabilities (backend) (push) Waiting to run
Snyk Security Scan / npm vulnerabilities (frontend) (push) Waiting to run
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Waiting to run
Aegis CI / lint-and-test (push) Waiting to run
Snyk Security Scan / Python vulnerabilities (backend) (push) Waiting to run
Snyk Security Scan / npm vulnerabilities (frontend) (push) Waiting to run
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Waiting to run
This commit is contained in:
@@ -845,6 +845,8 @@ function ProfileSection() {
|
|||||||
const [showTempoToken, setShowTempoToken] = useState(false);
|
const [showTempoToken, setShowTempoToken] = useState(false);
|
||||||
const [tempoTestResult, setTempoTestResult] = useState<string | null>(null);
|
const [tempoTestResult, setTempoTestResult] = useState<string | null>(null);
|
||||||
const [tempoTestError, setTempoTestError] = useState<string | null>(null);
|
const [tempoTestError, setTempoTestError] = useState<string | null>(null);
|
||||||
|
const [jiraTestResult, setJiraTestResult] = useState<{ connectedAs: string; url: string } | null>(null);
|
||||||
|
const [jiraTestError, setJiraTestError] = useState<string | null>(null);
|
||||||
const [dirty, setDirty] = useState(false);
|
const [dirty, setDirty] = useState(false);
|
||||||
|
|
||||||
// Initialise editable fields from server on first successful load
|
// Initialise editable fields from server on first successful load
|
||||||
@@ -871,6 +873,23 @@ function ProfileSection() {
|
|||||||
onError: () => setToast({ msg: "Failed to save", type: "error" }),
|
onError: () => setToast({ msg: "Failed to save", type: "error" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const jiraTestMut = useMutation({
|
||||||
|
mutationFn: testJiraConnection,
|
||||||
|
onSuccess: (data) => {
|
||||||
|
if (data.status === "ok") {
|
||||||
|
setJiraTestResult({ connectedAs: data.connected_as ?? "", url: data.jira_url ?? "" });
|
||||||
|
setJiraTestError(null);
|
||||||
|
} else {
|
||||||
|
setJiraTestError((data as { message?: string }).message ?? "Connection failed");
|
||||||
|
setJiraTestResult(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError: (err: Error) => {
|
||||||
|
setJiraTestError(err.message || "Connection failed");
|
||||||
|
setJiraTestResult(null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const tempoTestMut = useMutation({
|
const tempoTestMut = useMutation({
|
||||||
mutationFn: testTempoConnection,
|
mutationFn: testTempoConnection,
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
@@ -1041,6 +1060,38 @@ function ProfileSection() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Test Jira connection */}
|
||||||
|
<div className="mt-4 rounded-lg border border-gray-700 bg-gray-800/50 p-3 space-y-2">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setJiraTestResult(null);
|
||||||
|
setJiraTestError(null);
|
||||||
|
jiraTestMut.mutate();
|
||||||
|
}}
|
||||||
|
disabled={jiraTestMut.isPending || !me?.jira_token_set}
|
||||||
|
className="flex items-center gap-2 rounded-lg border border-cyan-700 px-3 py-1.5 text-sm font-medium text-cyan-400 hover:bg-cyan-900/30 disabled:opacity-50 transition-colors"
|
||||||
|
>
|
||||||
|
{jiraTestMut.isPending ? (
|
||||||
|
<Loader2 className="h-4 w-4 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<TestTube className="h-4 w-4" />
|
||||||
|
)}
|
||||||
|
Test Jira Connection
|
||||||
|
</button>
|
||||||
|
{jiraTestResult && (
|
||||||
|
<div className="flex items-center gap-2 rounded-md bg-emerald-900/30 border border-emerald-800/50 px-3 py-2 text-sm text-emerald-300">
|
||||||
|
<CheckCircle className="h-4 w-4 shrink-0" />
|
||||||
|
Connected as: {jiraTestResult.connectedAs}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{jiraTestError && (
|
||||||
|
<div className="flex items-center gap-2 rounded-md bg-red-900/30 border border-red-800/50 px-3 py-2 text-sm text-red-300">
|
||||||
|
<XCircle className="h-4 w-4 shrink-0" />
|
||||||
|
{jiraTestError}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
{/* ── Tempo Integration ─────────────────────────────────── */}
|
{/* ── Tempo Integration ─────────────────────────────────── */}
|
||||||
@@ -1153,8 +1204,6 @@ function ProfileSection() {
|
|||||||
function JiraConfigSection() {
|
function JiraConfigSection() {
|
||||||
const qc = useQueryClient();
|
const qc = useQueryClient();
|
||||||
const [toast, setToast] = useState<{ msg: string; type: "success" | "error" } | null>(null);
|
const [toast, setToast] = useState<{ msg: string; type: "success" | "error" } | null>(null);
|
||||||
const [testResult, setTestResult] = useState<{ connectedAs: string; url: string } | null>(null);
|
|
||||||
const [testError, setTestError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
const { data: cfg, isLoading } = useQuery({
|
const { data: cfg, isLoading } = useQuery({
|
||||||
queryKey: ["jira-config"],
|
queryKey: ["jira-config"],
|
||||||
@@ -1174,24 +1223,6 @@ function JiraConfigSection() {
|
|||||||
onError: () => setToast({ msg: "Failed to save Jira configuration", type: "error" }),
|
onError: () => setToast({ msg: "Failed to save Jira configuration", type: "error" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const testMut = useMutation({
|
|
||||||
mutationFn: testJiraConnection,
|
|
||||||
onSuccess: (data) => {
|
|
||||||
// Backend always returns HTTP 200; status field tells us if it worked
|
|
||||||
if (data.status === "ok") {
|
|
||||||
setTestResult({ connectedAs: data.connected_as ?? "", url: data.jira_url ?? "" });
|
|
||||||
setTestError(null);
|
|
||||||
} else {
|
|
||||||
setTestError((data as { message?: string }).message ?? "Connection failed");
|
|
||||||
setTestResult(null);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: (err: Error) => {
|
|
||||||
setTestError(err.message || "Connection failed");
|
|
||||||
setTestResult(null);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-center py-8">
|
<div className="flex justify-center py-8">
|
||||||
@@ -1284,43 +1315,6 @@ function JiraConfigSection() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Test Jira connection */}
|
|
||||||
<div className="mt-4 rounded-lg border border-gray-700 bg-gray-800/50 p-4 space-y-3">
|
|
||||||
<p className="text-sm font-medium text-gray-300">Test Jira Connection</p>
|
|
||||||
<div className="rounded-md bg-blue-900/20 border border-blue-800/50 px-3 py-2 text-xs text-blue-300">
|
|
||||||
Uses your personal Jira API token (configured in the Profile tab)
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setTestResult(null);
|
|
||||||
setTestError(null);
|
|
||||||
testMut.mutate();
|
|
||||||
}}
|
|
||||||
disabled={testMut.isPending}
|
|
||||||
className="flex items-center gap-2 rounded-lg border border-cyan-700 px-4 py-2 text-sm font-medium text-cyan-400 hover:bg-cyan-900/30 disabled:opacity-50 transition-colors"
|
|
||||||
>
|
|
||||||
{testMut.isPending ? (
|
|
||||||
<Loader2 className="h-4 w-4 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<TestTube className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
Test Jira Connection
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{testResult && (
|
|
||||||
<div className="flex items-center gap-2 rounded-md bg-emerald-900/30 border border-emerald-800/50 px-3 py-2 text-sm text-emerald-300">
|
|
||||||
<CheckCircle className="h-4 w-4 shrink-0" />
|
|
||||||
Connected as: {testResult.connectedAs}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{testError && (
|
|
||||||
<div className="flex items-center gap-2 rounded-md bg-red-900/30 border border-red-800/50 px-3 py-2 text-sm text-red-300">
|
|
||||||
<XCircle className="h-4 w-4 shrink-0" />
|
|
||||||
{testError}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user