From 765bf67cab6d478dfbac5b216326441d2f8ffb19 Mon Sep 17 00:00:00 2001 From: kyy Date: Thu, 29 Jan 2026 17:05:16 +0900 Subject: [PATCH] =?UTF-8?q?=ED=81=B4=EB=9D=BC=EC=9D=B4=EC=96=B8=ED=8A=B8?= =?UTF-8?q?=20=EA=B4=80=EB=A6=AC=20=ED=8E=98=EC=9D=B4=EC=A7=80=20ui/ux=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../features/clients/ClientConsentsPage.tsx | 2 +- .../features/clients/ClientDetailsPage.tsx | 275 +++++++----- .../features/clients/ClientGeneralPage.tsx | 413 ++++++++---------- devfront/src/features/clients/ClientsPage.tsx | 9 +- 5 files changed, 341 insertions(+), 359 deletions(-) diff --git a/.gitignore b/.gitignore index 276b51e6..a4d2df8d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # General .env +.temp .DS_Store .idea/ .vscode/ diff --git a/devfront/src/features/clients/ClientConsentsPage.tsx b/devfront/src/features/clients/ClientConsentsPage.tsx index 3f1f60fa..36aa843d 100644 --- a/devfront/src/features/clients/ClientConsentsPage.tsx +++ b/devfront/src/features/clients/ClientConsentsPage.tsx @@ -109,7 +109,7 @@ function ClientConsentsPage() { to={`/clients/${clientId}`} className="whitespace-nowrap border-b-2 border-transparent text-muted-foreground hover:text-foreground" > - Overview + Connection Consent & Users diff --git a/devfront/src/features/clients/ClientDetailsPage.tsx b/devfront/src/features/clients/ClientDetailsPage.tsx index 5e8c99f5..48035dd4 100644 --- a/devfront/src/features/clients/ClientDetailsPage.tsx +++ b/devfront/src/features/clients/ClientDetailsPage.tsx @@ -1,10 +1,10 @@ -import { useQuery } from "@tanstack/react-query"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import type { AxiosError } from "axios"; -import { AlertCircle, Copy, Eye, Link2, Shield, Workflow } from "lucide-react"; +import { AlertCircle, Copy, Eye, Link2, Shield, Workflow, Save } from "lucide-react"; import { Link, useParams } from "react-router-dom"; import { Badge } from "../../components/ui/badge"; import { Button } from "../../components/ui/button"; -import { Card, CardContent } from "../../components/ui/card"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "../../components/ui/card"; import { Separator } from "../../components/ui/separator"; import { Table, @@ -12,17 +12,47 @@ import { TableCell, TableRow, } from "../../components/ui/table"; -import { fetchClient } from "../../lib/devApi"; +import { Textarea } from "../../components/ui/textarea"; +import { Label } from "../../components/ui/label"; +import { fetchClient, updateClient } from "../../lib/devApi"; +import { useState, useEffect } from "react"; function ClientDetailsPage() { const params = useParams(); + const queryClient = useQueryClient(); const clientId = params.id ?? ""; + const { data, isLoading, error } = useQuery({ queryKey: ["client", clientId], queryFn: () => fetchClient(clientId), enabled: clientId.length > 0, }); + const [redirectUris, setRedirectUris] = useState(""); + + useEffect(() => { + if (data?.client?.redirectUris) { + setRedirectUris(data.client.redirectUris.join(", ")); + } + }, [data]); + + const mutation = useMutation({ + mutationFn: () => { + const uriList = redirectUris + .split(",") + .map((u) => u.trim()) + .filter(Boolean); + return updateClient(clientId, { redirectUris: uriList }); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["client", clientId] }); + alert("Redirect URIs가 저장되었습니다."); + }, + onError: (err) => { + alert(`저장 실패: ${(err as Error).message}`); + }, + }); + if (!clientId) { return
Client ID가 필요합니다.
; } @@ -81,7 +111,7 @@ function ClientDetailsPage() { to={`/clients/${clientId}`} className="border-b-2 border-primary pb-3 text-sm font-bold text-primary" > - Overview + Connection -
-

클라이언트 자격 증명

- - -
-

- Client ID -

-

{data.client.id}

-
- -
-
- - - -
-

- Client Secret -

-

- •••••••••••••••• -

-
-
- - - -
-
-
-
- -
-
-

OIDC 엔드포인트

- - - 읽기 전용 - -
- - - - {endpoints.map((endpoint) => ( - - -

- {endpoint.label} -

-
- - - {endpoint.value} - - - -
- ))} -
-
-
-
+ + -
-
-
-
- -
-
-

보안 메모

-

- 엔드포인트는 읽기 전용으로 유지하고, 비밀키 재발행/복사는 감사 - 로그와 연계하세요. -

-
+ + +
+

+ Client Secret +

+
+

••••••••••••••••

+
+ + +
+
+
+ +
-
- - - 감사 이벤트 필요 - + +
+
+

OIDC 엔드포인트

+ + + 읽기 전용 + +
+ + + + {endpoints.map((endpoint) => ( + + +

+ {endpoint.label} +

+
+ + + {endpoint.value} + + + +
+ ))} +
+
+
+
+
+ +
+
+

리디렉션 URI 설정

+ + + Redirect URIs + + 인증 성공 후 사용자를 리다이렉트할 허용된 URL 목록입니다. 콤마(,)로 구분하여 여러 개 입력할 수 있습니다. + + + +
+ +