1
0
forked from baron/baron-sso

adminfront: 권한부여 세부 탭에 네이버웍스 연동 권한(worksmobile_viewers/managers) 지원을 추가하고, 세부 권한 부여 자격을 Super Admin 전용으로 승격 (#1183)

This commit is contained in:
2026-06-16 17:53:24 +09:00
parent 26c4666a89
commit c990bd591b
2 changed files with 69 additions and 6 deletions

View File

@@ -61,7 +61,7 @@ const users = [
id: "user-owner", id: "user-owner",
name: "Owner User", name: "Owner User",
email: "owner@example.com", email: "owner@example.com",
role: "tenant_admin", role: "super_admin",
status: "active", status: "active",
}, },
{ {

View File

@@ -31,13 +31,13 @@ import {
import { toast } from "../../../components/ui/use-toast"; import { toast } from "../../../components/ui/use-toast";
import { import {
addTenantRelation, addTenantRelation,
fetchMe,
fetchTenantRelations, fetchTenantRelations,
fetchUsers, fetchUsers,
removeTenantRelation, removeTenantRelation,
type TenantRelation, type TenantRelation,
} from "../../../lib/adminApi"; } from "../../../lib/adminApi";
import { t } from "../../../lib/i18n"; import { t } from "../../../lib/i18n";
import { useTenantPermission } from "../hooks/useTenantPermission";
interface TenantFineGrainedPermissionsTabProps { interface TenantFineGrainedPermissionsTabProps {
tenantIdProp?: string; tenantIdProp?: string;
@@ -48,8 +48,11 @@ export function TenantFineGrainedPermissionsTab({
}: TenantFineGrainedPermissionsTabProps = {}) { }: TenantFineGrainedPermissionsTabProps = {}) {
const { tenantId: tenantIdParam } = useParams<{ tenantId: string }>(); const { tenantId: tenantIdParam } = useParams<{ tenantId: string }>();
const tenantId = tenantIdProp || tenantIdParam || ""; const tenantId = tenantIdProp || tenantIdParam || "";
const { hasPermission } = useTenantPermission(tenantId); const { data: profile } = useQuery({
const isWritable = hasPermission("manage_admins"); queryKey: ["me"],
queryFn: fetchMe,
});
const isWritable = profile?.role === "super_admin";
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [isDialogOpen, setIsDialogOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false);
@@ -75,7 +78,13 @@ export function TenantFineGrainedPermissionsTab({
> = {}; > = {};
for (const user of relationsQuery.data) { for (const user of relationsQuery.data) {
initialMap[user.userId] = {}; initialMap[user.userId] = {};
const tabs = ["profile", "permissions", "organization", "schema"]; const tabs = [
"profile",
"permissions",
"organization",
"schema",
"worksmobile",
];
for (const tab of tabs) { for (const tab of tabs) {
const isWrite = user.relations.includes(`${tab}_managers`); const isWrite = user.relations.includes(`${tab}_managers`);
const isRead = user.relations.includes(`${tab}_viewers`); const isRead = user.relations.includes(`${tab}_viewers`);
@@ -337,6 +346,12 @@ export function TenantFineGrainedPermissionsTab({
<TableHead className="font-bold"> <TableHead className="font-bold">
{t("ui.admin.tenants.detail.tab_schema", "사용자 스키마")} {t("ui.admin.tenants.detail.tab_schema", "사용자 스키마")}
</TableHead> </TableHead>
<TableHead className="font-bold">
{t(
"ui.admin.tenants.detail.tab_worksmobile",
"네이버웍스 연동",
)}
</TableHead>
<TableHead className="font-bold text-center w-20"> <TableHead className="font-bold text-center w-20">
{t("ui.common.action", "작업")} {t("ui.common.action", "작업")}
</TableHead> </TableHead>
@@ -346,7 +361,7 @@ export function TenantFineGrainedPermissionsTab({
{relations.length === 0 ? ( {relations.length === 0 ? (
<TableRow> <TableRow>
<TableCell <TableCell
colSpan={6} colSpan={7}
className="text-center py-12 text-muted-foreground" className="text-center py-12 text-muted-foreground"
> >
{t( {t(
@@ -387,6 +402,14 @@ export function TenantFineGrainedPermissionsTab({
? "read" ? "read"
: "none"; : "none";
const worksmobileVal = user.relations.includes(
"worksmobile_managers",
)
? "write"
: user.relations.includes("worksmobile_viewers")
? "read"
: "none";
const curProfileVal = const curProfileVal =
localTenantPermissions[user.userId]?.profile ?? localTenantPermissions[user.userId]?.profile ??
profileVal; profileVal;
@@ -398,6 +421,9 @@ export function TenantFineGrainedPermissionsTab({
organizationVal; organizationVal;
const curSchemaVal = const curSchemaVal =
localTenantPermissions[user.userId]?.schema ?? schemaVal; localTenantPermissions[user.userId]?.schema ?? schemaVal;
const curWorksmobileVal =
localTenantPermissions[user.userId]?.worksmobile ??
worksmobileVal;
return ( return (
<TableRow <TableRow
@@ -562,6 +588,43 @@ export function TenantFineGrainedPermissionsTab({
</option> </option>
</select> </select>
</TableCell> </TableCell>
<TableCell>
<select
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
value={curWorksmobileVal}
disabled={!isWritable}
name={`tenant-fine-grained-worksmobile-${user.userId}`}
onChange={(e) => {
const nextVal = e.target.value as
| "none"
| "read"
| "write";
setLocalTenantPermissions((prev) => ({
...prev,
[user.userId]: {
...(prev[user.userId] ?? {}),
worksmobile: nextVal,
},
}));
handleRelationChange(
user.userId,
"worksmobile",
worksmobileVal,
nextVal,
);
}}
>
<option value="none">
{t("ui.common.none", "권한 없음")}
</option>
<option value="read">
{t("ui.common.read", "조회 가능 (Read)")}
</option>
<option value="write">
{t("ui.common.write", "수정 가능 (Write)")}
</option>
</select>
</TableCell>
<TableCell className="text-center"> <TableCell className="text-center">
<Button <Button
variant="ghost" variant="ghost"