1
0
forked from baron/baron-sso

조직도 M2M조회 추가, 자동로그인 보완

This commit is contained in:
2026-05-13 13:44:30 +09:00
parent 72288f1d39
commit 8c2b2f71ef
29 changed files with 2985 additions and 81 deletions

View File

@@ -72,6 +72,13 @@ const AVAILABLE_SCOPES = [
descKey: "msg.admin.api_keys.scopes.tenant_write.desc",
descFallback: "테넌트 정보를 직접 제어합니다.",
},
{
id: "org-context:read",
labelKey: "ui.admin.api_keys.scopes.org_context_read.title",
labelFallback: "조직 Context 조회",
descKey: "msg.admin.api_keys.scopes.org_context_read.desc",
descFallback: "외부 연동앱이 OrgFront SSOT 조직 JSON을 조회합니다.",
},
];
function ApiKeyCreatePage() {

View File

@@ -7,6 +7,7 @@ import {
ChevronDown,
ChevronRight,
CornerDownRight,
Download,
ExternalLink,
FolderOpen,
LayoutDashboard,
@@ -72,6 +73,7 @@ import {
type TenantSummary,
type UserSummary,
createUser,
exportTenantsCSV,
fetchTenants,
fetchUsers,
updateTenant,
@@ -422,6 +424,24 @@ function TenantUserGroupsTab() {
const [isAddExistingOpen, setIsAddExistingOpen] = useState(false);
const [existingSearch, setExistingSearch] = useState("");
const exportChildrenMutation = useMutation({
mutationFn: (parentId: string) => exportTenantsCSV(true, parentId),
onSuccess: ({ blob, filename }) => {
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
},
onError: () =>
toast.error(
t("msg.admin.tenants.export_error", "테넌트 내보내기에 실패했습니다."),
),
});
// Data Fetching
const {
data: allTenantsData,
@@ -611,6 +631,16 @@ function TenantUserGroupsTab() {
<UserPlus size={16} className="mr-2" />
{t("ui.admin.users.list.add", "멤버 추가")}
</Button>
<Button
variant="outline"
size="sm"
onClick={() => exportChildrenMutation.mutate(selectedNode.id)}
disabled={exportChildrenMutation.isPending}
data-testid="tenant-subtree-export-btn"
>
<Download size={16} className="mr-2" />
{t("ui.admin.tenants.sub.export", "하위 조직 CSV")}
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>

View File

@@ -241,9 +241,9 @@ export async function deleteTenantsBulk(ids: string[]) {
});
}
export async function exportTenantsCSV(includeIds = false) {
export async function exportTenantsCSV(includeIds = false, parentId?: string) {
const response = await apiClient.get<Blob>("/v1/admin/tenants/export", {
params: { includeIds },
params: { includeIds, parentId: parentId || undefined },
responseType: "blob",
});
const dispositionHeader = response.headers["content-disposition"];