From d60bc1d5d5f54005b2cb8a4464c72526bb530b2b Mon Sep 17 00:00:00 2001 From: kyy Date: Thu, 26 Feb 2026 13:24:50 +0900 Subject: [PATCH] =?UTF-8?q?=EC=97=B0=EB=8F=99=20=EC=95=B1=20=EB=B0=8F=20Co?= =?UTF-8?q?nsent=20=EB=AA=A9=EB=A1=9D=20=EC=83=81=EC=84=B8=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/clients/ClientConsentsPage.tsx | 163 ++++++++++++------ devfront/src/features/clients/ClientsPage.tsx | 136 ++++++++++++--- .../clients/routes/ClientFederationPage.tsx | 2 +- 3 files changed, 224 insertions(+), 77 deletions(-) diff --git a/devfront/src/features/clients/ClientConsentsPage.tsx b/devfront/src/features/clients/ClientConsentsPage.tsx index 113855d7..e7e17685 100644 --- a/devfront/src/features/clients/ClientConsentsPage.tsx +++ b/devfront/src/features/clients/ClientConsentsPage.tsx @@ -27,6 +27,7 @@ import { } from "../../components/ui/table"; import { fetchClient, fetchConsents, revokeConsent } from "../../lib/devApi"; import { t } from "../../lib/i18n"; +import { cn } from "../../lib/utils"; function ClientConsentsPage() { const params = useParams(); @@ -34,6 +35,8 @@ function ClientConsentsPage() { const [subjectInput, setSubjectInput] = useState(""); const [subject, setSubject] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); + const [scopeFilter, setScopeFilter] = useState("all"); + const [isAdvancedFilterOpen, setIsAdvancedFilterOpen] = useState(false); const { data: clientData } = useQuery({ queryKey: ["client", clientId], @@ -72,6 +75,10 @@ function ClientConsentsPage() { }; const rows = consentsData?.items ?? []; + const allScopes = Array.from(new Set(rows.flatMap((r) => r.grantedScopes))); + const filteredRows = rows.filter((row) => { + return scopeFilter === "all" || row.grantedScopes.includes(scopeFilter); + }); return (
@@ -147,59 +154,105 @@ function ClientConsentsPage() { - -
-
- - +
+
+
+ + setSubjectInput(e.target.value)} + /> +
+
+
+
-
- - {t("ui.dev.clients.consents.status_label", "Status:")} - - + + {t( + "ui.dev.clients.consents.filters.advanced", + "Advanced Filters", + )} + + +
-
- - - -
+ + {isAdvancedFilterOpen && ( +
+
+ + {t("ui.dev.clients.consents.status_label", "Status:")} + + +
+ +
+ + {t("ui.dev.clients.consents.scope_label", "Scope:")} + + +
+ + +
+ )} @@ -254,14 +307,14 @@ function ClientConsentsPage() { - {rows.length === 0 && !isLoading ? ( + {filteredRows.length === 0 && !isLoading ? ( {t("msg.dev.clients.consents.empty", "No consents found.")} ) : ( - rows.map((row) => ( + filteredRows.map((row) => ( 0 ? 1 : 0, - to: rows.length, + from: filteredRows.length > 0 ? 1 : 0, + to: filteredRows.length, total: rows.length, }, )} @@ -366,7 +419,7 @@ function ClientConsentsPage() { -
-
-
- - -
-
- - {t("ui.dev.clients.badge.tenant_selected", "테넌트: 선택됨")} - - - {t("ui.dev.clients.badge.admin_session", "관리자 세션")} - +
+
+
+ + setSearchQuery(e.target.value)} + /> +
+
+ +
+ + {t("ui.dev.clients.badge.tenant_selected", "테넌트: 선택됨")} + + + {t("ui.dev.clients.badge.admin_session", "관리자 세션")} + +
+
+ + {isAdvancedFilterOpen && ( +
+
+ + {t("ui.dev.clients.filter.type_label", "Type:")} + + +
+
+ + {t("ui.dev.clients.consents.status_label", "Status:")} + + +
+ +
+ )}
@@ -222,7 +318,7 @@ function ClientsPage() { - {clients.map((client) => ( + {filteredClients.map((client) => (
diff --git a/devfront/src/features/clients/routes/ClientFederationPage.tsx b/devfront/src/features/clients/routes/ClientFederationPage.tsx index ac6c14a8..c61c20be 100644 --- a/devfront/src/features/clients/routes/ClientFederationPage.tsx +++ b/devfront/src/features/clients/routes/ClientFederationPage.tsx @@ -1,5 +1,5 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { Plus, Trash2, Edit, Globe, Save } from "lucide-react"; +import { Edit, Globe, Plus, Save, Trash2 } from "lucide-react"; import { useState } from "react"; import { useParams } from "react-router-dom"; import { Button } from "../../../components/ui/button";