import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Key, Plus, Save, Trash2, Users } from "lucide-react"; import * as React from "react"; import { Link } from "react-router-dom"; import { PageHeader } from "../../../../common/core/components/page"; import { Button } from "../../components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "../../components/ui/card"; import { Input } from "../../components/ui/input"; import { toast } from "../../components/ui/use-toast"; import { fetchGlobalCustomClaimDefinitions, type GlobalCustomClaimDefinition, type GlobalCustomClaimPermission, updateGlobalCustomClaimDefinitions, } from "../../lib/adminApi"; import { t } from "../../lib/i18n"; type ClaimDraft = GlobalCustomClaimDefinition & { id: string }; const valueTypes: GlobalCustomClaimDefinition["valueType"][] = [ "text", "number", "boolean", "array", "object", "date", "datetime", ]; const permissions: GlobalCustomClaimPermission[] = [ "admin_only", "user_and_admin", ]; function toDrafts(items: GlobalCustomClaimDefinition[]): ClaimDraft[] { return items.map((item, index) => ({ id: `${item.key || "claim"}-${index}`, key: item.key, label: item.label, valueType: item.valueType || "text", readPermission: item.readPermission || "admin_only", writePermission: item.writePermission || "admin_only", description: item.description || "", })); } function toDefinitions(drafts: ClaimDraft[]): GlobalCustomClaimDefinition[] { return drafts .map((draft) => normalizeClaimDraftPermissions(draft)) .map((draft) => ({ key: draft.key.trim(), label: draft.label.trim(), valueType: draft.valueType, readPermission: draft.readPermission, writePermission: draft.writePermission, description: draft.description?.trim(), })) .filter((draft) => draft.key.length > 0); } function normalizeClaimDraftPermissions(draft: ClaimDraft): ClaimDraft { if (draft.writePermission !== "user_and_admin") { return draft; } return { ...draft, readPermission: "user_and_admin", }; } function permissionLabel(permission: GlobalCustomClaimPermission) { return permission === "user_and_admin" ? t( "ui.common.custom_claim_permission.user_and_admin", "사용자 및 관리자 가능", ) : t("ui.common.custom_claim_permission.admin_only", "관리자만 가능"); } export default function GlobalCustomClaimsPage() { const queryClient = useQueryClient(); const [drafts, setDrafts] = React.useState([]); const query = useQuery({ queryKey: ["global-custom-claim-definitions"], queryFn: fetchGlobalCustomClaimDefinitions, }); React.useEffect(() => { if (query.data) { setDrafts(toDrafts(query.data.items)); } }, [query.data]); const mutation = useMutation({ mutationFn: updateGlobalCustomClaimDefinitions, onSuccess: (data) => { queryClient.setQueryData(["global-custom-claim-definitions"], data); toast.success(t("msg.info.saved_success", "저장되었습니다.")); }, onError: () => { toast.error(t("err.common.unknown", "오류가 발생했습니다.")); }, }); const addClaim = () => { setDrafts((current) => [ ...current, { id: `global-claim-${Date.now()}`, key: "", label: "", valueType: "text", readPermission: "admin_only", writePermission: "admin_only", description: "", }, ]); }; const updateClaim = (id: string, patch: Partial) => { setDrafts((current) => current.map((draft) => draft.id === id ? normalizeClaimDraftPermissions({ ...draft, ...patch }) : draft, ), ); }; const removeClaim = (id: string) => { setDrafts((current) => current.filter((draft) => draft.id !== id)); }; const saveClaims = () => { mutation.mutate({ items: toDefinitions(drafts) }); }; return (
} title={t( "ui.admin.users.global_custom_claims.title", "전역 Claim 설정", )} description={t( "msg.admin.users.global_custom_claims.description", "모든 RP에 공통 적용할 사용자 claim 정의와 사용자의 읽기/쓰기 권한 기본값을 관리합니다. 쓰기 허용 시 읽기도 자동으로 허용됩니다.", )} actions={ <> } /> {t( "ui.admin.users.global_custom_claims.registry", "Global Claim Registry", )} {t( "msg.admin.users.global_custom_claims.registry", "정의된 claim key만 사용자 상세의 전역 claim 값 관리 대상이 됩니다. 읽기/쓰기는 관리자 권한이 아니라 사용자가 본인 claim 값을 조회하거나 수정할 수 있는지에 대한 설정입니다.", )} {query.isLoading ? (
{t("ui.common.loading", "로딩 중...")}
) : drafts.length === 0 ? (
{t( "msg.admin.users.global_custom_claims.empty", "정의된 전역 claim이 없습니다.", )}
) : ( drafts.map((claim) => (
updateClaim(claim.id, { key: event.target.value }) } /> updateClaim(claim.id, { label: event.target.value }) } /> updateClaim(claim.id, { description: event.target.value }) } />
)) )}
); }