Merge pull request 'Search and bill detail page' (#135) from frontend into main

Reviewed-on: #135
This commit is contained in:
ismail 2025-07-10 19:20:16 +03:00
commit 19b2913a4e
13 changed files with 366 additions and 284 deletions

View File

@ -1,4 +1,4 @@
# Generated by Django 5.2.4 on 2025-07-09 13:00
# Generated by Django 5.1.7 on 2025-07-10 12:55
import django.db.models.deletion
import django.utils.timezone

View File

@ -1,4 +1,4 @@
# Generated by Django 5.2.4 on 2025-07-09 13:00
# Generated by Django 5.1.7 on 2025-07-10 12:55
import django.db.models.deletion
from django.db import migrations, models

View File

@ -1,4 +1,4 @@
# Generated by Django 5.2.4 on 2025-07-09 13:00
# Generated by Django 5.1.7 on 2025-07-10 12:55
import datetime
import django.core.serializers.json
@ -22,7 +22,7 @@ class Migration(migrations.Migration):
('appointment', '0001_initial'),
('auth', '0012_alter_user_first_name_max_length'),
('contenttypes', '0002_remove_content_type_name'),
('django_ledger', '0023_merge_20250708_1825'),
('django_ledger', '0021_alter_bankaccountmodel_account_model_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

View File

@ -2595,7 +2595,7 @@ class CustomGroup(models.Model):
"activity",
"opportunity",
"vendor",
"customer"
"customer",
"notes",
"tasks",
"activity",
@ -2707,7 +2707,8 @@ class CustomGroup(models.Model):
"notes",
"tasks",
"activity",
"payment"],
"payment",
'vendor'],
other_perms=[
"view_car",
"view_carlocation",
@ -2715,6 +2716,9 @@ class CustomGroup(models.Model):
"view_carcolors",
"view_cartransfer",
"view_saleorder",
"view_leads",
"view_opportunity",
"view_customers",
],
)

View File

@ -2496,7 +2496,7 @@ class VendorListView(LoginRequiredMixin,PermissionRequiredMixin, ListView):
@login_required
@permission_required('django_ledger.view_vendormodel',raise_exception=True)
@permission_required('inventory.view_vendor',raise_exception=True)
def vendorDetailView(request, dealer_slug,slug):
"""
Fetches and renders the detail view for a specific vendor.
@ -3862,6 +3862,15 @@ class BankAccountDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailV
template_name = "ledger/bank_accounts/bank_account_detail.html"
context_object_name = "bank_account"
permission_required = ["django_ledger.view_bankaccountmodel"]
def get_queryset(self):
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
query=self.request.GET.get('q')
qs=self.model.objects.filter(entity=dealer.entity)
if query:
qs=apply_search_filters(qs,query)
return qs
return qs
class BankAccountUpdateView(
@ -5580,14 +5589,23 @@ class LeadListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
query = self.request.GET.get("q")
qs = models.Lead.objects.filter(dealer=dealer).exclude(status="converted")
if query:
qs = apply_search_filters(qs, query)
qs = qs.filter(Q(first_name__icontains=query)
| Q(last_name__icontains=query)
| Q(id_car_make__name__icontains=query)
| Q(id_car_model__name__icontains=query)
| Q(email__icontains=query)
| Q(phone_number__icontains=query)
| Q(next_action__icontains=query)
| Q(staff__name__icontains=query))
if self.request.is_dealer:
return qs
if self.request.user.is_staff:
staff = getattr(self.request.user.staffmember, "staff", None)
return qs.filter(staff=staff)
return models.Lead.objects.none()
class LeadDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
"""
@ -6650,12 +6668,15 @@ class OpportunityListView(LoginRequiredMixin,PermissionRequiredMixin, ListView):
queryset = models.Opportunity.objects.filter(dealer=dealer, lead__staff=staff)
# Search filter
search = self.request.GET.get("search")
search = self.request.GET.get("q")
if search:
queryset = queryset.filter(
Q(customer__customer_name__icontains=search)
Q(customer__first_name__icontains=search)
| Q(customer__last_name__icontains=search)
| Q(customer__email__icontains=search)
)
)
# Stage filter
stage = self.request.GET.get("stage")
@ -6943,10 +6964,17 @@ class ItemServiceListView(LoginRequiredMixin, PermissionRequiredMixin, ListView)
paginate_by = 30
permission_required = ["inventory.view_additionalservices"]
def get_queryset(self):
dealer = get_user_type(self.request)
return models.AdditionalServices.objects.filter(dealer=dealer).all()
query=self.request.GET.get('q')
qs= models.AdditionalServices.objects.filter(dealer=dealer).all()
if query:
qs=apply_search_filters(qs,query)
return qs
# def get_queryset(self):
# dealer = get_user_type(self.request)
# return models.AdditionalServices.objects.filter(dealer=dealer).all()
class ItemExpenseCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
@ -7064,9 +7092,18 @@ class ItemExpenseListView(LoginRequiredMixin, PermissionRequiredMixin, ListView)
paginate_by = 30
permission_required = ["django_ledger.view_itemmodel"]
# def get_queryset(self):
# dealer = get_user_type(self.request)
# return dealer.entity.get_items_expenses()
def get_queryset(self):
dealer = get_user_type(self.request)
return dealer.entity.get_items_expenses()
query=self.request.GET.get('q')
qs= dealer.entity.get_items_expenses()
if query:
qs=apply_search_filters(qs,query)
return qs
class BillListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
@ -7096,6 +7133,9 @@ class BillListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
def get_queryset(self):
dealer = get_user_type(self.request)
qs = dealer.entity.get_bills()
query=self.request.GET.get('q')
if query:
qs=qs.filter(vendor__vendor_name__icontains=query)
return qs
def get_context_data(self, **kwargs):
@ -9921,10 +9961,19 @@ class PurchaseOrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListVie
template_name = "purchase_orders/po_list.html"
permission_required = ["django_ledger.view_purchaseordermodel"]
# def get_queryset(self):
# dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
# return self.model.objects.filter(entity=dealer.entity)
def get_queryset(self):
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
return self.model.objects.filter(entity=dealer.entity)
query=self.request.GET.get("q")
qs=self.model.objects.filter(entity=dealer.entity)
if query:
qs=apply_search_filters(qs,query)
return qs
# def get_queryset(self):
# dealer = get_user_type(self.request)
# entity = dealer.entity
@ -10007,7 +10056,7 @@ class BasePurchaseOrderActionActionView(BasePurchaseOrderActionActionViewBase):
class PurchaseOrderModelDeleteView(PurchaseOrderModelDeleteViewBase):
template_name = "purchase_orders/po_delete.html"
permission_required = "django_ledger.delete_purchaseordermodel"
permission_required = "django_ledger.delete_purchaseordermodel"
def get_success_url(self):
messages.add_message(

View File

@ -36,210 +36,232 @@
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="row g-4" >
<!-- Left Sidebar -->
<div class="col-lg-4">
<div class="card shadow-sm" >
<div class="card-body">
{% include 'bill/includes/card_bill.html' with dealer_slug=request.dealer.slug bill=bill entity_slug=view.kwargs.entity_slug style='bill-detail' %}
<div class="d-grid mt-4">
<a href="{% url 'bill_list' request.dealer.slug %}"
class="btn btn-phoenix-primary">
<i class="fas fa-arrow-left me-1"></i> {% trans 'Bill List' %}
</a>
</div>
</div>
</div>
</div>
<div class="row mt-3 mb-2">
<!-- Main Content -->
<div class="col-lg-8 ">
{% if bill.is_configured %}
<div class="card mb-4 shadow-sm">
<div class="card-body">
<div class="row text-center g-3">
<div class="col-md-3">
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'Cash Account' %}:
<a href="{% url 'account_detail' request.dealer.slug bill.cash_account.uuid %}"
class="text-decoration-none ms-1">
{{ bill.cash_account.code }}
</a>
</h6>
<h4 class="mb-0" id="djl-bill-detail-amount-paid">
{% currency_symbol %}{{ bill.get_amount_cash | absolute | currency_format }}
</h4>
</div>
</div>
{% if bill.accrue %}
<div class="col-md-3">
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'Prepaid Account' %}:
<a href="{% url 'account_detail' request.dealer.slug bill.prepaid_account.uuid %}"
class="text-decoration-none ms-1">
{{ bill.prepaid_account.code }}
</a>
</h6>
<h4 class="text-success mb-0" id="djl-bill-detail-amount-prepaid">
{% currency_symbol %}{{ bill.get_amount_prepaid | currency_format }}
</h4>
</div>
</div>
<div class="col-md-3">
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'Accounts Payable' %}:
<a href="{% url 'account_detail' request.dealer.slug bill.unearned_account.uuid %}"
class="text-decoration-none ms-1">
{{ bill.unearned_account.code }}
</a>
</h6>
<h4 class="text-danger mb-0" id="djl-bill-detail-amount-unearned">
{% currency_symbol %}{{ bill.get_amount_unearned | currency_format }}
</h4>
</div>
</div>
<div class="col-md-3">
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'Accrued' %} {{ bill.get_progress | percentage }}
</h6>
<h4 class="mb-0">
{% currency_symbol %}{{ bill.get_amount_earned | currency_format }}
</h4>
</div>
</div>
{% else %}
<div class="col-md-3 offset-md-6">
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'You Still Owe' %}
</h6>
<h4 class="text-danger mb-0" id="djl-bill-detail-amount-owed">
{% currency_symbol %}{{ bill.get_amount_open | currency_format }}
</h4>
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
<div class="col-12 col-md-3">
<div class="row">
<div class="col-12 mb-3">
<div class="card shadow-sm">
<div class="card-body">
{% include 'bill/includes/card_bill.html' with dealer_slug=request.dealer.slug bill=bill entity_slug=view.kwargs.entity_slug style='bill-detail' %}
<!-- Bill Items Card -->
<div class="card mb-4 shadow-sm">
<div class="card-header pb-0">
<div class="d-flex align-items-center mb-2">
<i class="fas fa-receipt me-3 text-primary"></i>
<h5 class="mb-0">{% trans 'Bill Items' %}</h5>
</div>
</div>
<div class="card-body px-0 pt-0 pb-2">
<div class="table-responsive">
<table class="table table-hover">
<thead >
<tr class="bg-body-highlight">
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Item' %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Entity Unit' %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Unit Cost' %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Quantity' %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Total' %}</th>
<th class="sort white-space-nowrap align-middle " scope="col">{% trans 'PO' %}</th>
</tr>
</thead>
<tbody class="list fs-9" id="project-list-table-body">
{% for bill_item in itemtxs_qs %}
<tr>
<td class="align-middle white-space-nowrap">
<div class="d-flex px-2 py-1">
<div class="d-flex flex-column justify-content-center">
<h6 class="mb-0 text-sm">{{ bill_item.item_model }}</h6>
</div>
<div class="d-grid mt-4">
<a href="{% url 'bill_list' request.dealer.slug %}" class="btn btn-phoenix-primary">
<i class="fas fa-arrow-left me-1"></i> {% trans 'Bill List' %}
</a>
</div>
</td>
<td class="align-middle white-space-nowrap">
<span class="text-xs font-weight-bold">
{% if bill_item.entity_unit %}
{{ bill_item.entity_unit }}
{% endif %}
</span>
</td>
<td class="align-middle white-space-nowrap">
<span class="text-xs font-weight-bold">
{{ bill_item.unit_cost | currency_format }}
</span>
</td>
<td class="align-middle white-space-nowrap">
<span class="text-xs font-weight-bold">{{ bill_item.quantity }}</span>
</td>
<td class="align-middle white-space-nowrap">
<span class="text-xs font-weight-bold">
{{ bill_item.total_amount | currency_format }}
</span>
</td>
<td class="align-items-start white-space-nowrap pe-2">
{% if bill_item.po_model_id %}
{% if perms.django_ledger.view_purchaseordermodel%}
<a class="btn btn-sm btn-phoenix-primary"
href="{% url 'purchase_order_detail' request.dealer.slug request.dealer.entity.slug bill_item.po_model_id %}">
{% trans 'View PO' %}
</a>
{% endif %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="3"></td>
<td class="text-end"><strong>{% trans 'Total' %}</strong></td>
<td class="text-end">
<strong>
{% currency_symbol %}{{ total_amount__sum | currency_format }}
</strong>
</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="card shadow-sm ">
<div class="card-header pb-0">
<div class="d-flex align-items-center mb-2">
<i class="fas fa-sticky-note me-3 text-primary"></i>
<h5 class="mb-0">{% trans 'Bill Notes' %}</h5>
</div>
</div>
{% if perms.django_ledger.change_billmodel%}
<div class="card-body">
{% include 'bill/includes/card_markdown.html' with style='card_1' title='' notes_html=bill.notes_html %}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Bill Transactions Card -->
<div class="card mb-4 shadow-sm">
<div class="card-header pb-0">
<div class="d-flex align-items-center mb-2">
<i class="fas fa-exchange-alt me-3 text-primary"></i>
<h5 class="mb-0">{% trans 'Bill Transactions' %}</h5>
</div>
</div>
<div class="card-body px-0 pt-0 pb-2 table-responsive">
{% transactions_table bill %}
</div>
</div>
<!-- Bill Notes Card -->
<div class="card shadow-sm ">
<div class="card-header pb-0">
<div class="d-flex align-items-center mb-2">
<i class="fas fa-sticky-note me-3 text-primary"></i>
<h5 class="mb-0">{% trans 'Bill Notes' %}</h5>
</div>
</div>
{% if perms.django_ledger.change_billmodel%}
<div class="card-body">
{% include 'bill/includes/card_markdown.html' with style='card_1' title='' notes_html=bill.notes_html %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
<div class="col-12 col-md-9">
<div class="row">
<div class="col-12">
{% if bill.is_configured %}
<div class="card mb-3 shadow-sm">
<div class="card-body">
<div class="row text-center g-3">
<div class="col-12 col-md-3">
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'Cash Account' %}:
<a href="{% url 'account_detail' request.dealer.slug bill.cash_account.uuid %}"
class="text-decoration-none ms-1">
{{ bill.cash_account.code }}
</a>
</h6>
<h4 class="mb-0" id="djl-bill-detail-amount-paid">
{% currency_symbol %}{{ bill.get_amount_cash | absolute | currency_format }}
</h4>
</div>
</div>
{% if bill.accrue %}
<div class="col-12 col-md-3">
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'Prepaid Account' %}:
<a href="{% url 'account_detail' request.dealer.slug bill.prepaid_account.uuid %}"
class="text-decoration-none ms-1">
{{ bill.prepaid_account.code }}
</a>
</h6>
<h4 class="text-success mb-0" id="djl-bill-detail-amount-prepaid">
{% currency_symbol %}{{ bill.get_amount_prepaid | currency_format }}
</h4>
</div>
</div>
<div class="col-12 col-md-3">
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'Accounts Payable' %}:
<a href="{% url 'account_detail' request.dealer.slug bill.unearned_account.uuid %}"
class="text-decoration-none ms-1">
{{ bill.unearned_account.code }}
</a>
</h6>
<h4 class="text-danger mb-0" id="djl-bill-detail-amount-unearned">
{% currency_symbol %}{{ bill.get_amount_unearned | currency_format }}
</h4>
</div>
</div>
<div class="col-12 col-md-3">
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'Accrued' %} {{ bill.get_progress | percentage }}
</h6>
<h4 class="mb-0">
{% currency_symbol %}{{ bill.get_amount_earned | currency_format }}
</h4>
</div>
</div>
{% else %}
<div class="col-12 col-md-3 offset-md-6">
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'You Still Owe' %}
</h6>
<h4 class="text-danger mb-0" id="djl-bill-detail-amount-owed">
{% currency_symbol %}{{ bill.get_amount_open | currency_format }}
</h4>
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
</div>
<div class="col-12">
<div class="card mb-3 shadow-sm">
<div class="card-header pb-0">
<div class="d-flex align-items-center mb-2">
<i class="fas fa-receipt me-3 text-primary"></i>
<h5 class="mb-0">{% trans 'Bill Items' %}</h5>
</div>
</div>
<div class="card-body px-0 pt-0 pb-2">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr class="bg-body-highlight">
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Item' %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Entity Unit' %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Unit Cost' %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Quantity' %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Total' %}</th>
<th class="sort white-space-nowrap align-middle " scope="col">{% trans 'PO' %}</th>
</tr>
</thead>
<tbody class="list fs-9" id="project-list-table-body">
{% for bill_item in itemtxs_qs %}
<tr>
<td class="align-middle white-space-nowrap">
<div class="d-flex px-2 py-1">
<div class="d-flex flex-column justify-content-center">
<h6 class="mb-0 text-sm">{{ bill_item.item_model }}</h6>
</div>
</div>
</td>
<td class="align-middle white-space-nowrap">
<span class="text-xs font-weight-bold">
{% if bill_item.entity_unit %}
{{ bill_item.entity_unit }}
{% endif %}
</span>
</td>
<td class="align-middle white-space-nowrap">
<span class="text-xs font-weight-bold">
{{ bill_item.unit_cost | currency_format }}
</span>
</td>
<td class="align-middle white-space-nowrap">
<span class="text-xs font-weight-bold">{{ bill_item.quantity }}</span>
</td>
<td class="align-middle white-space-nowrap">
<span class="text-xs font-weight-bold">
{{ bill_item.total_amount | currency_format }}
</span>
</td>
<td class="align-items-start white-space-nowrap pe-2">
{% if bill_item.po_model_id %}
{% if perms.django_ledger.view_purchaseordermodel%}
<a class="btn btn-sm btn-phoenix-primary"
href="{% url 'purchase_order_detail' request.dealer.slug request.dealer.entity.slug bill_item.po_model_id %}">
{% trans 'View PO' %}
</a>
{% endif %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="3"></td>
<td class="text-end"><strong>{% trans 'Total' %}</strong></td>
<td class="text-end">
<strong>
{% currency_symbol %}{{ total_amount__sum | currency_format }}
</strong>
</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="card mb-3 shadow-sm">
<div class="card-header pb-0">
<div class="d-flex align-items-center mb-2">
<i class="fas fa-exchange-alt me-3 text-primary"></i>
<h5 class="mb-0">{% trans 'Bill Transactions' %}</h5>
</div>
</div>
<div class="card-body px-0 pt-0 pb-2 table-responsive">
{% transactions_table bill %}
</div>
</div>
</div>
</div>
</div>
</div>
{% include "bill/includes/mark_as.html" %}
{% endblock %}

View File

@ -18,7 +18,12 @@
<div class="card mb-2">
<div class="card-body">
{% include 'bill/includes/card_bill.html' with dealer_slug=request.dealer.slug bill=bill_model style='bill-detail' entity_slug=view.kwargs.entity_slug %}
<form action="{% url 'bill-update' dealer_slug=request.dealer.slug entity_slug=view.kwargs.entity_slug bill_pk=bill_model.uuid %}" method="post">
<a href="{% url 'bill-detail' dealer_slug=request.dealer.slug entity_slug=view.kwargs.entity_slug bill_pk=bill_model.uuid %}"
class="btn btn-phoenix-secondary w-100 mb-2">
<i class="fas fa-arrow-left me-2"></i>{% trans 'Back to Bill Detail' %}
</a>
<form action="{% url 'bill-update' dealer_slug=request.dealer.slug entity_slug=view.kwargs.entity_slug bill_pk=bill_model.uuid %}" method="post">
{% csrf_token %}
<div class="mb-3">
@ -29,18 +34,15 @@
<i class="fas fa-save me-2"></i>{% trans 'Save Bill' %}
</button>
<a href="{% url
<a href="{% url 'bill-detail' dealer_slug=request.dealer.slug entity_slug=view.kwargs.entity_slug bill_pk=bill_model.uuid %}"
class="btn btn-phoenix-secondary w-100 mb-2">
<i class="fas fa-arrow-left me-2"></i>{% trans 'Back to Bill Detail' %}
</a>
<a href="{% url 'bill_list' request.dealer.slug %}"
class="btn btn-phoenix-info w-100 mb-2">
<i class="fas fa-list me-2"></i>{% trans 'Bill List' %}
</a>
</form>
</form>
<a href="{% url 'bill_list' request.dealer.slug %}"
class="btn btn-phoenix-info w-100 mb-2">
<i class="fas fa-list me-2"></i>{% trans 'Bill List' %}
</a>
</div>
</div>
</div>

View File

@ -50,18 +50,20 @@
<div class="" dir="ltr">{{ _("Phone Number") }}</div>
</div>
</th>
<th class="align-middle white-space-nowrap text-uppercase" scope="col" style="width: 15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-primary-subtle rounded me-2"><span class="text-primary-dark" data-feather="phone"></span></div>
<span>{{ _("Schedule") }}</span>
</div>
</th>
<th class="align-middle white-space-nowrap text-uppercase" scope="col" style="width: 10%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center bg-warning-subtle rounded me-2"><span class="text-warning-dark" data-feather="zap"></span></div>
<span>{{ _("Action")|capfirst }}</span>
<span>{{ _("Next Action")|capfirst }}</span>
</div>
</th>
<th class="align-middle white-space-nowrap text-uppercase" scope="col" style="width: 15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-primary-subtle rounded me-2"><span class="far fa-calendar-alt" ></span></div>
<span>{{ _("Scheduled at") }}</span>
</div>
</th>
<th class="align-middle white-space-nowrap text-uppercase" scope="col" style="width: 10%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center bg-success-subtle rounded me-2"><span class="text-success-dark" data-feather="user-check"></span></div>
@ -69,15 +71,16 @@
</div>
</th>
<th class="align-middle white-space-nowrap text-uppercase" scope="col" style="width: 10%;">
{% comment %} <th class="align-middle white-space-nowrap text-uppercase" scope="col" style="width: 10%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center bg-warning-subtle rounded me-2"><span class="text-warning-dark" data-feather="grid"></span></div>
<span>{{ _("Opportunity")|capfirst }}</span>
</div>
</th>
</th> {% endcomment %}
<th class="align-middle white-space-nowrap text-uppercase" scope="col" style="width: 15%;">
{{ _("Action") }}
</th>
<th class="text-end white-space-nowrap align-middle" scope="col"></th>
</tr>
{% for lead in leads %}
@ -103,7 +106,7 @@
</div>
<tbody>
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="name align-middle white-space-nowrap ps-0">
<td class="name align-middle white-space-nowrap ps-1">
<div class="d-flex align-items-center">
<div><a class="fs-8 fw-bold" href="{% url 'lead_detail' request.dealer.slug lead.slug %}">{{lead.full_name|capfirst}}</a>
<div class="d-flex align-items-center">
@ -128,9 +131,9 @@
<td class="align-middle white-space-nowrap fw-semibold"><a class="text-body-highlight" href="">{{ lead.id_car_make.get_local_name }} - {{ lead.id_car_model.get_local_name }} {{ lead.year }}</a></td>
<td class="align-middle white-space-nowrap fw-semibold"><a class="text-body-highlight" href="">{{ lead.email }}</a></td>
<td class="align-middle white-space-nowrap fw-semibold"><a class="text-body-highlight" href="tel:{{ lead.phone_number }}">{{ lead.phone_number }}</a></td>
<td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">{{ lead.get_status|upper }}</td>
<td class="align-middle white-space-nowrap fw-semibold"><a class="text-body-highlight" href="tel:{{ lead.phone_number }}">{{ lead.phone_number }}</a></td>
<td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">{{ lead.next_action|upper }}</td>
<td class="align-middle white-space-nowrap fw-semibold">{{ lead.next_action_date|upper }}</td>
<td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">{{ lead.staff|upper }}</td>
{% comment %} <td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">
{% if lead.opportunity.stage == "prospect" %}
@ -145,13 +148,13 @@
<span class="badge text-bg-danger">{{ lead.opportunity.stage|upper }}</span>
{% endif %}
</td> {% endcomment %}
<td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">
{% comment %} <td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">
{% if lead.opportunity %}
<a href="{% url 'opportunity_detail' request.dealer.slug lead.opportunity.slug %}">
<span class="badge badge-phoenix badge-phoenix-success">Opportunity {{ lead.opportunity.lead}} <i class="fa-solid fa-arrow-up-right-from-square"></i></span>
</a>
{% endif %}
</td>
</td> {% endcomment %}
<td class="align-middle white-space-nowrap text-end">
{% if user == lead.staff.user or request.is_dealer %}
<div class="btn-reveal-trigger position-static">

View File

@ -12,25 +12,22 @@
<!-- Filter Controls -->
<div class="d-flex flex-column flex-lg-row align-items-start align-items-lg-center gap-3 w-100" id="filter-container">
<!-- Search Input - Wider and properly aligned -->
<div class="position-relative flex-grow-1" style="min-width: 300px;">
<span class="fas fa-search position-absolute top-50 translate-middle-y ms-3 text-body-tertiary"></span>
<input
class="form-control ps-6"
type="text"
placeholder="{% trans 'Search opportunities...' %}"
name="search"
hx-get="{% url 'opportunity_list' request.dealer.slug %}"
hx-trigger="keyup changed delay:500ms"
hx-target="#opportunities-grid"
hx-select="#opportunities-grid"
hx-include="#filter-container select"
hx-swap="outerHTML"
style="width: 100%;"
>
</div>
<div class="search-box position-relative flex-grow-1 me-2" style="min-width: 200px;">
<form class="position-relative show" id="search-form">
<input name="q" id="search-input" class="form-control form-control-sm search-input search" type="search"
aria-label="Search" placeholder="{{ _('Search') }}" value="{{ request.GET.q }}" />
<span class="fa fa-magnifying-glass search-box-icon"></span>
{% if request.GET.q %}
<button type="button" class="btn-close position-absolute end-0 top-50 translate-middle cursor-pointer shadow-none"
id="clear-search" aria-label="Close"></button>
{% endif %}
</form>
</div>
<!-- Filter Dropdowns - Aligned in a row -->
<div class="d-flex flex-column flex-sm-row gap-3 w-100" style="max-width: 500px;">
<div class="d-flex flex-column flex-sm-row gap-3 w-100" style="max-width: 400px;">
<!-- Stage Filter -->
<div class="flex-grow-1">
<select
@ -78,7 +75,7 @@
</div>
</div>
{% if perms.inventory.add_opportunity %}
<div class="d-flex justify-content-end">
<div class="d-flex justify-content-between">
<a class="btn btn-phoenix-primary btn-sm" href="{% url 'opportunity_create' request.dealer.slug %}">
<span class="fas fa-plus me-2"></span>{{ _("Add Opportunity") }}
</a>
@ -101,4 +98,24 @@
</div>
{% endif %}
{% block customJS %}
<script>
document.addEventListener("DOMContentLoaded", function() {
const searchInput = document.getElementById("search-input");
const clearButton = document.getElementById("clear-search");
if (clearButton) {
clearButton.addEventListener("click", function(event) {
event.preventDefault();
searchInput.value = ""; // Clear input field
// Remove query parameter without reloading the page
const newUrl = window.location.pathname;
history.replaceState(null, "", newUrl);
window.location.reload();
});
}
});
</script>
{% endblock %}
{% endblock %}

View File

@ -48,7 +48,7 @@
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="company" style="width:15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-warning-subtle rounded me-2"><span class="text-warning-dark" data-feather="grid"></span></div><span>{{ _("Address")|capfirst }}</span>
<div class="d-flex align-items-center px-1 py-1 bg-warning-subtle rounded me-2"><span class="text-warning-dark" data-feather="home"></span></div><span>{{ _("Address")|capfirst }}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="company" style="width:15%;">
@ -57,7 +57,7 @@
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase" scope="col" data-sort="date" style="width:15%;">
{{ _("Create date") }}</th>
{{ _("Create date") }} <span class="text-warning-dark" data-feather="clock"></span></th>
<th class="sort text-end align-middle pe-0 ps-4" scope="col"></th>
</tr>
</thead>

View File

@ -19,25 +19,10 @@
{% endif %}
</div>
<div class="col-12">
<form method="get" class=" mb-4">
<div class="input-group input-group-sm">
<button class="btn btn-sm btn-phoenix-secondary rounded-start" type="submit">
{% trans "search" %}
</button>
<input type="text"
name="q"
class="form-control form-control-sm rounded-end"
value="{{ request.GET.q }}"
placeholder="{% trans 'Search bills...' %}" />
{% if request.GET.q %}
<a href="{% url request.resolver_match.view_name %}"
class="btn btn-sm btn-phoenix-danger ms-1 rounded">
<i class="bi bi-x-lg"></i>
</a>
{% endif %}
</div>
</form>
<div class="col-auto">
<div class="d-flex">
{% include 'partials/search_box.html' %}
</div>
</div>

View File

@ -149,7 +149,7 @@
onclick="showPOModal('Fulfill PO', '{% url 'po-action-mark-as-fulfilled' request.dealer.slug entity_slug po_model.pk %}', 'Mark As Fulfilled')">
<i class="fas fa-truck me-2"></i>{% trans 'Mark as Fulfilled' %}
</button>
{% endif %}

View File

@ -129,7 +129,7 @@
<p class="mb-0 fs-9 text-body">{{tx.customer.phone_number}}</p>
</td>
<td class="align-middle white-space-nowrap quotation">
{% if tx.estimate %}
{% if tx.estimate and perms.django_ledger.view_estimatemodel%}
<p class="fw-bo text-body fs-9 mb-0">
<a href="{% url 'estimate_detail' request.dealer.slug tx.estimate.uuid %}">
{{ tx.estimate.estimate_number}}
@ -147,7 +147,7 @@
{% endif %}
</td>
<td class="align-middle white-space-nowrap invoice">
{% if tx.invoice %}
{% if tx.invoice and perms.django_ledger.view_invoicemodel%}
<p class="fw-bo text-body fs-9 mb-0">
<a href="{% url 'invoice_detail' request.dealer.slug tx.invoice.uuid %}">
{{tx.invoice.invoice_number}}