✨ Support delete own account and other tweaks (#614)
Co-authored-by: Esteban Maya Cadavid <emaya@trueblue.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlmodel import func, select
|
||||
from sqlmodel import func, select, delete
|
||||
|
||||
from app import crud
|
||||
from app.api.deps import (
|
||||
@@ -21,6 +21,7 @@ from app.models import (
|
||||
UsersOut,
|
||||
UserUpdate,
|
||||
UserUpdateMe,
|
||||
Item
|
||||
)
|
||||
from app.utils import send_new_account_email
|
||||
|
||||
@@ -194,12 +195,14 @@ def delete_user(
|
||||
user = session.get(User, user_id)
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
if not current_user.is_superuser:
|
||||
raise HTTPException(status_code=400, detail="Not enough permissions")
|
||||
if user == current_user:
|
||||
|
||||
if (user == current_user and not current_user.is_superuser) or (user != current_user and current_user.is_superuser):
|
||||
statement = delete(Item).where(Item.owner_id == user_id)
|
||||
session.exec(statement)
|
||||
session.delete(user)
|
||||
session.commit()
|
||||
return Message(message="User deleted successfully")
|
||||
elif user == current_user and current_user.is_superuser:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="Users are not allowed to delete themselves"
|
||||
status_code=400, detail="Super users are not allowed to delete themselves"
|
||||
)
|
||||
session.delete(user)
|
||||
session.commit()
|
||||
return Message(message="User deleted successfully")
|
||||
|
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
|
||||
import { Box, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerOverlay, Flex, IconButton, Image, Text, useColorModeValue, useDisclosure } from '@chakra-ui/react';
|
||||
import { FiLogOut, FiMenu } from 'react-icons/fi';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import Logo from '../../assets/images/fastapi-logo.svg';
|
||||
import useAuth from '../../hooks/useAuth';
|
||||
@@ -16,11 +15,9 @@ const Sidebar: React.FC = () => {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { user } = useUserStore();
|
||||
const { logout } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleLogout = async () => {
|
||||
logout()
|
||||
navigate('/login');
|
||||
};
|
||||
|
||||
|
||||
|
@@ -3,17 +3,15 @@ import React from 'react';
|
||||
import { Box, IconButton, Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/react';
|
||||
import { FaUserAstronaut } from 'react-icons/fa';
|
||||
import { FiLogOut, FiUser } from 'react-icons/fi';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import useAuth from '../../hooks/useAuth';
|
||||
|
||||
const UserMenu: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { logout } = useAuth();
|
||||
|
||||
const handleLogout = async () => {
|
||||
logout()
|
||||
navigate('/login');
|
||||
};
|
||||
|
||||
return (
|
||||
|
@@ -2,7 +2,10 @@ import React, { useState } from 'react';
|
||||
|
||||
import { AlertDialog, AlertDialogBody, AlertDialogContent, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, Button } from '@chakra-ui/react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { ApiError } from '../../client';
|
||||
import useAuth from '../../hooks/useAuth';
|
||||
import useCustomToast from '../../hooks/useCustomToast';
|
||||
import { useUserStore } from '../../store/user-store';
|
||||
|
||||
interface DeleteProps {
|
||||
isOpen: boolean;
|
||||
@@ -12,18 +15,19 @@ interface DeleteProps {
|
||||
const DeleteConfirmation: React.FC<DeleteProps> = ({ isOpen, onClose }) => {
|
||||
const showToast = useCustomToast();
|
||||
const cancelRef = React.useRef<HTMLButtonElement | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { handleSubmit } = useForm();
|
||||
const { handleSubmit, formState: { isSubmitting } } = useForm();
|
||||
const { user, deleteUser } = useUserStore();
|
||||
const { logout } = useAuth();
|
||||
|
||||
const onSubmit = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// TODO: Delete user account when API is ready
|
||||
await deleteUser(user!.id);
|
||||
logout();
|
||||
onClose();
|
||||
showToast('Success', 'Your account has been successfully deleted.', 'success');
|
||||
} catch (err) {
|
||||
showToast('An error occurred', 'An error occurred while deleting your account.', 'error');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
const errDetail = (err as ApiError).body.detail;
|
||||
showToast('Something went wrong.', `${errDetail}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +51,7 @@ const DeleteConfirmation: React.FC<DeleteProps> = ({ isOpen, onClose }) => {
|
||||
</AlertDialogBody>
|
||||
|
||||
<AlertDialogFooter gap={3}>
|
||||
<Button bg='ui.danger' color='white' _hover={{ opacity: 0.8 }} type='submit' isLoading={isLoading}>
|
||||
<Button bg='ui.danger' color='white' _hover={{ opacity: 0.8 }} type='submit' isLoading={isSubmitting}>
|
||||
Confirm
|
||||
</Button>
|
||||
<Button ref={cancelRef} onClick={onClose} isDisabled={isLoading}>
|
||||
|
@@ -2,11 +2,14 @@ import { useUserStore } from '../store/user-store';
|
||||
import { Body_login_login_access_token as AccessToken, LoginService } from '../client';
|
||||
import { useUsersStore } from '../store/users-store';
|
||||
import { useItemsStore } from '../store/items-store';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const useAuth = () => {
|
||||
const { user, getUser, resetUser } = useUserStore();
|
||||
const { resetUsers } = useUsersStore();
|
||||
const { resetItems } = useItemsStore();
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
||||
const login = async (data: AccessToken) => {
|
||||
const response = await LoginService.loginAccessToken({
|
||||
@@ -21,6 +24,7 @@ const useAuth = () => {
|
||||
resetUser();
|
||||
resetUsers();
|
||||
resetItems();
|
||||
navigate('/login');
|
||||
};
|
||||
|
||||
const isLoggedIn = () => {
|
||||
|
@@ -14,7 +14,7 @@ export const useItemsStore = create<ItemsStore>((set) => ({
|
||||
items: [],
|
||||
getItems: async () => {
|
||||
const itemsResponse = await ItemsService.readItems({ skip: 0, limit: 10 });
|
||||
set({ items: itemsResponse });
|
||||
set({ items: itemsResponse.data });
|
||||
},
|
||||
addItem: async (item: ItemCreate) => {
|
||||
const itemResponse = await ItemsService.createItem({ requestBody: item });
|
||||
|
@@ -14,7 +14,7 @@ export const useUsersStore = create<UsersStore>((set) => ({
|
||||
users: [],
|
||||
getUsers: async () => {
|
||||
const usersResponse = await UsersService.readUsers({ skip: 0, limit: 10 });
|
||||
set({ users: usersResponse });
|
||||
set({ users: usersResponse.data });
|
||||
},
|
||||
addUser: async (user: UserCreate) => {
|
||||
const userResponse = await UsersService.createUser({ requestBody: user });
|
||||
|
Reference in New Issue
Block a user