Skip to content

Commit

Permalink
Merge branch 'TreyWW:main' into fix/email-as-auth-bug
Browse files Browse the repository at this point in the history
  • Loading branch information
James-Makela authored Jun 12, 2024
2 parents aab5e3f + 82efa68 commit 3da0fe5
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 21 deletions.
46 changes: 46 additions & 0 deletions backend/api/clients/delete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from django.contrib import messages
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods

from backend.models import Client
from backend.types.htmx import HtmxHttpRequest


@require_http_methods(["DELETE"])
@login_required
def client_delete(request: HtmxHttpRequest, id: int):
try:
client = Client.objects.get(id=id)
except Client.DoesNotExist:
messages.error(request, "Client not found")
return render(request, "pages/clients/dashboard/_table.html", {"delete": True})

if not client:
messages.error(request, "Client not found")
return render(request, "pages/clients/dashboard/_table.html", {"delete": True})

if (
not request.user.is_authenticated
or request.user != client.user
or request.user.logged_in_as_team
and request.user.logged_in_as_team != client.organization
):
messages.error(request, "You do not have permission to delete this client")
return render(request, "pages/clients/dashboard/_table.html", {"delete": True})

client.delete()
messages.success(request, f'Client "{client.name}" deleted successfully')

if request.user.logged_in_as_team:
return render(
request,
"pages/clients/dashboard/_table.html",
{"clients": Client.objects.filter(organization=request.user.logged_in_as_team).order_by("-name"), "delete": True},
)
else:
return render(
request,
"pages/clients/dashboard/_table.html",
{"clients": Client.objects.filter(user=request.user).order_by("-name"), "delete": True},
)
9 changes: 7 additions & 2 deletions backend/api/clients/urls.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.urls import path
from . import fetch
from . import fetch, delete


urlpatterns = [
path(
Expand All @@ -12,6 +13,10 @@
fetch.fetch_clients_dropdown,
name="fetch dropdown",
),
path(
"delete/<int:id>/",
delete.client_delete,
name="delete",
),
]

app_name = "clients"
27 changes: 18 additions & 9 deletions frontend/templates/pages/clients/create/create.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<div class="card bg-base-100 p-6">
<form method="post">
{% csrf_token %}
<div class="divider mb-6">REQUIRED DETAILS</div>
<div class="divider mb-6">IDENTIFICATION DETAILS [REQUIRED]</div>
<div class="w-full md:w-1/3 lg:1/4">
<label class="label">
Single
Expand All @@ -19,8 +19,9 @@
<div class="card me-3 w-full bg-base-200 border border-base-200 hover:border-gray-400">
<div class="card-body w-full">
<div class="form-control w-full">
<label class="label">
<span class="label-text">Client Name</span>
<label class="label label-text justify-start">
Client Name
<span class="required_star">*</span>
</label>
<input required
id="clientName"
Expand All @@ -39,8 +40,8 @@
<div class="card me-3 w-full bg-base-200 border border-base-200 hover:border-gray-400">
<div class="card-body w-full">
<div class="form-control w-full">
<label class="label">
<span class="label-text">Client Email</span>
<label class="label justify-start label-text">
Client Email <span class="required_star">*</span>
</label>
<input required
id="clientEmail"
Expand All @@ -61,22 +62,26 @@
class="card me-3 w-full bg-base-200 border border-base-200 hover:border-gray-400">
<div class="card-body w-full">
<div class="form-control w-full">
<label class="label">
<span class="label-text">Client Company</span>
<label class="label label-text justify-start">
Client Company
<span class="required_star">*</span>
</label>
<input id="companyName"
name="company_name"
minlength="3"
type="text"
class="peer input input-bordered w-full"
placeholder="Bob Smith" />
placeholder="Google" />
<label class="label peer-[&amp;:not(:placeholder-shown):not(:focus):invalid]:block hidden">
<span class="label-text-alt text-error">Please enter a valid company name</span>
</label>
</div>
</div>
</div>
<div class="divider my-6">OPTIONAL DETAILS</div>
<div class="divider my-6">
INFORMATION STORAGE
<i class="text-neutral-content">[OPTIONAL]</i>
</div>
{# Client Address Card #}
<div class="my-4 w-full grid grid-cols-1 gap-4 md:grid-cols-2">
<div class="card me-3 w-full bg-base-200 border border-base-200 hover:border-gray-400">
Expand Down Expand Up @@ -139,14 +144,18 @@
<script>
is_rep_inp = document.querySelector('input[name="is_representative"]');
is_rep_div = document.querySelector('div[data-selection="representative"]');
company_input = document.querySelector('#companyName')

function check_is_rep() {
if (is_rep_inp.checked) {
is_rep_div.style.display = "block";
is_rep_inp.required = true;
company_input.required = true;

} else {
is_rep_div.style.display = "none";
is_rep_inp.required = false;
company_input.required = false;
}
}

Expand Down
6 changes: 5 additions & 1 deletion frontend/templates/pages/clients/dashboard/_rows.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{% load humanize %}
{% for client in clients %}
{% csrf_token %}
<tr>
<td>{{ client.id }}</td>
<td colspan="2">{{ client.name }}</td>
Expand All @@ -11,7 +12,10 @@
</button>
</div>
<div class="tooltip" data-tip="Delete">
<button class="btn btn-outline btn-error btn-sm btn-disabled">
<button class="btn btn-outline btn-error btn-sm"
hx-delete="{% url 'api:clients:delete' id=client.id %}"
hx-target="#items"
hx-confirm="Are you sure you would like to delete this client?">
<i class="fa fa-solid fa-trash"></i>
</button>
</div>
Expand Down
5 changes: 4 additions & 1 deletion frontend/templates/pages/clients/dashboard/_table.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{% if delete %}
{% component "messages_list" %}
{% endif %}
<div class="flex w-full overflow-x-auto overflow-y-hidden" id="items">
<table class="table-zebra table">
<table class="table">
<thead>
<tr>
<th>Client ID</th>
Expand Down
1 change: 1 addition & 0 deletions frontend/templates/pages/clients/dashboard/dashboard.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{% extends base|default:"base/base.html" %}
{% block content %}
<div class="card w-full p-6 bg-base-100 shadow-xl mt-2">
<h2 class="text-xl">Clients</h2>
<a class="btn btn-primary mb-4"
href="{% url "clients create" %}"
hx-boost="true">
Expand Down
1 change: 1 addition & 0 deletions frontend/templates/pages/invoices/dashboard/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<form id="sort_direction_storage">
</form>
<div class="card bg-base-100 p-6 mb-4 h-screen">
<h2 class="text-xl">Invoices</h2>
<div class="flex gap-3 justify-between mb-3">
<div class="dropdown dropdown-hover dropdown-right w-14">
<div tabindex="0"
Expand Down
21 changes: 13 additions & 8 deletions frontend/templates/pages/receipts/_search_results.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,28 +62,33 @@ <h2 class="menu-title">Amount</h2>
<form method="post">
<button type="button"
onclick="modal_receipt_{{ row.id }}.showModal();"
class="btn btn-outline btn-success btn-sm">Preview</button>
data-tip="Preview receipt in modal"
class="btn btn-outline btn-primary btn-sm tooltip">
<i class="fa fa-eye"></i>
</button>
<button onclick="modal_{{ row.id }}_receipts_upload.showModal();"
id="edit_receipt_button"
class="btn btn-outline btn-success btn-sm"
class="btn btn-outline btn-warning btn-sm tooltip"
for="edit_receipt"
data-tip="Edit receipt details"
hx-trigger="click once"
hx-swap="beforeend"
hx-target="#modal_container"
hx-get="{% url "api:base:modal retrieve with context" modal_name="receipts_upload" context_type="edit_receipt" context_value=row.id %}">
<i class="fa-solid fa-receipt pe-1"></i>
Edit Receipt
<i class="fa-solid fa-edit"></i>
</button>
<button type="button"
class="btn btn-outline btn-primary btn-sm"
class="btn btn-outline btn-success btn-sm tooltip"
data-tip="Download Receipt"
onclick="download_file('{% url 'api:receipts:generate_download_link' receipt_id=row.id %}')">
Download
<i class="fa fa-download"></i>
</button>
<button class="btn btn-outline btn-error btn-sm"
<button class="btn btn-error btn-sm tooltip"
data-tip="Delete receipt"
hx-delete="{% url 'api:receipts:delete' id=row.id %}"
hx-target="#items"
hx-confirm="Are you sure you would like to delete the receipt?">
Delete
<i class="fa fa-trash"></i>
</button>
</form>
</td>
Expand Down
36 changes: 36 additions & 0 deletions tests/api/test_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.urls import reverse
from model_bakery import baker

from backend.models import Client
from tests.handler import ViewTestCase, assert_url_matches_view


Expand Down Expand Up @@ -109,3 +110,38 @@ def test_search_functionality(self):
self.assertIn(client1, response.context["clients"])
self.assertIn(client2, response.context["clients"])
self.assertIn(client3, response.context["clients"])


class ClientAPIDelete(ViewTestCase):
def setUp(self):
super().setUp()
self.id = 1
self.url_path = f"/api/clients/delete/{self.id}/"
self.url_name = "api:clients:delete"
self.view_function_path = "backend.api.clients.delete.client_delete"

def test_client_delete_view_matches_with_urls_view(self):
self.assertEqual(reverse(self.url_name, args=[self.id]), self.url_path)

def test_client_delete_view_302_for_non_authenticated_users(self):
response = self.client.delete(reverse(self.url_name, args=[self.id]))
self.assertEqual(response.status_code, 302)

def test_client_delete_view_200_for_authenticated_users(self):
self.login_user()
client = baker.make("backend.Client", user=self.log_in_user)
response = self.client.delete(reverse(self.url_name, args=[client.id]))
self.assertEqual(response.status_code, 200)

def test_client_delete_view_deletes_client(self):
self.login_user()
client = baker.make("backend.Client", user=self.log_in_user)
self.client.delete(reverse(self.url_name, args=[client.id]))
with self.assertRaises(Client.DoesNotExist):
Client.objects.get(id=client.id)

def test_client_delete_view_returns_error_for_non_existent_client(self):
self.login_user()
response = self.client.delete(reverse(self.url_name, args=[999]))
self.assertEqual(response.status_code, 200) # in future should be 404
self.assertIn("Client not found", str(response.content))

0 comments on commit 3da0fe5

Please sign in to comment.