diff --git a/devfront/src/features/clients/ClientConsentsPage.tsx b/devfront/src/features/clients/ClientConsentsPage.tsx index e7e17685..75bafe57 100644 --- a/devfront/src/features/clients/ClientConsentsPage.tsx +++ b/devfront/src/features/clients/ClientConsentsPage.tsx @@ -80,6 +80,56 @@ function ClientConsentsPage() { return scopeFilter === "all" || row.grantedScopes.includes(scopeFilter); }); + const handleExportCSV = () => { + if (filteredRows.length === 0) return; + + const headers = [ + t("ui.dev.clients.consents.table.user", "User"), + t("ui.dev.clients.consents.table.tenant", "Tenant"), + t("ui.dev.clients.table.status", "Status"), + t("ui.dev.clients.consents.table.scopes", "Granted Scopes"), + t("ui.dev.clients.consents.table.first_granted", "First Granted"), + t( + "ui.dev.clients.consents.table.last_auth", + "Last Authenticated / Revoked", + ), + ]; + + const csvContent = [ + headers.join(","), + ...filteredRows.map((row) => { + const lastAuthRevoked = + row.status === "revoked" && row.deletedAt + ? `${t("ui.dev.clients.consents.status_revoked", "Revoked")}: ${new Date(row.deletedAt).toLocaleString()}` + : row.authenticatedAt + ? new Date(row.authenticatedAt).toLocaleString() + : "-"; + + return [ + `"${row.subject} (${row.userName || ""})"`, + `"${row.tenantName || row.tenantId || ""}"`, + `"${row.status}"`, + `"${row.grantedScopes.join(", ")}"`, + `"${new Date(row.createdAt).toLocaleString()}"`, + `"${lastAuthRevoked}"`, + ].join(","); + }), + ].join("\n"); + + const blob = new Blob(["\uFEFF" + csvContent], { + type: "text/csv;charset=utf-8;", + }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + const date = new Date().toISOString().split("T")[0]; + link.setAttribute("href", url); + link.setAttribute("download", `consents_${clientId}_${date}.csv`); + link.style.visibility = "hidden"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }; + return (
@@ -191,7 +241,11 @@ function ClientConsentsPage() { > {t("ui.common.search", "검색")} -
@@ -455,6 +509,8 @@ function ClientConsentsPage() { {rows.reduce((acc, row) => acc + row.grantedScopes.length, 0)} + +

{t(