1
0
forked from baron/baron-sso

조직도 기능 추가

This commit is contained in:
2026-04-10 11:38:47 +09:00
parent 6971b69b79
commit 5211842d47
28 changed files with 1845 additions and 447 deletions

View File

@@ -72,7 +72,7 @@ function UserListPage() {
Record<string, boolean>
>({});
const [selectedUserIds, setSelectedUserIds] = React.useState<string[]>([]);
const limit = 50;
const limit = 1000;
const offset = (page - 1) * limit;
const { data: profile } = useQuery({
@@ -190,13 +190,14 @@ function UserListPage() {
const bulkDeleteMutation = useMutation({
mutationFn: bulkDeleteUsers,
onSuccess: () => {
onSuccess: (_, variables) => {
query.refetch();
setSelectedUserIds([]);
toast.success(
t(
"msg.admin.users.bulk.delete_success",
"선택한 사용자들이 삭제되었습니다.",
"{{count}}명의 사용자 삭제되었습니다.",
{ count: variables.length },
),
);
},
@@ -493,9 +494,18 @@ function UserListPage() {
<TableCell>
<input
type="checkbox"
className="w-4 h-4 rounded border-gray-300 text-primary focus:ring-primary cursor-pointer"
className="w-4 h-4 rounded border-gray-300 text-primary focus:ring-primary cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed"
checked={selectedUserIds.includes(user.id)}
onChange={() => toggleSelectUser(user.id)}
disabled={user.id === profile?.id}
title={
user.id === profile?.id
? t(
"msg.admin.users.self_delete_blocked",
"본인 계정은 삭제할 수 없습니다.",
)
: undefined
}
/>
</TableCell>
<TableCell>
@@ -559,9 +569,20 @@ function UserListPage() {
<Button
variant="ghost"
size="icon"
className="text-destructive hover:text-destructive"
className="text-destructive hover:text-destructive disabled:opacity-30 disabled:cursor-not-allowed"
onClick={() => handleDelete(user.id, user.name)}
disabled={deleteMutation.isPending}
disabled={
deleteMutation.isPending ||
user.id === profile?.id
}
title={
user.id === profile?.id
? t(
"msg.admin.users.self_delete_blocked",
"본인 계정은 삭제할 수 없습니다.",
)
: undefined
}
>
<Trash2 size={16} />
</Button>

View File

@@ -74,15 +74,14 @@ export function UserBulkUploadModal({ onSuccess }: UserBulkUploadModalProps) {
};
const downloadTemplate = () => {
const headers = "email,name,phone,role,tenant,department,employee_id";
const headers = "email,name,phone,role,tenant,department,position,jobTitle,employee_id";
const example =
"user1@example.com,홍길동,010-1234-5678,user,tenant-slug,개발팀,EMP001";
"user1@example.com,홍길동,010-1234-5678,user,tenant-slug,개발팀,수석,프론트엔드,EMP001";
const blob = new Blob(
[
`${headers}
${example}`,
`${headers}\n${example}`,
],
{ type: "text/csv" },
{ type: "text/csv;charset=utf-8;" },
);
const url = URL.createObjectURL(blob);
const a = document.createElement("a");

View File

@@ -34,6 +34,10 @@ export function parseUserCSV(text: string): BulkUserItem[] {
item.tenantSlug = value;
} else if (header === "department") {
item.department = value;
} else if (header === "position") {
item.position = value;
} else if (header === "jobtitle") {
item.jobTitle = value;
} else {
item.metadata[header] = value;
}