forked from baron/baron-sso
feat: 테넌트 그룹 관리 UI 및 상세 멤버십 관리 기능 구현 #239
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { AxiosError } from "axios";
|
||||
import { useState } from "react";
|
||||
import { useOutletContext } from "react-router-dom";
|
||||
import { Button } from "../../../components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "../../../components/ui/card";
|
||||
import { Input } from "../../../components/ui/input";
|
||||
import { Label } from "../../../components/ui/label";
|
||||
import { Textarea } from "../../../components/ui/textarea";
|
||||
import { updateTenantGroup, type TenantGroupSummary } from "../../../lib/adminApi";
|
||||
|
||||
function TenantGroupProfileTab() {
|
||||
const { group, refetch } = useOutletContext<{
|
||||
group: TenantGroupSummary;
|
||||
refetch: () => void
|
||||
}>();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [name, setName] = useState(group?.name ?? "");
|
||||
const [description, setDescription] = useState(group?.description ?? "");
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: () => updateTenantGroup(group.id, { name, description }),
|
||||
onSuccess: () => {
|
||||
refetch();
|
||||
queryClient.invalidateQueries({ queryKey: ["tenant-groups"] });
|
||||
},
|
||||
});
|
||||
|
||||
const errorMsg = (mutation.error as AxiosError<{ error?: string }>)?.response
|
||||
?.data?.error;
|
||||
|
||||
if (!group) return null;
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl space-y-6">
|
||||
<Card className="bg-[var(--color-panel)]">
|
||||
<CardHeader>
|
||||
<CardTitle>그룹 정보 수정</CardTitle>
|
||||
<CardDescription>
|
||||
그룹의 기본 이름과 설명을 변경할 수 있습니다. 식별자(Slug)는 변경할 수 없습니다.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label>그룹 ID (고유 식별자)</Label>
|
||||
<Input value={group.id} disabled className="bg-muted" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Slug</Label>
|
||||
<Input value={group.slug} disabled className="bg-muted" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="groupName">Group Name</Label>
|
||||
<Input
|
||||
id="groupName"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="groupDesc">Description</Label>
|
||||
<Textarea
|
||||
id="groupDesc"
|
||||
rows={4}
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{errorMsg && (
|
||||
<div className="rounded-lg border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
||||
{errorMsg}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex justify-end pt-4">
|
||||
<Button
|
||||
onClick={() => mutation.mutate()}
|
||||
disabled={mutation.isPending || (name === group.name && description === group.description)}
|
||||
>
|
||||
{mutation.isPending ? "저장 중..." : "변경사항 저장"}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TenantGroupProfileTab;
|
||||
Reference in New Issue
Block a user