Skip to content

Commit

Permalink
Add delete button to app (#428)
Browse files Browse the repository at this point in the history
* Add delete button to app

* Fix UT

* Use RTK Query for deleting batch

* npm run format

---------

Co-authored-by: Rob Gries <[email protected]>
  • Loading branch information
georgehelman and robert-w-gries authored Oct 1, 2023
1 parent 4965254 commit 1d1e297
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 38 deletions.
4 changes: 4 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ repos:
hooks:
- id: black
language_version: python3
- repo: https://github.com/pre-commit/mirrors-prettier
rev: "" # Use the sha or tag you want to point at
hooks:
- id: prettier
69 changes: 35 additions & 34 deletions dear_petition/petition/api/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,8 @@
create_zip_file,
)
from dear_petition.users.models import User
from dear_petition.petition.export.documents.advice_letter import (
generate_advice_letter
)
from dear_petition.petition.export.documents.records_summary import (
generate_summary
)
from dear_petition.petition.export.documents.advice_letter import generate_advice_letter
from dear_petition.petition.export.documents.records_summary import generate_summary

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -148,17 +144,23 @@ class ContactViewSet(viewsets.ModelViewSet):
ordering = ["name"]

def get_serializer_class(self):
if self.request.data.get('category') == 'client':
if self.request.data.get("category") == "client":
return serializers.ClientSerializer
return serializers.ContactSerializer

def get_queryset(self):
if self.request.query_params.get('category') == 'client' or self.request.data.get('category') == 'client':
if (
self.request.query_params.get("category") == "client"
or self.request.data.get("category") == "client"
):
return pm.Contact.objects.filter(user=self.request.user)
return pm.Contact.objects.all()

def perform_create(self, serializer):
if self.request.query_params.get('category') == 'client' or self.request.data.get('category') == 'client':
if (
self.request.query_params.get("category") == "client"
or self.request.data.get("category") == "client"
):
serializer.save(user=self.request.user)
else:
serializer.save()
Expand Down Expand Up @@ -187,9 +189,9 @@ class BatchViewSet(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated]
parser_classes = (parsers.MultiPartParser, parsers.FormParser, parsers.JSONParser)
filter_backends = [filters.OrderingFilter, DjangoFilterBackend]
filterset_fields = ['user']
ordering_fields = ['date_uploaded']
ordering = ['-date_uploaded']
filterset_fields = ["user"]
ordering_fields = ["date_uploaded"]
ordering = ["-date_uploaded"]

def get_serializer_class(self):
"""Use a custom serializer when accessing a specific batch"""
Expand All @@ -211,6 +213,13 @@ def create(self, request, *args, **kwargs):
headers = self.get_success_headers(serializer.data)
return Response(response, status=status.HTTP_201_CREATED, headers=headers)

def destroy(self, request, pk=None):
user = request.user
batch = self.queryset.get(pk=pk)
if batch and batch.user == user:
batch.delete()
return Response({}, status=status.HTTP_204_NO_CONTENT)

def perform_create(self, serializer):
files = self.request.data.getlist("files")
batch = import_ciprs_records(
Expand All @@ -229,7 +238,7 @@ def perform_update(self, serializer):
)
def generate_advice_letter(self, request, pk):
batch = self.get_object()
serializer = serializers.GenerateDocumentSerializer(data={'batch': pk})
serializer = serializers.GenerateDocumentSerializer(data={"batch": pk})
serializer.is_valid(raise_exception=True)

with tempfile.TemporaryDirectory() as tmpdir:
Expand All @@ -251,7 +260,7 @@ def generate_advice_letter(self, request, pk):
)
def generate_summary(self, request, pk):
batch = self.get_object()
serializer = serializers.GenerateDocumentSerializer(data={'batch': pk})
serializer = serializers.GenerateDocumentSerializer(data={"batch": pk})
serializer.is_valid(raise_exception=True)

with tempfile.TemporaryDirectory() as tmpdir:
Expand All @@ -262,9 +271,7 @@ def generate_summary(self, request, pk):
resp[
"Content-Type"
] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
resp[
"Content-Disposition"
] = 'attachment; filename="Records Summary.docx"'
resp["Content-Disposition"] = 'attachment; filename="Records Summary.docx"'
return resp


Expand All @@ -290,7 +297,9 @@ def create(self, request, *args, **kwargs):
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
return Response(
serializer.data, status=status.HTTP_201_CREATED, headers=headers
)

@action(
detail=True,
Expand Down Expand Up @@ -342,7 +351,7 @@ def generate_petition_pdf(self, request, pk=None):
petition = self.get_object()

request_data = request.data.copy()
request_data['petition'] = petition.pk
request_data["petition"] = petition.pk
serializer = serializers.GeneratePetitionSerializer(data=request_data)
serializer.is_valid(raise_exception=True)

Expand All @@ -358,12 +367,10 @@ def generate_petition_pdf(self, request, pk=None):
), "Petition documents could not be found for petition"

form_context = serializer.validated_data.copy()
form_context['client'] = client
form_context['attorney'] = petition.batch.attorney
form_context['agencies'] = petition.agencies
generated_petition_pdf = generate_petition_pdf(
petition_documents, form_context
)
form_context["client"] = client
form_context["attorney"] = petition.batch.attorney
form_context["agencies"] = petition.agencies
generated_petition_pdf = generate_petition_pdf(petition_documents, form_context)

for doc in petition_documents.iterator():
pm.GeneratedPetition.get_stats_generated_petition(doc.pk, request.user)
Expand All @@ -375,9 +382,7 @@ def generate_petition_pdf(self, request, pk=None):
)

petition = self.get_object()
petition_filename = utils.get_petition_filename(
client.name, petition, "pdf"
)
petition_filename = utils.get_petition_filename(client.name, petition, "pdf")
if addendum_documents.exists():
docs = [
generated_petition_pdf,
Expand All @@ -386,9 +391,7 @@ def generate_petition_pdf(self, request, pk=None):
petition_filename,
]
for addendum_document in addendum_documents:
doc = generate_addendum_document_file(
addendum_document
)
doc = generate_addendum_document_file(addendum_document)
docs.append(doc)
filename = utils.get_petition_filename(
client.name,
Expand All @@ -401,9 +404,7 @@ def generate_petition_pdf(self, request, pk=None):
zip_file = create_zip_file(docs, filenames)
resp = FileResponse(zip_file)
resp["Content-Type"] = "application/zip"
filename = utils.get_petition_filename(
client.name, petition, "zip"
)
filename = utils.get_petition_filename(client.name, petition, "zip")
resp["Content-Disposition"] = f'attachment; filename="{filename}"'
else:
resp = FileResponse(generated_petition_pdf)
Expand Down
9 changes: 8 additions & 1 deletion src/components/elements/Button/ModalButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,15 @@ export const ModalButton = ({ children, className, colorClass, title, allowClose
closeModal();
}
});
const handleClick = (e) => {
// ignore click if it's been propogated from child
if (e.currentTarget !== e.target) {
return;
}
setShowModal(true);
};
return (
<Button className={className} colorClass={colorClass ?? 'neutral'} onClick={() => setShowModal(true)}>
<Button className={className} colorClass={colorClass ?? 'neutral'} onClick={handleClick}>
{title}
<ModalContext.Provider value={{ closeModal }}>
<StyledDialog isOpen={showModal} onClose={allowCloseOnEscape ? closeModal : undefined}>
Expand Down
33 changes: 30 additions & 3 deletions src/features/ExistingPetitions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,44 @@ import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { formatDistance } from 'date-fns';
import { Link } from 'react-router-dom';
import { manualAxiosRequest } from '../service/axios';
import { Button } from '../components/elements/Button';
import { Button, ModalButton } from '../components/elements/Button';
import { Table, TableBody, TableCell, TableHeader, TableRow } from '../components/elements/Table';
import { Tooltip } from '../components/elements/Tooltip/Tooltip';
import { useGetUserBatchesQuery } from '../service/api';
import { useDeleteBatchMutation, useGetUserBatchesQuery } from '../service/api';
import useAuth from '../hooks/useAuth';
import { DownloadDocumentsModal } from './DownloadDocuments';
import { hasValidationsErrors } from '../util/errors';
import { downloadFile } from '../util/downloadFile';
import { CAUTION, NEUTRAL } from '../components/elements/Button/Button';
import { useModalContext } from '../components/elements/Button/ModalButton';

const DeleteBatchModal = ({ batch }) => {
const [triggerDelete] = useDeleteBatchMutation();
const { closeModal } = useModalContext();
return (
<div className="flex flex-col gap-10 justify-center w-[450px] h-[200px]">
<p className="text-[1.6rem] flex flex-wrap gap-x-2 gap-y-4 px-16">
<span>
<span className="text-red">Warning:</span>This action will PERMANENTLY delete the petition group:
</span>
<span className="font-semibold">{batch.label}</span>
</p>
<div className="flex gap-8 justify-center">
<Button colorClass={CAUTION} className="w-[100px]" onClick={() => triggerDelete({ id: batch.pk })}>
Delete
</Button>
<Button colorClass={NEUTRAL} className="w-[100px]" onClick={() => closeModal()}>
Cancel
</Button>
</div>
</div>
);
};

// TODO: Rename batches to "Petition Groups"
export const ExistingPetitions = () => {
const { user } = useAuth();
const { data } = useGetUserBatchesQuery({ user: user.pk });

const [downloadDocumentBatch, setDownloadDocumentBatch] = useState(null);

return (
Expand Down Expand Up @@ -112,6 +136,9 @@ export const ExistingPetitions = () => {
>
Records Summary
</Button>
<ModalButton title="Delete" colorClass={CAUTION}>
<DeleteBatchModal batch={batch} />
</ModalButton>
</TableCell>
</TableRow>
))}
Expand Down
5 changes: 5 additions & 0 deletions src/service/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export const api = createApi({
query: ({ data }) => ({ url: 'batch/', method: 'post', timeout: 30 * 1000, data }),
invalidatesTags: (result) => (result ? [{ type: 'Batch' }] : []),
}),
deleteBatch: builder.mutation({
query: ({ id }) => ({ url: `batch/${id}/`, method: 'delete' }),
invalidatesTags: ['Batch'],
}),
updateBatch: builder.mutation({
query: ({ id, data }) => ({ url: `batch/${id}/`, method: 'put', data }),
invalidatesTags: (result, _err, { id }) => {
Expand Down Expand Up @@ -153,6 +157,7 @@ export const {
useAssignAgenciesToDocumentsMutation,
useLazyGetContactFilterOptionsQuery,
useCreateBatchMutation,
useDeleteBatchMutation,
useLazyCheckLoginQuery,
useGetBatchQuery,
useGetUserBatchesQuery,
Expand Down

0 comments on commit 1d1e297

Please sign in to comment.