Skip to content

Commit

Permalink
feat: CTHUB 248 -Delete user (#253)
Browse files Browse the repository at this point in the history
* feat: turns x icon into buttton, adds empty handler function

* feat: adds functionality for deleting users, reuses and slightly adjusts alert dialogue for confirming, adds checks on frontend and backend for ensuring user does not delete their own self from user table

* fix: adds missed backend check

* chore: makes minor changes to handle delelete user and delete viewset

* refactor AlertDialog  plus a few small changes

---------

Co-authored-by: tim738745 <[email protected]>
  • Loading branch information
emi-hi and tim738745 authored Mar 18, 2024
1 parent 22ea4f4 commit 442a169
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 88 deletions.
17 changes: 12 additions & 5 deletions django/api/viewsets/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import CreateModelMixin
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin
from api.models.user import User
from api.serializers.user import UserSerializer, UserListSerializer
from api.decorators.permission import check_admin_permission
from api.services.user import update_permissions
from api.services.permissions import get_permissions_map

class UserViewSet(GenericViewSet, CreateModelMixin):
class UserViewSet(GenericViewSet, CreateModelMixin, DestroyModelMixin):
"""
This viewset automatically provides `list`, `create`, `retrieve`,
and `update` actions.
"""
permission_classes = (AllowAny,)
http_method_names = ['get', 'post', 'put', 'patch']
http_method_names = ['get', 'post', 'put', 'patch', 'delete']
queryset = User.objects.all()
lookup_field = 'idir'

serializer_classes = {
'default': UserSerializer,
Expand All @@ -28,10 +29,16 @@ class UserViewSet(GenericViewSet, CreateModelMixin):
def get_serializer_class(self):
if self.action in list(self.serializer_classes.keys()):
return self.serializer_classes[self.action]

return self.serializer_classes['default']


@method_decorator(check_admin_permission())
def destroy(self, request, idir=None):
if request.user == idir:
return Response('you cannot delete your own idir', status=status.HTTP_400_BAD_REQUEST )
return super().destroy(self, request)


@method_decorator(check_admin_permission())
def create(self, request):
return super().create(request)
Expand All @@ -44,7 +51,7 @@ def update_permissions(self, request):
update_permissions(user_permissions)
except Exception as e:
return Response(str(e), status=status.HTTP_400_BAD_REQUEST)
return Response('User permissions were updated!', status=status.HTTP_201_CREATED)
return Response('User permissions were updated!', status=status.HTTP_200_OK)

@action(detail=False)
def current(self, request):
Expand Down
47 changes: 28 additions & 19 deletions react/src/app/components/AlertDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,27 @@ import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';

export default function AlertDialog(props) {
const AlertDialog = (props) => {
const {
open, setOpen, rightButtonText, dialogue, leftButtonText, setReplaceData, title
open,
dialogue,
title,
cancelText,
handleCancel,
confirmText,
handleConfirm
} = props;
const handleClose = (trueFalse) => {
setReplaceData(trueFalse);
setOpen(false);
};

if (!open) {
return null
}
return (
<div>
<Dialog
open={open}
onClose={handleClose}
open={true}
onClose={() => {
handleCancel()
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
Expand All @@ -34,36 +41,38 @@ export default function AlertDialog(props) {
</DialogContent>
<DialogActions>
<Button onClick={() => {
handleClose(false);
handleCancel();
}}
>
{leftButtonText}
{cancelText}

</Button>
<Button
onClick={() => {
handleClose(true);
handleConfirm();
}}
autoFocus
>
{rightButtonText}
{confirmText}
</Button>
</DialogActions>
</Dialog>
</div>
);
}

AlertDialog.defaultProps = {
rightButtonText: '',
dialogue: '',
leftButtonText: '',
setReplaceData: '',
title: '',
};
AlertDialog.propTypes = {
open: PropTypes.bool.isRequired,
setOpen: PropTypes.func.isRequired,
rightButtonText: PropTypes.string,
title: PropTypes.string,
dialogue: PropTypes.string,
leftButtonText: PropTypes.string,
setReplaceData: PropTypes.func,
cancelText: PropTypes.string.isRequired,
handleCancel: PropTypes.func.isRequired,
confirmText: PropTypes.string.isRequired,
handleConfirm: PropTypes.func.isRequired
};

export default AlertDialog
61 changes: 33 additions & 28 deletions react/src/uploads/UploadContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,15 @@ const UploadContainer = () => {
const [datasetList, setDatasetList] = useState([{}]); // holds the array of names of datasets
const [loading, setLoading] = useState(false);
const [datasetSelected, setDatasetSelected] = useState(''); // string identifying which dataset is being uploaded
const [replaceData, setReplaceData] = useState('false'); // if true, we will replace all
const [replaceData, setReplaceData] = useState(false); // if true, we will replace all
const [alertContent, setAlertContent] = useState();
const [alert, setAlert] = useState(false);
const [currentUser, setCurrentUser] = useState('');
const [alertSeverity, setAlertSeverity] = useState('');
// existing data with what is being uploaded
const [open, setOpen] = useState(false);
const [openDialog, setOpenDialog] = useState(false);
const [adminUser, setAdminUser] = useState(false);
const dialogue = 'Selecting replace will delete all previously uploaded records for this dataset';
const leftButtonText = 'Cancel';
const rightButtonText = 'Replace existing data';
const handleRadioChange = (event) => {
const choice = event.target.value;
if (choice === 'true') {
setOpen(true);
}
setReplaceData(choice);
};
const axios = useAxios()
const axiosDefault = useAxios(true)
const axios = useAxios();
const axiosDefault = useAxios(true);

const refreshList = () => {
setLoading(true);
Expand Down Expand Up @@ -88,9 +77,9 @@ const UploadContainer = () => {
const downloadSpreadsheet = () => {
axios.get(ROUTES_UPLOAD.DOWNLOAD_SPREADSHEET, {
params: {
datasetSelected: datasetSelected
datasetSelected,
},
responseType: 'blob'
responseType: 'blob',
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
Expand All @@ -107,34 +96,50 @@ const UploadContainer = () => {
});
};

const handleRadioChange = (event) => {
const choice = event.target.value;
if (choice === 'replace') {
setOpenDialog(true);
} else {
setReplaceData(false);
}
};

const handleReplaceDataConfirm = () => {
setReplaceData(true)
setOpenDialog(false)
}

const handleReplaceDataCancel = () => {
setOpenDialog(false)
}

useEffect(() => {
refreshList(true);
}, []);

if (loading) {
return <Loading />
return <Loading />;
}

const alertElement = alert && alertContent && alertSeverity ? <Alert severity={alertSeverity}>{alertContent}</Alert> : null

return (
<div className="row">
<div className="col-12 mr-2">
{open && (
<AlertDialog
open={open}
setOpen={setOpen}
dialogue={dialogue}
rightButtonText={rightButtonText}
leftButtonText={leftButtonText}
setReplaceData={setReplaceData}
title="Replace existing data?"
open={openDialog}
title={'Replace existing data?'}
dialogue={'Selecting replace will delete all previously uploaded records for this dataset'}
cancelText={'Cancel'}
handleCancel={handleReplaceDataCancel}
confirmText={'Replace existing data'}
handleConfirm={handleReplaceDataConfirm}
/>
)}
<Stack direction="column" spacing={2}>
<Paper square variant="outlined">
<UploadPage
alertElement={alertElement}
alertElement={alertElement}
uploadFiles={uploadFiles}
datasetList={datasetList}
doUpload={doUpload}
Expand Down
10 changes: 4 additions & 6 deletions react/src/uploads/components/UploadPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,19 @@ const UploadPage = (props) => {
<FormControl component="fieldset">
<RadioGroup
aria-label="Replace or add to existing data"
value={replaceData}
value={replaceData ? "replace" : "add"}
name="radio-buttons-group"
onChange={handleRadioChange}
>
<FormControlLabel
disabled={datasetSelected ? false: true}
value
control={(
<Radio />
)}
value={"replace"}
control={<Radio />}
label="Replace existing data"
/>
<FormControlLabel
disabled={datasetSelected ? false: true}
value={false}
value={"add"}
control={<Radio />}
label="Add to existing data"
/>
Expand Down
Loading

0 comments on commit 442a169

Please sign in to comment.