Skip to content

Commit

Permalink
✨ Support delete own account and other tweaks (#614)
Browse files Browse the repository at this point in the history
Co-authored-by: Esteban Maya Cadavid <emaya@trueblue.com>
  • Loading branch information
alejsdev and Esteban Maya Cadavid committed Feb 27, 2024
1 parent e44777f commit 2346b81
Show file tree
Hide file tree
Showing 7 changed files with 30 additions and 24 deletions.
19 changes: 11 additions & 8 deletions src/backend/app/api/api_v1/endpoints/users.py
@@ -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 (
Expand All @@ -21,6 +21,7 @@
UsersOut,
UserUpdate,
UserUpdateMe,
Item
)
from app.utils import send_new_account_email

Expand Down Expand Up @@ -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")
3 changes: 0 additions & 3 deletions src/new-frontend/src/components/Common/Sidebar.tsx
Expand Up @@ -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';
Expand All @@ -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');
};


Expand Down
4 changes: 1 addition & 3 deletions src/new-frontend/src/components/Common/UserMenu.tsx
Expand Up @@ -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 (
Expand Down
Expand Up @@ -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;
Expand All @@ -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');
}
}

Expand All @@ -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}>
Expand Down
4 changes: 4 additions & 0 deletions src/new-frontend/src/hooks/useAuth.tsx
Expand Up @@ -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({
Expand All @@ -21,6 +24,7 @@ const useAuth = () => {
resetUser();
resetUsers();
resetItems();
navigate('/login');
};

const isLoggedIn = () => {
Expand Down
2 changes: 1 addition & 1 deletion src/new-frontend/src/store/items-store.tsx
Expand Up @@ -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 });
Expand Down
2 changes: 1 addition & 1 deletion src/new-frontend/src/store/users-store.tsx
Expand Up @@ -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 });
Expand Down

0 comments on commit 2346b81

Please sign in to comment.