update the notes crud

This commit is contained in:
ismail 2025-09-07 13:52:05 +03:00
parent 7178e92673
commit 29532ffbd0
23 changed files with 94 additions and 239 deletions

View File

@ -2448,12 +2448,7 @@ class Opportunity(models.Model):
null=True,
blank=True,
)
probability = models.PositiveIntegerField(validators=[validate_probability],null=True, blank=True)
amount = models.DecimalField(
max_digits=10,
decimal_places=2,
verbose_name=_("Amount"),
)
expected_revenue = models.DecimalField(
max_digits=10,
decimal_places=2,
@ -2461,8 +2456,6 @@ class Opportunity(models.Model):
blank=True,
null=True,
)
vehicle_of_interest_make = models.CharField(max_length=50, blank=True, null=True)
vehicle_of_interest_model = models.CharField(max_length=100, blank=True, null=True)
expected_close_date = models.DateField(blank=True, null=True)
created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated"))
@ -2523,8 +2516,6 @@ class Opportunity(models.Model):
return objects
def save(self, *args, **kwargs):
if self.amount:
self.expected_revenue = self.amount * self.probability / 100
opportinity_for = ""
if self.lead.lead_type == "customer":
self.customer = self.lead.customer

View File

@ -6750,12 +6750,26 @@ def delete_note(request, dealer_slug, pk):
:return: An HTTP redirection to the lead detail page of the corresponding note's lead.
:rtype: HttpResponseRedirect
"""
note = get_object_or_404(models.Notes, pk=pk, created_by=request.user)
lead_pk = note.content_object.pk
lead = models.Lead.objects.get(pk=lead_pk)
note.delete()
messages.success(request, _("Note deleted successfully."))
return redirect("lead_detail", dealer_slug=dealer_slug, slug=lead.slug)
try:
note = get_object_or_404(models.Notes, pk=pk, created_by=request.user)
print(note)
if isinstance(note.content_object, models.Lead):
url = "lead_detail"
slug = note.content_object.slug
if hasattr(note.content_object, "opportunity"):
url = "opportunity_detail"
slug = note.content_object.opportunity.slug
elif isinstance(note.content_object, models.Opportunity):
url = "opportunity_detail"
slug = note.content_object.slug
note.delete()
messages.success(request, _("Note deleted successfully."))
except Exception as e:
print("Errroooorrr: ",e)
print(url)
print(dealer_slug)
return redirect(url, dealer_slug=dealer_slug,slug=slug)
@login_required
@ -6837,9 +6851,9 @@ def schedule_event(request, dealer_slug, content_type, slug):
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
obj = get_object_or_404(model, slug=slug)
if not request.is_staff:
messages.error(request, _("You do not have permission to schedule."))
return redirect(f"{content_type}_detail", dealer_slug=dealer_slug, slug=slug)
# if not request.is_staff:
# messages.error(request, _("You do not have permission to schedule."))
# return redirect(f"{content_type}_detail", dealer_slug=dealer_slug, slug=slug)
if request.method == "POST":
from django_q.models import Schedule as DjangoQSchedule
@ -7340,7 +7354,6 @@ class OpportunityDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailV
form.fields["stage"].widget.attrs["hx-get"] = url
form.fields["stage"].initial = self.object.stage
context["status_form"] = form
context["lead_notes"] = models.Notes.objects.filter(
content_type__model="lead", object_id=self.object.id
).order_by("-created")
@ -10188,8 +10201,9 @@ def add_note(request, dealer_slug, content_type, slug):
@require_http_methods(["POST"])
@permission_required("inventory.change_notes", raise_exception=True)
def update_note(request, dealer_slug, pk):
print(pk)
note = get_object_or_404(models.Notes, pk=pk)
lead = get_object_or_404(models.Lead, pk=note.content_object.id)
# lead = get_object_or_404(models.Lead, pk=note.content_object.id)
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
if request.method == "POST":
note.note = request.POST.get("note")

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

View File

@ -13,7 +13,7 @@
<p class="text-body-tertiary">{{ _("Are you sure you want to sign out?") }}</p>
</div>
<div class="d-grid">
<form hx-boost="false" method="post" action="{% url 'account_logout' %}">
<form method="post" action="{% url 'account_logout' %}">
{% csrf_token %}
{{ redirect_field }}
<div class="d-grid gap-2 mt-3">

View File

@ -27,7 +27,7 @@
<h3 class="mb-4">{% trans "Change Password" %}</h3>
</div>
<form method="post"
hx-boost="false"
action="{% url 'account_change_password' %}"
class="form needs-validation"
novalidate>

View File

@ -24,7 +24,8 @@
<h3 class="text-body-highlight">{% trans 'Car Dealership Registration' %}</h3>
<p class="text-body-tertiary fs-9">{% trans 'Create your dealership account today' %}</p>
</div>
<div class="mx-auto mt-4 text-center">
</div>
<form method="post" action="{% url 'account_signup' %}" class="needs-validation">
{% csrf_token %}
<div class="card theme-wizard">
@ -32,15 +33,13 @@
{{ form|crispy }}
</div>
<button class="btn btn-primary" type="submit">{% trans 'Sign Up' %}</button>
</form>
</div>
</div>
</form>
<a href="#" class="float-start mt-4" data-bs-toggle="modal" data-bs-target="#contentModal">
Our Refund Policy
</a>
</div>
</div>
<div class="mx-auto mt-4 text-center">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#contentModal" data-url="{% url 'refund_policy' %}">
Our Refund Policy
</button>
</div>
</div>
</section>
@ -51,148 +50,11 @@
<h5 class="modal-title" id="contentModalLabel">Refund Policy</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Loading...</p>
<div class="modal-body modal-dialog-scrollable">
{% include "haikal_policy/refund_policy.html" %}
</div>
</div>
</div>
</div>
{% include 'footer.html' %}
<script src="{% static 'js/phoenix.js' %}"></script>
{% endblock content %}
{% block customJS %}
<script src="{% static 'js/main.js' %}"></script>
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
<script type="module"
src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-beta.11/bundles/datastar.js"></script>
<script>
function validatePassword(password, confirmPassword) {
return password === confirmPassword && password.length > 7 && password !== '';
}
function validateEmail(email) {
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return emailRegex.test(email) && email !== '';
}
function validateform2(name,arabic_name,phone_number) {
if (name === '' || arabic_name === '' || phone_number === '' || phone_number.length < 10 || !phone_number.startsWith('05')) {
return false;
}
return true
}
function validate_sa_phone_number(phone_number) {
const phone_numberRegex = /^05[0-9]{8}$/;
return phone_numberRegex.test(phone_number) && phone_numberRegex !== '';
}
function getAllFormData() {
const forms = document.querySelectorAll('.needs-validation');
const formData = {};
forms.forEach(form => {
const fields = form.querySelectorAll('input,textarea,select');
fields.forEach(field => {
formData[field.name] = field.value;
});
});
return formData;
}
function showLoading() {
Swal.fire({
title: "{% trans 'Please Wait' %}",
text: "{% trans 'Loading' %}...",
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
}
});
}
function hideLoading() {
Swal.close();
}
function notify(tag,msg){
Swal.fire({
icon: tag,
titleText: msg
});
}
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== "") {
const cookies = document.cookie.split(";");
for (let cookie of cookies) {
cookie = cookie.trim();
if (cookie.substring(0, name.length + 1) === name + "=") {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
async function sendFormData() {
const formData = getAllFormData();
const url = "{% url 'account_signup' %}";
const csrftoken = getCookie('csrftoken');
try {
showLoading();
const response = await fetch(url, {
method: 'POST',
headers: {
'X-CSRFToken': '{{csrf_token}}',
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});
hideLoading();
const data = await response.json();
if (response.ok) {
notify("success","Account created successfully");
setTimeout(() => {
window.location.href = "{% url 'account_login' %}";
}, 1000);
} else {
notify("error",data.error);
}
} catch (error) {
notify("error",error);
}
}
//modal for policy
document.addEventListener('DOMContentLoaded', function() {
const contentModal = document.getElementById('contentModal');
if (contentModal) {
contentModal.addEventListener('show.bs.modal', function(event) {
// Get the button that triggered the modal
const button = event.relatedTarget;
// Extract the URL from the button's data-url attribute
const url = button.getAttribute('data-url');
// Select the modal body element
const modalBody = contentModal.querySelector('.modal-body');
// Use the Fetch API to load content
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.text();
})
.then(html => {
// Insert the HTML content into the modal body
modalBody.innerHTML = html;
})
.catch(error => {
modalBody.innerHTML = `<p class="text-danger">Failed to load content: ${error.message}</p>`;
});
});
}
});
</script>
{% endblock customJS %}
{% include 'footer.html' %}
{% endblock content %}

View File

@ -87,13 +87,7 @@
<img src="{% static 'spinner.svg' %}" width="100" height="100" alt="">
</div>
<div id="main_content"
class="fade-me-in"
hx-boost="false"
hx-target="#main_content"
hx-select="#main_content"
hx-swap="outerHTML transition:true"
hx-select-oob="#toast-container"
hx-history-elt>
class="fade-me-in">
{% block customCSS %}{% endblock %}
{% block content %}
{% endblock content %}

View File

@ -1,6 +1,6 @@
{% load django_ledger %}
{% load i18n %}
<div id="djl-bill-card-widget" class="" hx-boost="false">
<div id="djl-bill-card-widget" class="">
{% if not create_bill %}
{% if style == 'dashboard' %}
<!-- Dashboard Style Card -->
@ -54,7 +54,7 @@
<a href="{% url 'django_ledger:bill-detail' entity_slug=entity_slug bill_pk=bill.uuid %}"
class="btn btn-sm btn-phoenix-primary me-md-2">{% trans 'View' %}</a>
{% if perms.django_ledger.change_billmodel %}
<a hx-boost="true"
<a
href="{% url 'django_ledger:bill-update' entity_slug=entity_slug bill_pk=bill.uuid %}"
class="btn btn-sm btn-phoenix-warning me-md-2">{% trans 'Update' %}</a>
{% if bill.can_pay %}
@ -203,7 +203,7 @@
<!-- Update Button -->
{% if perms.django_ledger.change_billmodel %}
{% if "update" not in request.path %}
<a hx-boost="true"
<a
href="{% url 'bill-update' dealer_slug=dealer_slug entity_slug=entity_slug bill_pk=bill.uuid %}">
<button class="btn btn-phoenix-primary">
<i class="fas fa-edit me-2"></i>{% trans 'Update' %}

View File

@ -16,11 +16,12 @@
</div>
<div class="modal-body">
<form id="noteForm"
hx-boost="true"
action="{% url 'add_note' request.dealer.slug content_type slug %}"
hx-select="#notesTable"
hx-target="#notesTable"
hx-on::after-request="{ resetSubmitButton(document.querySelector('.add_note_form button[type=submit]')); $('#noteModal').modal('hide'); }"
hx-swap="outerHTML"
hx-swap="outerHTML show:window.top"
method="post"
class="add_note_form">
{% csrf_token %}
@ -33,12 +34,13 @@
</div>
<script>
function updateNote(e) {
let form = document.querySelector('#noteForm');
let url = e.getAttribute('data-url');
let note = e.getAttribute('data-note');
document.querySelector('#id_note').value = note;
let form = document.querySelector('#noteForm');
form.action = url;
htmx.process(form);
}

View File

@ -16,11 +16,13 @@
</div>
<div class="modal-body">
<form id="scheduleForm"
hx-boost="true"
action="{% url 'schedule_event' request.dealer.slug content_type slug %}"
hx-select=".taskTable"
hx-target=".taskTable"
hx-on::after-request="{ resetSubmitButton(document.querySelector('.add_schedule_form button[type=submit]')); $('#scheduleModal').modal('hide'); }"
hx-swap="outerHTML"
hx-select-oob="#toast-container:outerHTML"
method="post"
class="add_schedule_form">
{% csrf_token %}

View File

@ -23,13 +23,12 @@
</div>
<div class="modal-body">
<form id="taskForm"
action="{% url 'add_task' request.dealer.slug content_type slug %}"
method="post"
class="add_task_form"
hx-post="{% url 'add_task' request.dealer.slug content_type slug %}"
hx-target="#your-content-container"
hx-swap="innerHTML"
hx-boost="true">
>
{% csrf_token %}
{{ staff_task_form|crispy }}
<button type="submit" class="btn btn-phoenix-success w-100">{% trans 'Save' %}</button>

View File

@ -324,11 +324,7 @@
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade"
id="tab-activity"
hx-get="{% url 'lead_detail' request.dealer.slug lead.slug %}"
hx-trigger="htmx:afterRequest from:#noteForm, htmx:afterRequest from:#scheduleForm"
hx-select="#tab-activity"
hx-target="this"
hx-swap="outerHTML"
role="tabpanel"
aria-labelledby="activity-tab">
<div class="mb-1 d-flex justify-content-between align-items-center">
@ -619,7 +615,7 @@
<td class="sent align-middle white-space-nowrap text-start fw-bold text-body-tertiary py-2">{{ email.from_email }}</td>
<td class="date align-middle white-space-nowrap text-body py-2">{{ email.created|naturalday }}</td>
<td class="align-middle white-space-nowrap ps-3">
<a class="text-body" href=""><span class="fa-solid fa-phone text-primary me-2"></span>{%trans "Call"%</a>
<a class="text-body" href=""><span class="fa-solid fa-phone text-primary me-2"></span>{%trans "Call"%}</a>
</td>
<td class="status align-middle fw-semibold text-end py-2">
<span class="badge badge-phoenix fs-10 badge-phoenix-success">{%trans "sent"%}</span>

View File

@ -9,7 +9,6 @@
<h5 class="card-header">{% trans "Send Mail" %}</h5>
<div class="card-body">
<form class="email-form d-flex flex-column h-100"
hx-boost="true"
action="{% url 'send_lead_email' request.dealer.slug lead.slug %}"
method="post">
{% csrf_token %}
@ -49,9 +48,7 @@
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex gap-2">
{% comment %} <a href="{{ request.META.HTTP_REFERER }}" class="btn btn-phoenix-danger">Discard</a> {% endcomment %}
<a hx-boost="true"
hx-push-url='false'
hx-include="#message,#subject,#to"
<a
href="{% url 'send_lead_email' request.dealer.slug lead.slug %}?status=draft"
class="btn btn-phoenix-success">{% trans "Save as Draft" %}</a>
<button class="btn btn-phoenix-primary fs-10" type="submit">

View File

@ -15,7 +15,7 @@
</h3>
</div>
<div class="card-body p-4 p-md-5">
<form hx-boost="false"
<form
method="post"
enctype="multipart/form-data"
class="needs-validation"

View File

@ -1,17 +1,4 @@
{% load i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans "Haikal Refund Policy" %}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css">
</head>
<body>
<div class="policy-container m-4">
<div class="policy-header">
<h1>{% trans "Haikal Refund Policy" %}</h1>
@ -80,7 +67,3 @@
</p>
</section>
</div>
</body>
</html>

View File

@ -5,12 +5,7 @@
<div class="navbar-vertical-content d-flex flex-column">
<ul class="navbar-nav flex-column"
id="navbarVerticalNav"
hx-boost="false"
hx-target="#main_content"
hx-select="#main_content"
hx-swap="outerHTML"
hx-select-oob="#toast-container"
hx-indicator="#spinner">
>
<li class="nav-item">
{% comment %} <p class="navbar-vertical-label text-primary fs-8 text-truncate">{{request.dealer|default:"Apps"}}</p>
@ -38,7 +33,7 @@
<li class="collapsed-nav-item-title d-none">{% trans "Inventory"|capfirst %}</li>
{% if perms.inventory.add_car %}
<li class="nav-item">
<a hx-boost="false"
<a
id="btn-add-car"
class="nav-link btn-add-car"
href="{% url 'car_add' request.dealer.slug %}">
@ -201,7 +196,7 @@
<li class="collapsed-nav-item-title d-none">{% trans 'sales'|capfirst %}</li>
{% if perms.django_ledger.add_estimatemodel %}
<li class="nav-item">
<a hx-boost="false"
<a
class="nav-link"
href="{% url 'estimate_create' request.dealer.slug %}">
<div class="d-flex align-items-center">
@ -514,7 +509,7 @@
</div>
</div>
{% endif %}
<ul class="navbar-nav navbar-nav-icons flex-row gap-2" hx-boost="false">
<ul class="navbar-nav navbar-nav-icons flex-row gap-2">
<li class="nav-item">
<div class="theme-control-toggle fa-icon-wait">
<input class="form-check-input ms-0 theme-control-toggle-input"
@ -566,7 +561,7 @@
{% endif %}
{% if user.is_authenticated and request.is_dealer or request.is_staff %}
<li class="nav-item dropdown">
<a hx-boost="false"
<a
class="nav-link lh-1 pe-0"
id="navbarDropdownUser"
role="button"
@ -614,7 +609,7 @@
</li>
{% else %}
<li class="nav-item">
<a hx-boost="false"
<a
class="nav-link px-3 d-block"
href="{% url 'staff_detail' request.dealer.slug request.staff.slug %}"> <span class="me-2 text-body align-bottom" data-feather="user"></span><span>{% translate 'profile'|capfirst %}</span></a>
</li>
@ -648,7 +643,7 @@
{% endif %}
{% if request.is_staff %}
<li class="nav-item">
<a hx-boost="false"
<a
class="nav-link px-3 d-block"
href="{% url 'schedule_calendar' request.dealer.slug %}"> <span class="me-2 text-body align-bottom" data-feather="calendar"></span>{{ _("My Calendar") }}</a>
</li>

View File

@ -170,7 +170,7 @@
data-bs-toggle="modal"
data-bs-target="#mainModal"
hx-get="{% url 'add_custom_card' request.dealer.slug car.slug %}"
hx-target=".main-modal-body"
hx-target="#mainModalBody"
hx-swap="innerHTML">{% trans 'Add' %}</button>
{% endif %}
</td>
@ -518,7 +518,7 @@
data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="main-modal-body" style="padding: 20px;">
<div id="mainModalBody" class="main-modal-body" style="padding: 20px;">
<!-- Content will be loaded here via AJAX -->
</div>
</div>
@ -693,5 +693,18 @@
button.classList.add("d-none");
});
}
document.addEventListener('htmx:afterRequest', e => {
console.log('PUT Success!')
});
document.addEventListener('htmx:beforeSwap', e => {
console.log('Before Swap!')
});
document.addEventListener('htmx:swapError', e => {
console.log('Swap Error!')
});
document.addEventListener('htmx:afterSwap', e => {
console.log('After Swap!')
});
</script>
{% endblock %}

View File

@ -25,7 +25,7 @@
{% endif %}
<!---->
<div class="row justify-content-center mt-5 mb-3 {% if not vendor_exists %}d-none{% endif %}"
hx-boost="false">
>
<div class="col-lg-8 col-md-10">
<div class="card shadow-sm border-0 rounded-3">
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">

View File

@ -5,7 +5,7 @@
{{ _("Create Bill") }}
{% endblock title %}
{% block content %}
<div class="row mt-4" hx-boost="true">
<div class="row mt-4">
<h3 class="text-center">
{% trans "Create Bill" %}<span class="fas fa-money-bills ms-2 text-primary"></span>
</h3>

View File

@ -24,8 +24,10 @@
</div>
<div class="modal-footer flex justify-content-center border-top-0">
<a id="deleteModalConfirm"
hx-boost="true"
hx-select-oob="#notesTable:outerHTML,#toast-container:outerHTML"
hx-swap="none"
hx-on::after-request="{ $('#deleteModal').modal('hide'); }"
hx-swap="none show:#notesTable"
type="button"
class="btn btn-sm btn-phoenix-danger w-100"
href="">{{ _("Delete") }}</a>
@ -57,6 +59,7 @@
if (!deleteModal || !confirmDeleteBtn || !deleteModalMessage) return;
const deleteUrl = this.getAttribute("data-url");
console.log(deleteUrl)
const deleteMessage = this.getAttribute("data-message") || "Are you sure you want to delete this item?";
// Update modal content
@ -68,11 +71,13 @@
htmx.process(confirmDeleteBtn);
}
// Show the modal
/*if (typeof bootstrap !== 'undefined') {
const modal = new bootstrap.Modal(deleteModal);
modal.show();
}*/
}
}
</script>

View File

@ -104,7 +104,7 @@
{% endif %}
</div>
</div>
<div class="d-flex align-items-center gap-2" hx-boost="false">
<div class="d-flex align-items-center gap-2">
{% if estimate.status == 'draft' %}
{% if perms.django_ledger.change_estimatemodel %}
<button id="mark_as_sent_estimate"

View File

@ -87,6 +87,7 @@
{% endif %}
</div>
{% else %}
{% include "empty-illustration-page.html" with value="invoice" url="#" %}
{% url 'estimate_create' request.dealer.slug as url %}
{% include "empty-illustration-page.html" with value="invoice" url=url %}
{% endif %}
{% endblock %}

View File

@ -119,6 +119,7 @@
</div>
</section>
{% else %}
{% include "empty-illustration-page.html" with value="sale order" url='#' %}
{% url 'estimate_create' request.dealer.slug as url %}
{% include "empty-illustration-page.html" with value="Sale Orders" url=url %}
{% endif %}
{% endblock %}