1
0
forked from baron/baron-sso

adminfront 및 백엔드: ReBAC 기반 각 탭별 읽기/쓰기 권한 제어 구현

This commit is contained in:
2026-06-10 10:01:30 +09:00
parent c880b3c333
commit 85707500ef
13 changed files with 485 additions and 40 deletions

View File

@@ -21,6 +21,7 @@ import {
import type React from "react";
import { useState } from "react";
import { useParams } from "react-router-dom";
import { useTenantPermission } from "../hooks/useTenantPermission";
import { commonStickyTableHeaderClass } from "../../../../../common/ui/table";
import { Badge } from "../../../components/ui/badge";
import { Button } from "../../../components/ui/button";
@@ -126,6 +127,7 @@ interface UserGroupTreeNodeProps {
AxiosError<{ error?: string }>,
{ groupId: string; userId: string }
>;
isWritable?: boolean;
}
const UserGroupTreeNode: React.FC<UserGroupTreeNodeProps> = ({
@@ -137,6 +139,7 @@ const UserGroupTreeNode: React.FC<UserGroupTreeNodeProps> = ({
onAddSubGroup,
addMemberMutation,
removeMemberMutation,
isWritable = true,
}) => {
const [isExpanded, setIsExpanded] = useState(true);
const hasChildren = node.children.length > 0;
@@ -200,6 +203,7 @@ const UserGroupTreeNode: React.FC<UserGroupTreeNodeProps> = ({
e.stopPropagation();
onAddSubGroup(node.id);
}}
disabled={!isWritable}
>
<Plus size={14} />
</Button>
@@ -210,6 +214,7 @@ const UserGroupTreeNode: React.FC<UserGroupTreeNodeProps> = ({
e.stopPropagation();
onDelete(node.id);
}}
disabled={!isWritable}
>
<Trash2 size={14} className="text-destructive" />
</Button>
@@ -229,6 +234,7 @@ const UserGroupTreeNode: React.FC<UserGroupTreeNodeProps> = ({
onAddSubGroup={onAddSubGroup}
addMemberMutation={addMemberMutation}
removeMemberMutation={removeMemberMutation}
isWritable={isWritable}
/>
))}
</>
@@ -240,6 +246,9 @@ function TenantGroupsPage() {
const tenantId = params.tenantId ?? "";
const _queryClient = useQueryClient();
const { hasPermission } = useTenantPermission(tenantId);
const isWritable = hasPermission("manage");
const [newGroupName, setNewGroupName] = useState("");
const [newGroupDesc, setNewGroupNameDesc] = useState("");
const [newGroupUnitType, setNewGroupUnitType] = useState("Team");
@@ -423,6 +432,7 @@ function TenantGroupsPage() {
id="name"
value={newGroupName}
onChange={(e) => setNewGroupName(e.target.value)}
disabled={!isWritable}
placeholder={t(
"ui.admin.groups.form.name_placeholder",
"예: 개발팀, 인사팀",
@@ -437,6 +447,7 @@ function TenantGroupsPage() {
id="unitType"
value={newGroupUnitType}
onChange={(e) => setNewGroupUnitType(e.target.value)}
disabled={!isWritable}
placeholder={t(
"ui.admin.groups.form.unit_level_placeholder",
"예: 본부, 팀, 셀",
@@ -449,9 +460,10 @@ function TenantGroupsPage() {
</Label>
<select
id="parentId"
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
value={newGroupParentId || ""}
onChange={(e) => setNewGroupParentId(e.target.value || null)}
disabled={!isWritable}
>
<option value="">{t("ui.common.none", "없음")}</option>
{groupsQuery.data?.map((group) => (
@@ -469,6 +481,7 @@ function TenantGroupsPage() {
id="desc"
value={newGroupDesc}
onChange={(e) => setNewGroupNameDesc(e.target.value)}
disabled={!isWritable}
placeholder={t(
"ui.admin.groups.form.desc_placeholder",
"그룹 용도 설명",
@@ -478,7 +491,7 @@ function TenantGroupsPage() {
<Button
className="w-full"
onClick={() => createMutation.mutate()}
disabled={!newGroupName || createMutation.isPending}
disabled={!newGroupName || createMutation.isPending || !isWritable}
>
{t("ui.admin.groups.form.submit", "생성하기")}
</Button>
@@ -569,6 +582,7 @@ function TenantGroupsPage() {
onAddSubGroup={handleAddSubGroup}
addMemberMutation={addMemberMutation}
removeMemberMutation={removeMemberMutation}
isWritable={isWritable}
/>
))}
</TableBody>