1
0
forked from baron/baron-sso

af 린트 적용

This commit is contained in:
2026-02-23 17:45:24 +09:00
parent 4011a65683
commit d525895ae7
5 changed files with 65 additions and 35 deletions

View File

@@ -63,7 +63,8 @@ const RoleSwitcher: FC = () => {
border: "1px solid #333", border: "1px solid #333",
}} }}
> >
<div <button
type="button"
style={{ style={{
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
@@ -73,6 +74,11 @@ const RoleSwitcher: FC = () => {
fontWeight: "bold", fontWeight: "bold",
paddingBottom: isCollapsed ? "0" : "4px", paddingBottom: isCollapsed ? "0" : "4px",
borderBottom: isCollapsed ? "none" : "1px solid #444", borderBottom: isCollapsed ? "none" : "1px solid #444",
background: "transparent",
border: "none",
width: "100%",
color: "inherit",
textAlign: "inherit",
}} }}
onClick={toggleCollapse} onClick={toggleCollapse}
> >
@@ -88,7 +94,7 @@ const RoleSwitcher: FC = () => {
)} )}
</div> </div>
{isCollapsed ? <ChevronUp size={14} /> : <ChevronDown size={14} />} {isCollapsed ? <ChevronUp size={14} /> : <ChevronDown size={14} />}
</div> </button>
{!isCollapsed && ( {!isCollapsed && (
<div <div

View File

@@ -1,4 +1,8 @@
import { useMutation, useQuery } from "@tanstack/react-query"; import {
type UseMutationResult,
useMutation,
useQuery,
} from "@tanstack/react-query";
import type { AxiosError } from "axios"; import type { AxiosError } from "axios";
import { import {
ChevronDown, ChevronDown,
@@ -57,15 +61,15 @@ function buildGroupTree(
const childrenOf = new Map<string, UserGroupNode[]>(); const childrenOf = new Map<string, UserGroupNode[]>();
// First pass: Initialize all groups as nodes and populate childrenOf map // First pass: Initialize all groups as nodes and populate childrenOf map
groups.forEach((group) => { for (const group of groups) {
childrenOf.set(group.id, []); childrenOf.set(group.id, []);
}); }
// Second pass: Populate children // Second pass: Populate children
groups.forEach((group) => { for (const group of groups) {
const node: UserGroupNode = { const node: UserGroupNode = {
...group, ...group,
children: childrenOf.get(group.id)!, children: childrenOf.get(group.id) ?? [],
}; };
if (group.parentId === parentId) { if (group.parentId === parentId) {
nodes.push(node); nodes.push(node);
@@ -79,13 +83,13 @@ function buildGroupTree(
nodes.push(node); nodes.push(node);
} }
} }
}); }
// Sort children for consistent rendering (optional, but good for UI) // Sort children for consistent rendering (optional, but good for UI)
nodes.sort((a, b) => a.name.localeCompare(b.name)); nodes.sort((a, b) => a.name.localeCompare(b.name));
nodes.forEach((node) => { for (const node of nodes) {
node.children.sort((a, b) => a.name.localeCompare(b.name)); node.children.sort((a, b) => a.name.localeCompare(b.name));
}); }
return nodes; return nodes;
} }
@@ -97,8 +101,16 @@ interface UserGroupTreeNodeProps {
selectedGroupId: string | null; selectedGroupId: string | null;
onDelete: (groupId: string) => void; onDelete: (groupId: string) => void;
onAddSubGroup: (parentId: string) => void; onAddSubGroup: (parentId: string) => void;
addMemberMutation: any; // Simplified type for now addMemberMutation: UseMutationResult<
removeMemberMutation: any; // Simplified type for now void,
AxiosError<{ error?: string }>,
{ groupId: string; userId: string }
>;
removeMemberMutation: UseMutationResult<
void,
AxiosError<{ error?: string }>,
{ groupId: string; userId: string }
>;
} }
const UserGroupTreeNode: React.FC<UserGroupTreeNodeProps> = ({ const UserGroupTreeNode: React.FC<UserGroupTreeNodeProps> = ({

View File

@@ -38,7 +38,9 @@ function buildTenantTree(tenants: TenantSummary[]): TenantNode[] {
} }
for (const tenant of tenants) { for (const tenant of tenants) {
const node = tenantMap.get(tenant.id)!; const node = tenantMap.get(tenant.id);
if (!node) continue;
if (tenant.parentId) { if (tenant.parentId) {
const parent = tenantMap.get(tenant.parentId); const parent = tenantMap.get(tenant.parentId);
if (parent) { if (parent) {

View File

@@ -50,23 +50,29 @@ function buildGroupTree(groups: GroupSummary[]): UserGroupNode[] {
const nodeMap = new Map<string, UserGroupNode>(); const nodeMap = new Map<string, UserGroupNode>();
const rootNodes: UserGroupNode[] = []; const rootNodes: UserGroupNode[] = [];
groups.forEach((group) => { for (const group of groups) {
nodeMap.set(group.id, { ...group, children: [] }); nodeMap.set(group.id, { ...group, children: [] });
}); }
for (const group of groups) {
const node = nodeMap.get(group.id);
if (!node) continue;
groups.forEach((group) => {
const node = nodeMap.get(group.id)!;
if (group.parentId && nodeMap.has(group.parentId)) { if (group.parentId && nodeMap.has(group.parentId)) {
const parent = nodeMap.get(group.parentId)!; const parent = nodeMap.get(group.parentId);
parent.children.push(node); if (parent) {
parent.children.push(node);
}
} else { } else {
rootNodes.push(node); rootNodes.push(node);
} }
}); }
const sortNodes = (nodes: UserGroupNode[]) => { const sortNodes = (nodes: UserGroupNode[]) => {
nodes.sort((a, b) => a.name.localeCompare(b.name)); nodes.sort((a, b) => a.name.localeCompare(b.name));
nodes.forEach((node) => sortNodes(node.children)); for (const node of nodes) {
sortNodes(node.children);
}
}; };
sortNodes(rootNodes); sortNodes(rootNodes);
@@ -227,7 +233,7 @@ export function TenantUserGroupsTab() {
groupsQuery.refetch(); groupsQuery.refetch();
if ( if (
selectedGroupId && selectedGroupId &&
selectedGroupId === (deleteMutation.variables as any) selectedGroupId === deleteMutation.variables
) { ) {
setSelectedGroupId(null); setSelectedGroupId(null);
} }

View File

@@ -1,4 +1,5 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type { AxiosError } from "axios";
import { ArrowLeft, Shield, Trash2, UserPlus, Users } from "lucide-react"; import { ArrowLeft, Shield, Trash2, UserPlus, Users } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { Link, useParams } from "react-router-dom"; import { Link, useParams } from "react-router-dom";
@@ -62,14 +63,13 @@ export function UserGroupDetailPage() {
const [selectedTargetTenantId, setSelectedTargetTenantId] = useState(""); const [selectedTargetTenantId, setSelectedTargetTenantId] = useState("");
const [selectedRelation, setSelectedRelation] = useState("view"); const [selectedRelation, setSelectedRelation] = useState("view");
// Fetch specific group details
const { const {
data: currentGroup, data: currentGroup,
isLoading: isGroupLoading, isLoading: isGroupLoading,
error, error,
} = useQuery({ } = useQuery({
queryKey: ["user-group-detail", id], queryKey: ["user-group-detail", id],
queryFn: () => fetchGroup(tenantId!, id!), queryFn: () => fetchGroup(tenantId ?? "", id ?? ""),
enabled: !!id && !!tenantId, enabled: !!id && !!tenantId,
retry: false, retry: false,
}); });
@@ -77,7 +77,7 @@ export function UserGroupDetailPage() {
// Fetch assigned roles // Fetch assigned roles
const { data: groupRoles, isLoading: isRolesLoading } = useQuery({ const { data: groupRoles, isLoading: isRolesLoading } = useQuery({
queryKey: ["user-group-roles", id], queryKey: ["user-group-roles", id],
queryFn: () => fetchGroupRoles(tenantId!, id!), queryFn: () => fetchGroupRoles(tenantId ?? "", id ?? ""),
enabled: !!id && !!tenantId, enabled: !!id && !!tenantId,
}); });
@@ -96,7 +96,7 @@ export function UserGroupDetailPage() {
}); });
const addMemberMutation = useMutation({ const addMemberMutation = useMutation({
mutationFn: (userId: string) => addGroupMember(tenantId!, id!, userId), mutationFn: (userId: string) => addGroupMember(tenantId ?? "", id ?? "", userId),
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["user-group-detail", id] }); queryClient.invalidateQueries({ queryKey: ["user-group-detail", id] });
setIsAddMemberOpen(false); setIsAddMemberOpen(false);
@@ -105,15 +105,17 @@ export function UserGroupDetailPage() {
t("msg.admin.groups.members.add_success", "구성원이 추가되었습니다."), t("msg.admin.groups.members.add_success", "구성원이 추가되었습니다."),
); );
}, },
onError: (error: any) => { onError: (error: AxiosError<{ error?: string }>) => {
toast.error( toast.error(
error.message || t("err.common.unknown", "오류가 발생했습니다."), error.response?.data?.error ||
error.message ||
t("err.common.unknown", "오류가 발생했습니다."),
); );
}, },
}); });
const removeMemberMutation = useMutation({ const removeMemberMutation = useMutation({
mutationFn: (userId: string) => removeGroupMember(tenantId!, id!, userId), mutationFn: (userId: string) => removeGroupMember(tenantId ?? "", id ?? "", userId),
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["user-group-detail", id] }); queryClient.invalidateQueries({ queryKey: ["user-group-detail", id] });
toast.success( toast.success(
@@ -127,7 +129,7 @@ export function UserGroupDetailPage() {
const assignRoleMutation = useMutation({ const assignRoleMutation = useMutation({
mutationFn: () => mutationFn: () =>
assignGroupRole(tenantId!, id!, selectedTargetTenantId, selectedRelation), assignGroupRole(tenantId ?? "", id ?? "", selectedTargetTenantId, selectedRelation),
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["user-group-roles", id] }); queryClient.invalidateQueries({ queryKey: ["user-group-roles", id] });
setIsAddRoleOpen(false); setIsAddRoleOpen(false);
@@ -135,16 +137,18 @@ export function UserGroupDetailPage() {
t("msg.admin.groups.roles.assign_success", "역할이 할당되었습니다."), t("msg.admin.groups.roles.assign_success", "역할이 할당되었습니다."),
); );
}, },
onError: (error: any) => { onError: (error: AxiosError<{ error?: string }>) => {
toast.error( toast.error(
error.message || t("err.common.unknown", "오류가 발생했습니다."), error.response?.data?.error ||
error.message ||
t("err.common.unknown", "오류가 발생했습니다."),
); );
}, },
}); });
const removeRoleMutation = useMutation({ const removeRoleMutation = useMutation({
mutationFn: (role: { targetTenantId: string; relation: string }) => mutationFn: (role: { targetTenantId: string; relation: string }) =>
removeGroupRole(tenantId!, id!, role.targetTenantId, role.relation), removeGroupRole(tenantId ?? "", id ?? "", role.targetTenantId, role.relation),
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["user-group-roles", id] }); queryClient.invalidateQueries({ queryKey: ["user-group-roles", id] });
toast.success( toast.success(
@@ -172,8 +176,8 @@ export function UserGroupDetailPage() {
<div className="p-4 bg-destructive/10 text-destructive rounded-md text-left text-sm font-mono overflow-auto max-w-xl mx-auto border border-destructive/20"> <div className="p-4 bg-destructive/10 text-destructive rounded-md text-left text-sm font-mono overflow-auto max-w-xl mx-auto border border-destructive/20">
<p> <p>
Error:{" "} Error:{" "}
{(error as any)?.response?.data?.error || {(error as AxiosError<{ error?: string }>)?.response?.data?.error ||
(error as any)?.message || error.message ||
"Not found"} "Not found"}
</p> </p>
</div> </div>