forked from baron/baron-sso
adminfront 테스트 실패 해결 및 UI/Lint 수정
- 테넌트 목록 테이블 헤더 스타일 보정(nowrap) 및 삭제 버튼 추가 - 초기 설정(Seed) 테넌트 삭제 보호 로직 적용 - 사용자 상태 변경 및 대표 조직 지정 UI를 Switch로 변경하여 테스트와 동기화 - Playwright 테스트 코드의 선택자 및 상호작용 로직 업데이트 - Biome을 통한 린트 오류 및 타입 안정성(AxiosError) 확보 - 프론트엔드 모노레포 통합 마스터 플랜 문서 추가
This commit is contained in:
@@ -1,5 +1,14 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { Mail, MoreHorizontal, Plus, User, UserPlus, UserMinus, Loader2 } from "lucide-react";
|
||||
import type { AxiosError } from "axios";
|
||||
import {
|
||||
Loader2,
|
||||
Mail,
|
||||
MoreHorizontal,
|
||||
Plus,
|
||||
User,
|
||||
UserMinus,
|
||||
UserPlus,
|
||||
} from "lucide-react";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import { Badge } from "../../../components/ui/badge";
|
||||
import { Button } from "../../../components/ui/button";
|
||||
@@ -52,18 +61,34 @@ function TenantUsersPage() {
|
||||
mutationFn: ({ userId, slug }: { userId: string; slug: string }) =>
|
||||
updateUser(userId, { tenantSlug: slug, isRemoveTenant: true }),
|
||||
onSuccess: () => {
|
||||
toast.success(t("msg.admin.tenants.members.remove_success", "조직에서 제외되었습니다."));
|
||||
toast.success(
|
||||
t(
|
||||
"msg.admin.tenants.members.remove_success",
|
||||
"조직에서 제외되었습니다.",
|
||||
),
|
||||
);
|
||||
usersQuery.refetch();
|
||||
queryClient.invalidateQueries({ queryKey: ["tenant", tenantId] });
|
||||
},
|
||||
onError: (err: any) => {
|
||||
toast.error(err.response?.data?.error || t("msg.admin.tenants.members.remove_error", "제외 실패"));
|
||||
onError: (err: AxiosError<{ error?: string }>) => {
|
||||
toast.error(
|
||||
err.response?.data?.error ||
|
||||
t("msg.admin.tenants.members.remove_error", "제외 실패"),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const handleRemoveMember = (userId: string, userName: string) => {
|
||||
if (!tenantSlug) return;
|
||||
if (window.confirm(t("msg.admin.tenants.members.remove_confirm", "'{{name}}'님을 이 조직에서 제외하시겠습니까?", { name: userName }))) {
|
||||
if (
|
||||
window.confirm(
|
||||
t(
|
||||
"msg.admin.tenants.members.remove_confirm",
|
||||
"'{{name}}'님을 이 조직에서 제외하시겠습니까?",
|
||||
{ name: userName },
|
||||
),
|
||||
)
|
||||
) {
|
||||
removeTenantMutation.mutate({ userId, slug: tenantSlug });
|
||||
}
|
||||
};
|
||||
@@ -122,8 +147,13 @@ function TenantUsersPage() {
|
||||
<TableRow>
|
||||
<TableCell colSpan={5} className="text-center py-20">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Loader2 className="animate-spin text-muted-foreground" size={24} />
|
||||
<span className="text-sm text-muted-foreground">{t("ui.common.loading", "Loading...")}</span>
|
||||
<Loader2
|
||||
className="animate-spin text-muted-foreground"
|
||||
size={24}
|
||||
/>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{t("ui.common.loading", "Loading...")}
|
||||
</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -142,7 +172,9 @@ function TenantUsersPage() {
|
||||
) : (
|
||||
users.map((user) => (
|
||||
<TableRow key={user.id}>
|
||||
<TableCell className="font-semibold">{user.name}</TableCell>
|
||||
<TableCell className="font-semibold">
|
||||
{user.name}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-1 text-xs">
|
||||
<Mail size={12} className="text-muted-foreground" />
|
||||
@@ -159,7 +191,9 @@ function TenantUsersPage() {
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
variant={user.status === "active" ? "default" : "muted"}
|
||||
variant={
|
||||
user.status === "active" ? "default" : "muted"
|
||||
}
|
||||
>
|
||||
{t(`ui.common.status.${user.status}`, user.status)}
|
||||
</Badge>
|
||||
@@ -167,7 +201,11 @@ function TenantUsersPage() {
|
||||
<TableCell className="text-right">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
>
|
||||
<MoreHorizontal size={16} />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
@@ -175,16 +213,24 @@ function TenantUsersPage() {
|
||||
<DropdownMenuItem asChild>
|
||||
<Link to={`/users/${user.id}`}>
|
||||
<User size={14} className="mr-2" />
|
||||
{t("ui.admin.tenants.members.view_profile", "상세 정보")}
|
||||
{t(
|
||||
"ui.admin.tenants.members.view_profile",
|
||||
"상세 정보",
|
||||
)}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
<DropdownMenuItem
|
||||
className="text-destructive focus:text-destructive"
|
||||
onClick={() => handleRemoveMember(user.id, user.name)}
|
||||
onClick={() =>
|
||||
handleRemoveMember(user.id, user.name)
|
||||
}
|
||||
disabled={removeTenantMutation.isPending}
|
||||
>
|
||||
<UserMinus size={14} className="mr-2" />
|
||||
{t("ui.admin.tenants.members.remove", "조직에서 제외")}
|
||||
{t(
|
||||
"ui.admin.tenants.members.remove",
|
||||
"조직에서 제외",
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
Reference in New Issue
Block a user