chore: add delete user button

This commit is contained in:
Steven 2023-08-22 23:02:14 +08:00
parent ec2ec74e31
commit e91050c803
5 changed files with 42 additions and 12 deletions

View File

@ -89,7 +89,7 @@ func (s *UserService) CreateUser(ctx context.Context, request *apiv2pb.CreateUse
func (s *UserService) DeleteUser(ctx context.Context, request *apiv2pb.DeleteUserRequest) (*apiv2pb.DeleteUserResponse, error) { func (s *UserService) DeleteUser(ctx context.Context, request *apiv2pb.DeleteUserRequest) (*apiv2pb.DeleteUserResponse, error) {
userID := ctx.Value(UserIDContextKey).(int32) userID := ctx.Value(UserIDContextKey).(int32)
if userID == request.Id { if userID == request.Id {
return nil, status.Errorf(codes.InvalidArgument, "cannot delete self") return nil, status.Errorf(codes.InvalidArgument, "cannot delete yourself")
} }
err := s.Store.DeleteUser(ctx, &store.DeleteUser{ err := s.Store.DeleteUser(ctx, &store.DeleteUser{

View File

@ -1,4 +1,4 @@
import { Button } from "@mui/joy"; import { Button, IconButton } from "@mui/joy";
import axios from "axios"; import axios from "axios";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
@ -110,7 +110,7 @@ const AccessTokenSection = () => {
{String(userAccessToken.expiresAt ?? "Never")} {String(userAccessToken.expiresAt ?? "Never")}
</td> </td>
<td className="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm"> <td className="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm">
<Button <IconButton
color="danger" color="danger"
variant="plain" variant="plain"
size="sm" size="sm"
@ -119,7 +119,7 @@ const AccessTokenSection = () => {
}} }}
> >
<Icon.Trash className="w-4 h-auto" /> <Icon.Trash className="w-4 h-auto" />
</Button> </IconButton>
</td> </td>
</tr> </tr>
))} ))}

View File

@ -1,7 +1,10 @@
import { Button } from "@mui/joy"; import { Button, IconButton } from "@mui/joy";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import toast from "react-hot-toast";
import useUserStore from "../../stores/v1/user"; import useUserStore from "../../stores/v1/user";
import { showCommonDialog } from "../Alert";
import CreateUserDialog from "../CreateUserDialog"; import CreateUserDialog from "../CreateUserDialog";
import Icon from "../Icon";
const MemberSection = () => { const MemberSection = () => {
const userStore = useUserStore(); const userStore = useUserStore();
@ -18,6 +21,22 @@ const MemberSection = () => {
setCurrentEditingUser(undefined); setCurrentEditingUser(undefined);
}; };
const handleDeleteUser = async (user: User) => {
showCommonDialog({
title: "Delete User",
content: `Are you sure to delete user \`${user.nickname}\`? You cannot undo this action.`,
style: "danger",
onConfirm: async () => {
try {
await userStore.deleteUser(user.id);
toast.success(`User \`${user.nickname}\` deleted successfully`);
} catch (error: any) {
toast.error(`Failed to delete user \`${user.nickname}\`: ${error.response.data.message}`);
}
},
});
};
return ( return (
<> <>
<div className="w-full flex flex-col justify-start items-start space-y-4"> <div className="w-full flex flex-col justify-start items-start space-y-4">
@ -68,16 +87,20 @@ const MemberSection = () => {
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm text-gray-900">{user.nickname}</td> <td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm text-gray-900">{user.nickname}</td>
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{user.email}</td> <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{user.email}</td>
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{user.role}</td> <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{user.role}</td>
<td className="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium"> <td className="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm">
<button <IconButton
className="text-indigo-600 hover:text-indigo-900" size="sm"
variant="plain"
onClick={() => { onClick={() => {
setCurrentEditingUser(user); setCurrentEditingUser(user);
setShowCreateUserDialog(true); setShowCreateUserDialog(true);
}} }}
> >
Edit <Icon.PenBox className="w-4 h-auto" />
</button> </IconButton>
<IconButton size="sm" color="danger" variant="plain" onClick={() => handleDeleteUser(user)}>
<Icon.Trash className="w-4 h-auto" />
</IconButton>
</td> </td>
</tr> </tr>
))} ))}

View File

@ -43,8 +43,8 @@ export function patchUser(userPatch: UserPatch) {
return axios.patch<User>(`/api/v1/user/${userPatch.id}`, userPatch); return axios.patch<User>(`/api/v1/user/${userPatch.id}`, userPatch);
} }
export function deleteUser(userDelete: UserDelete) { export function deleteUser(userId: UserId) {
return axios.delete(`/api/v1/user/${userDelete.id}`); return axios.delete(`/api/v2/users/${userId}`);
} }
export function getShortcutList(shortcutFind?: ShortcutFind) { export function getShortcutList(shortcutFind?: ShortcutFind) {

View File

@ -19,6 +19,7 @@ interface UserState {
getCurrentUser: () => User; getCurrentUser: () => User;
createUser: (userCreate: UserCreate) => Promise<User>; createUser: (userCreate: UserCreate) => Promise<User>;
patchUser: (userPatch: UserPatch) => Promise<void>; patchUser: (userPatch: UserPatch) => Promise<void>;
deleteUser: (id: UserId) => Promise<void>;
} }
const useUserStore = create<UserState>()((set, get) => ({ const useUserStore = create<UserState>()((set, get) => ({
@ -67,6 +68,12 @@ const useUserStore = create<UserState>()((set, get) => ({
userMap[user.id] = user; userMap[user.id] = user;
set(userMap); set(userMap);
}, },
deleteUser: async (userId: UserId) => {
await api.deleteUser(userId);
const userMap = get().userMapById;
delete userMap[userId];
set(userMap);
},
getUserById: (id: UserId) => { getUserById: (id: UserId) => {
const userMap = get().userMapById; const userMap = get().userMapById;
return userMap[id] as User; return userMap[id] as User;