rebase with marwan update
This commit is contained in:
parent
7a1b15bb97
commit
fa111b159f
@ -660,7 +660,7 @@ class Car(Base):
|
|||||||
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
|
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
|
||||||
mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage"))
|
mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage"))
|
||||||
receiving_date = models.DateTimeField(verbose_name=_("Receiving Date"))
|
receiving_date = models.DateTimeField(verbose_name=_("Receiving Date"))
|
||||||
sold_date=models.DateTimeField(verbose_name=_("Sold Date"))
|
sold_date=models.DateTimeField(verbose_name=_("Sold Date"),null=True,blank=True)
|
||||||
hash = models.CharField(
|
hash = models.CharField(
|
||||||
max_length=64, blank=True, null=True, verbose_name=_("Hash")
|
max_length=64, blank=True, null=True, verbose_name=_("Hash")
|
||||||
)
|
)
|
||||||
|
|||||||
@ -6104,6 +6104,7 @@ def update_lead_actions(request, dealer_slug):
|
|||||||
current_action = request.POST.get("current_action")
|
current_action = request.POST.get("current_action")
|
||||||
next_action = request.POST.get("next_action")
|
next_action = request.POST.get("next_action")
|
||||||
next_action_date = request.POST.get("next_action_date", None)
|
next_action_date = request.POST.get("next_action_date", None)
|
||||||
|
lead = models.Lead.objects.get(id=lead_id)
|
||||||
|
|
||||||
if not all([lead_id, current_action, next_action]):
|
if not all([lead_id, current_action, next_action]):
|
||||||
# Log for missing required fields
|
# Log for missing required fields
|
||||||
@ -6111,12 +6112,17 @@ def update_lead_actions(request, dealer_slug):
|
|||||||
f"User {user_username} submitted incomplete data to update lead actions "
|
f"User {user_username} submitted incomplete data to update lead actions "
|
||||||
f"for dealer '{dealer_slug}'. Missing fields: lead_id='{lead_id}', current_action='{current_action}', next_action='{next_action}'."
|
f"for dealer '{dealer_slug}'. Missing fields: lead_id='{lead_id}', current_action='{current_action}', next_action='{next_action}'."
|
||||||
)
|
)
|
||||||
return JsonResponse(
|
messages.error(
|
||||||
{"success": False, "message": "All fields are required"}, status=400
|
request,
|
||||||
|
_("All fields are required")
|
||||||
)
|
)
|
||||||
|
return redirect("lead_detail", dealer_slug=dealer_slug, slug=lead.slug)
|
||||||
|
# return JsonResponse(
|
||||||
|
# {"success": False, "message": "All fields are required"}, status=400
|
||||||
|
# )
|
||||||
|
|
||||||
# Get the lead
|
# Get the lead
|
||||||
lead = models.Lead.objects.get(id=lead_id)
|
|
||||||
|
|
||||||
# Update lead fields
|
# Update lead fields
|
||||||
|
|
||||||
@ -6146,9 +6152,14 @@ def update_lead_actions(request, dealer_slug):
|
|||||||
f"submitted invalid date format ('{next_action_date}') "
|
f"submitted invalid date format ('{next_action_date}') "
|
||||||
f"for Lead ID: {lead.pk}. Error: {ve}"
|
f"for Lead ID: {lead.pk}. Error: {ve}"
|
||||||
)
|
)
|
||||||
return JsonResponse(
|
messages.error(
|
||||||
{"success": False, "message": "Invalid date format"}, status=400
|
request,
|
||||||
|
_("Invalid date format")
|
||||||
)
|
)
|
||||||
|
return redirect("lead_detail", dealer_slug=dealer_slug, slug=lead.slug)
|
||||||
|
# return JsonResponse(
|
||||||
|
# {"success": False, "message": "Invalid date format"}, status=400
|
||||||
|
# )
|
||||||
# Save the lead
|
# Save the lead
|
||||||
lead.save()
|
lead.save()
|
||||||
# --- Logging for successful update (main try block success) ---
|
# --- Logging for successful update (main try block success) ---
|
||||||
@ -6156,9 +6167,14 @@ def update_lead_actions(request, dealer_slug):
|
|||||||
f"User {user_username} successfully updated Lead ID: {lead.pk} ('{lead.slug}'). "
|
f"User {user_username} successfully updated Lead ID: {lead.pk} ('{lead.slug}'). "
|
||||||
f"New Status: '{lead.status}', Next Action: '{lead.next_action}', Next Action Date: '{lead.next_action_date}'."
|
f"New Status: '{lead.status}', Next Action: '{lead.next_action}', Next Action Date: '{lead.next_action_date}'."
|
||||||
)
|
)
|
||||||
return JsonResponse(
|
messages.success(
|
||||||
{"success": True, "message": "Actions updated successfully"}
|
request,
|
||||||
|
_("Actions updated successfully")
|
||||||
)
|
)
|
||||||
|
return redirect("lead_detail", dealer_slug=dealer_slug, slug=lead.slug)
|
||||||
|
# return JsonResponse(
|
||||||
|
# {"success": True, "message": "Actions updated successfully"}
|
||||||
|
# )
|
||||||
|
|
||||||
except models.Lead.DoesNotExist:
|
except models.Lead.DoesNotExist:
|
||||||
# --- Logging for Lead not found ---
|
# --- Logging for Lead not found ---
|
||||||
@ -6166,7 +6182,12 @@ def update_lead_actions(request, dealer_slug):
|
|||||||
f"User {user_username} attempted to update non-existent Lead with ID: '{lead_id}' "
|
f"User {user_username} attempted to update non-existent Lead with ID: '{lead_id}' "
|
||||||
f"for dealer '{dealer_slug}'. Returning 404."
|
f"for dealer '{dealer_slug}'. Returning 404."
|
||||||
)
|
)
|
||||||
return JsonResponse({"success": False, "message": "Lead not found"}, status=404)
|
messages.error(
|
||||||
|
request,
|
||||||
|
_("Lead not found")
|
||||||
|
)
|
||||||
|
return redirect("lead_detail", dealer_slug=dealer_slug, slug=lead.slug)
|
||||||
|
# return JsonResponse({"success": False, "message": "Lead not found"}, status=404)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
involved_lead_id = request.POST.get("lead_id", "N/A")
|
involved_lead_id = request.POST.get("lead_id", "N/A")
|
||||||
logger.error(
|
logger.error(
|
||||||
@ -6174,7 +6195,12 @@ def update_lead_actions(request, dealer_slug):
|
|||||||
f"for dealer '{dealer_slug}'. Error: {e}",
|
f"for dealer '{dealer_slug}'. Error: {e}",
|
||||||
exc_info=True, # CRUCIAL: Includes the full traceback
|
exc_info=True, # CRUCIAL: Includes the full traceback
|
||||||
)
|
)
|
||||||
return JsonResponse({"success": False, "message": str(e)}, status=500)
|
messages.error(
|
||||||
|
request,
|
||||||
|
_("An error occurred while updating lead actions")
|
||||||
|
)
|
||||||
|
return redirect("lead_detail", dealer_slug=dealer_slug, slug=lead.slug)
|
||||||
|
# return JsonResponse({"success": False, "message": str(e)}, status=500)
|
||||||
|
|
||||||
|
|
||||||
class LeadUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
class LeadUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||||||
@ -6558,7 +6584,7 @@ def lead_transfer(request, dealer_slug, slug):
|
|||||||
messages.success(request, _("Lead transferred successfully"))
|
messages.success(request, _("Lead transferred successfully"))
|
||||||
else:
|
else:
|
||||||
messages.error(request, f"Invalid form data: {str(form.errors)}")
|
messages.error(request, f"Invalid form data: {str(form.errors)}")
|
||||||
return redirect("lead_list", dealer_slug=dealer.slug)
|
return redirect("lead_detail", dealer_slug=dealer.slug ,slug=lead.slug)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -10399,7 +10425,7 @@ def upload_cars(request, dealer_slug, pk=None):
|
|||||||
|
|
||||||
form = forms.CSVUploadForm()
|
form = forms.CSVUploadForm()
|
||||||
form.fields["vendor"].queryset = dealer.vendors.all()
|
form.fields["vendor"].queryset = dealer.vendors.all()
|
||||||
print(request)
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"csv_upload.html",
|
"csv_upload.html",
|
||||||
@ -10596,4 +10622,3 @@ def car_sale_report_csv_export(request,dealer_slug):
|
|||||||
])
|
])
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -132,3 +132,45 @@ html[dir="rtl"] .form-icon-container .form-control {
|
|||||||
to { transform: rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#spinner {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
#spinner-bg {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 500ms ease-in;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#spinner-bg.htmx-request {
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* .fade-me-in.htmx-added {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.fade-me-in {
|
||||||
|
opacity: .9;
|
||||||
|
transition: opacity 300ms ease-out;
|
||||||
|
} */
|
||||||
|
|
||||||
|
#main_content.fade-me-in:not(.modal):not(.modal *) {
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 300ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main_content.fade-me-in.htmx-added:not(.modal):not(.modal *) {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
1
static/spinner.svg
Normal file
1
static/spinner.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'><rect fill='#09577F' stroke='#09577F' stroke-width='15' width='30' height='30' x='25' y='85'><animate attributeName='opacity' calcMode='spline' dur='2' values='1;0;1;' keySplines='.5 0 .5 1;.5 0 .5 1' repeatCount='indefinite' begin='-.4'></animate></rect><rect fill='#09577F' stroke='#09577F' stroke-width='15' width='30' height='30' x='85' y='85'><animate attributeName='opacity' calcMode='spline' dur='2' values='1;0;1;' keySplines='.5 0 .5 1;.5 0 .5 1' repeatCount='indefinite' begin='-.2'></animate></rect><rect fill='#09577F' stroke='#09577F' stroke-width='15' width='30' height='30' x='145' y='85'><animate attributeName='opacity' calcMode='spline' dur='2' values='1;0;1;' keySplines='.5 0 .5 1;.5 0 .5 1' repeatCount='indefinite' begin='0'></animate></rect></svg>
|
||||||
@ -13,7 +13,7 @@
|
|||||||
<p class="text-body-tertiary">{{ _("Are you sure you want to sign out?") }}</p>
|
<p class="text-body-tertiary">{{ _("Are you sure you want to sign out?") }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-grid">
|
<div class="d-grid">
|
||||||
<form method="post" action="{% url 'account_logout' %}">
|
<form hx-boost="false" method="post" action="{% url 'account_logout' %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ redirect_field }}
|
{{ redirect_field }}
|
||||||
<div class="d-grid gap-2 mt-3">
|
<div class="d-grid gap-2 mt-3">
|
||||||
|
|||||||
@ -69,7 +69,7 @@
|
|||||||
<script src="{% static 'js/main.js' %}"></script>
|
<script src="{% static 'js/main.js' %}"></script>
|
||||||
<script src="{% static 'js/jquery.min.js' %}"></script>
|
<script src="{% static 'js/jquery.min.js' %}"></script>
|
||||||
{% comment %} <script src="{% static 'js/echarts.js' %}"></script> {% endcomment %}
|
{% comment %} <script src="{% static 'js/echarts.js' %}"></script> {% endcomment %}
|
||||||
{% block customCSS %}{% endblock %}
|
{% comment %} {% block customCSS %}{% endblock %} {% endcomment %}
|
||||||
</head>
|
</head>
|
||||||
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
|
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
|
||||||
{% include "toast-alert.html" %}
|
{% include "toast-alert.html" %}
|
||||||
@ -81,10 +81,20 @@
|
|||||||
{% include "plans/expiration_messages.html" %}
|
{% include "plans/expiration_messages.html" %}
|
||||||
{% block period_navigation %}
|
{% block period_navigation %}
|
||||||
{% endblock period_navigation %}
|
{% endblock period_navigation %}
|
||||||
<div id="main_content" hx-boost="true" hx-target="#main_content" hx-select="#main_content" hx-swap="outerHTML" hx-select-oob="#toast-container" hx-history-elt>
|
<div id="main_content" class="fade-me-in" hx-boost="true" hx-target="#main_content" hx-select="#main_content" hx-swap="outerHTML transition:false" hx-select-oob="#toast-container" hx-history-elt>
|
||||||
{% block content %}
|
<div id="spinner" class="htmx-indicator spinner-bg">
|
||||||
{% endblock content %}
|
<img src="{% static 'spinner.svg' %}" width="100" height="100" alt="">
|
||||||
<script src="{% static 'vendors/popper/popper.min.js' %}"></script>
|
</div>
|
||||||
|
{% block customCSS %}{% endblock %}
|
||||||
|
{% block content %}{% endblock content %}
|
||||||
|
{% block customJS %}{% endblock %}
|
||||||
|
|
||||||
|
{% comment %} <script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
|
||||||
|
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
|
||||||
|
<script src="{% static 'vendors/popper/popper.min.js' %}"></script>
|
||||||
|
<script src="{% static 'vendors/bootstrap/bootstrap.min.js' %}"></script>
|
||||||
|
<script src="{% static 'js/phoenix.js' %}"></script> {% endcomment %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% endblock body %}
|
{% endblock body %}
|
||||||
@ -136,7 +146,16 @@
|
|||||||
let datePickers = document.querySelectorAll("[id^='djl-datepicker']")
|
let datePickers = document.querySelectorAll("[id^='djl-datepicker']")
|
||||||
datePickers.forEach(dp => djLedger.getCalendar(dp.attributes.id.value, dateNavigationUrl))
|
datePickers.forEach(dp => djLedger.getCalendar(dp.attributes.id.value, dateNavigationUrl))
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
/*document.body.addEventListener('htmx:configRequest', function(evt) {
|
||||||
|
evt.detail.indicators = [
|
||||||
|
...(evt.detail.indicators || []),
|
||||||
|
document.getElementById('global-indicator')
|
||||||
|
];
|
||||||
|
});*/
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% block customJS %}{% endblock %}
|
{% comment %} {% block customJS %}{% endblock %} {% endcomment %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
{% load i18n crispy_forms_tags %}
|
{% load i18n crispy_forms_tags %}
|
||||||
<div class="modal fade" id="emailModal" tabindex="-1" aria-labelledby="emailModalLabel" aria-hidden="true">
|
<div class="modal fade" id="emailModal" tabindex="-1" aria-labelledby="emailModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-xl">
|
<div class="modal-dialog modal-xl">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header justify-content-between align-items-start gap-5 px-4 pt-4 pb-3 border-0">
|
<div class="modal-header justify-content-between align-items-start gap-5 px-4 pt-4 pb-3 border-0">
|
||||||
<h4 class="modal-title" id="emailModalLabel">{% trans 'Send Email' %}</h4>
|
<h4 class="modal-title" id="emailModalLabel">{% trans 'Send Email' %}</h4>
|
||||||
<button class="btn p-0 text-body-quaternary fs-6" data-bs-dismiss="modal" aria-label="Close">
|
<button class="btn p-0 text-body-quaternary fs-6" data-bs-dismiss="modal" aria-label="Close">
|
||||||
<span class="fas fa-times"></span>
|
<span class="fas fa-times"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
<div id="emailModalBody" class="modal-body">
|
|
||||||
<h1>hi</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="emailModalBody" class="modal-body">
|
||||||
|
<h1>hi</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -16,6 +16,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form action="{% url 'add_note' request.dealer.slug content_type slug %}"
|
<form 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"
|
||||||
method="post"
|
method="post"
|
||||||
class="add_note_form">
|
class="add_note_form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
@ -33,5 +40,6 @@
|
|||||||
document.querySelector('#id_note').value = note
|
document.querySelector('#id_note').value = note
|
||||||
let form = document.querySelector('.add_note_form')
|
let form = document.querySelector('.add_note_form')
|
||||||
form.action = url
|
form.action = url
|
||||||
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -23,6 +23,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form action="{% url 'add_task' request.dealer.slug content_type slug %}"
|
<form action="{% url 'add_task' request.dealer.slug content_type slug %}"
|
||||||
|
hx-select=".taskTable"
|
||||||
|
hx-target=".taskTable"
|
||||||
|
hx-on::after-request="{
|
||||||
|
resetSubmitButton(document.querySelector('.add_task_form button[type=submit]'));
|
||||||
|
$('#taskModal').modal('hide');
|
||||||
|
}"
|
||||||
method="post"
|
method="post"
|
||||||
class="add_task_form">
|
class="add_task_form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
border-bottom: 28px solid transparent;
|
border-bottom: 28px solid transparent;
|
||||||
border-left: 20px solid #dee2e6;
|
border-left: 20px solid #dee2e6;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
{% endblock customCSS %}
|
{% endblock customCSS %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@ -68,7 +69,7 @@
|
|||||||
<p>{{ lead.email|capfirst }}</p>
|
<p>{{ lead.email|capfirst }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-sm-auto flex-1">
|
<div class="col-6 col-sm-auto flex-1">
|
||||||
<h5 class="text-body-highlight mb-0 text-end">
|
<h5 id="leadStatus" class="text-body-highlight mb-0 text-end">
|
||||||
{{ _("Status") }}
|
{{ _("Status") }}
|
||||||
{% if lead.status == "new" %}
|
{% if lead.status == "new" %}
|
||||||
<span class="badge badge-phoenix badge-phoenix-primary"><span class="badge-label">{{ _("New") }}</span><span class="fa fa-bell ms-1"></span></span>
|
<span class="badge badge-phoenix badge-phoenix-primary"><span class="badge-label">{{ _("New") }}</span><span class="fa fa-bell ms-1"></span></span>
|
||||||
@ -103,7 +104,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card mb-2">
|
<div class="card mb-2">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row align-items-center g-3 text-center text-xxl-start">
|
<div id="assignedTo" class="row align-items-center g-3 text-center text-xxl-start">
|
||||||
<div class="col-6 col-sm-auto d-flex flex-column align-items-center text-center">
|
<div class="col-6 col-sm-auto d-flex flex-column align-items-center text-center">
|
||||||
<h5 class="fw-bolder mb-2 text-body-highlight">{{ _("Assigned To") }}</h5>
|
<h5 class="fw-bolder mb-2 text-body-highlight">{{ _("Assigned To") }}</h5>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
@ -219,7 +220,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-7 col-lg-7 col-xl-8">
|
<div class="col-md-7 col-lg-7 col-xl-8">
|
||||||
<div class="d-flex w-100 gap-5">
|
<div id="currentStage" class="d-flex w-100 gap-5">
|
||||||
<div class="kanban-header bg-success w-50 text-white fw-bold">
|
<div class="kanban-header bg-success w-50 text-white fw-bold">
|
||||||
<i class="fa-solid fa-circle-check me-2"></i>{{ lead.status|capfirst }}
|
<i class="fa-solid fa-circle-check me-2"></i>{{ lead.status|capfirst }}
|
||||||
<br>
|
<br>
|
||||||
@ -303,6 +304,11 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<form class="modal-content"
|
<form class="modal-content"
|
||||||
action="{% url 'lead_transfer' request.dealer.slug lead.slug %}"
|
action="{% url 'lead_transfer' request.dealer.slug lead.slug %}"
|
||||||
|
hx-select-oob="#assignedTo:outerHTML,#toast-container:outerHTML"
|
||||||
|
hx-swap="none"
|
||||||
|
hx-on::after-request="{
|
||||||
|
resetSubmitButton(document.querySelector('#exampleModal button[type=submit]'));
|
||||||
|
$('#exampleModal').modal('hide');}"
|
||||||
method="post">
|
method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@ -471,7 +477,7 @@
|
|||||||
<th class="align-middle pe-0 text-end" scope="col" style="width:10%;"></th>
|
<th class="align-middle pe-0 text-end" scope="col" style="width:10%;"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody id="notesTable">
|
||||||
{% for note in notes %}
|
{% for note in notes %}
|
||||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||||
<td class="align-middle text-start fw-bold text-body-tertiary ps-1">{{ note.note }}</td>
|
<td class="align-middle text-start fw-bold text-body-tertiary ps-1">{{ note.note }}</td>
|
||||||
@ -793,7 +799,7 @@
|
|||||||
style="min-width:100px">Completed</th>
|
style="min-width:100px">Completed</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="list" id="all-tasks-table-body">
|
<tbody class="list taskTable" id="all-tasks-table-body">
|
||||||
{% for task in schedules %}
|
{% for task in schedules %}
|
||||||
{% include "partials/task.html" %}
|
{% include "partials/task.html" %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -862,7 +868,9 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
function openActionModal(leadId, currentAction, nextAction, nextActionDate) {
|
function openActionModal(leadId, currentAction, nextAction, nextActionDate) {
|
||||||
const modal = new bootstrap.Modal(document.getElementById('actionTrackingModal'));
|
|
||||||
|
const modal = new bootstrap.Modal(document.getElementById('actionTrackingModal'));
|
||||||
|
document.getElementById('actionTrackingForm').setAttribute('hx-boost', 'false');
|
||||||
document.getElementById('leadId').value = leadId;
|
document.getElementById('leadId').value = leadId;
|
||||||
document.getElementById('currentAction').value = currentAction;
|
document.getElementById('currentAction').value = currentAction;
|
||||||
document.getElementById('nextAction').value = nextAction;
|
document.getElementById('nextAction').value = nextAction;
|
||||||
@ -870,7 +878,7 @@
|
|||||||
modal.show();
|
modal.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('actionTrackingForm').addEventListener('submit', function(e) {
|
/*document.getElementById('actionTrackingForm').addEventListener('submit', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const formData = new FormData(this);
|
const formData = new FormData(this);
|
||||||
|
|
||||||
@ -951,7 +959,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});*/
|
||||||
|
|
||||||
// Helper function for notifications
|
// Helper function for notifications
|
||||||
function notify(tag, msg) {
|
function notify(tag, msg) {
|
||||||
@ -961,5 +969,25 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
// Close modal after successful form submission
|
||||||
{% endblock customJS %}
|
document.body.addEventListener('htmx:afterSwap', function(evt) {
|
||||||
|
if (evt.detail.target.id === 'main_content') {
|
||||||
|
var modal = bootstrap.Modal.getInstance(document.getElementById('exampleModal'));
|
||||||
|
if (modal) {
|
||||||
|
modal.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cleanup modal backdrop if needed
|
||||||
|
document.body.addEventListener('htmx:beforeSwap', function(evt) {
|
||||||
|
if (evt.detail.target.id === 'main_content') {
|
||||||
|
var backdrops = document.querySelectorAll('.modal-backdrop');
|
||||||
|
backdrops.forEach(function(backdrop) {
|
||||||
|
backdrop.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock customJS %}
|
||||||
|
|||||||
@ -239,10 +239,6 @@
|
|||||||
class="dropdown-item text-success-dark">{% trans "Edit" %}</a>
|
class="dropdown-item text-success-dark">{% trans "Edit" %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.inventory.change_lead %}
|
{% if perms.inventory.change_lead %}
|
||||||
<button class="dropdown-item text-primary"
|
|
||||||
onclick="openActionModal('{{ lead.pk }}', '{{ lead.action }}', '{{ lead.next_action }}', '{{ lead.next_action_date|date:"Y-m-d\TH:i" }}')">
|
|
||||||
{% trans "Update Actions" %}
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not lead.opportunity %}
|
{% if not lead.opportunity %}
|
||||||
{% if perms.inventory.add_opportunity %}
|
{% if perms.inventory.add_opportunity %}
|
||||||
@ -335,69 +331,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fetch("{% url 'update_lead_actions' request.dealer.slug %}", {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData,
|
|
||||||
headers: {
|
|
||||||
'X-CSRFToken': '{{ csrf_token }}'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
Swal.close();
|
|
||||||
if (data.success) {
|
|
||||||
// Success notification
|
|
||||||
Swal.fire({
|
|
||||||
toast: true,
|
|
||||||
icon: 'success',
|
|
||||||
position: "top-end",
|
|
||||||
text: data.message || 'Actions updated successfully',
|
|
||||||
showConfirmButton: false,
|
|
||||||
timer: 2000,
|
|
||||||
timerProgressBar: false,
|
|
||||||
didOpen: (toast) => {
|
|
||||||
toast.onmouseenter = Swal.stopTimer;
|
|
||||||
toast.onmouseleave = Swal.resumeTimer;
|
|
||||||
}
|
|
||||||
}).then(() => {
|
|
||||||
location.reload(); // Refresh after user clicks OK
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Error notification
|
|
||||||
Swal.fire({
|
|
||||||
toast: true,
|
|
||||||
icon: 'error',
|
|
||||||
position: "top-end",
|
|
||||||
text: data.message || 'Failed to update actions',
|
|
||||||
showConfirmButton: false,
|
|
||||||
timer: 2000,
|
|
||||||
timerProgressBar: false,
|
|
||||||
didOpen: (toast) => {
|
|
||||||
toast.onmouseenter = Swal.stopTimer;
|
|
||||||
toast.onmouseleave = Swal.resumeTimer;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
Swal.close();
|
|
||||||
console.error('Error:', error);
|
|
||||||
Swal.fire({
|
|
||||||
toast: true,
|
|
||||||
icon: 'error',
|
|
||||||
position: "top-end",
|
|
||||||
text: 'An unexpected error occurred',
|
|
||||||
showConfirmButton: false,
|
|
||||||
timer: 2000,
|
|
||||||
timerProgressBar: false,
|
|
||||||
didOpen: (toast) => {
|
|
||||||
toast.onmouseenter = Swal.stopTimer;
|
|
||||||
toast.onmouseleave = Swal.resumeTimer;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Helper function for notifications
|
// Helper function for notifications
|
||||||
function notify(tag, msg) {
|
function notify(tag, msg) {
|
||||||
Toast.fire({
|
Toast.fire({
|
||||||
|
|||||||
@ -12,7 +12,15 @@
|
|||||||
data-bs-dismiss="modal"
|
data-bs-dismiss="modal"
|
||||||
aria-label="Close"></button>
|
aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<form id="actionTrackingForm" method="post">
|
<form id="actionTrackingForm"
|
||||||
|
action="{% url 'update_lead_actions' lead.dealer.slug %}"
|
||||||
|
hx-select-oob="#currentStage:outerHTML,#leadStatus:outerHTML,#toast-container:outerHTML"
|
||||||
|
hx-swap="none"
|
||||||
|
hx-on::after-request="{
|
||||||
|
resetSubmitButton(document.querySelector('#actionTrackingForm button[type=submit]'));
|
||||||
|
$('#actionTrackingModal').modal('hide');
|
||||||
|
}"
|
||||||
|
method="post">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" id="leadId" name="lead_id">
|
<input type="hidden" id="leadId" name="lead_id">
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<nav class="navbar navbar-vertical navbar-expand-lg ">
|
<nav class="navbar navbar-vertical navbar-expand-lg ">
|
||||||
<div class="collapse navbar-collapse" id="navbarVerticalCollapse">
|
<div class="collapse navbar-collapse" id="navbarVerticalCollapse">
|
||||||
<div class="navbar-vertical-content d-flex flex-column">
|
<div class="navbar-vertical-content d-flex flex-column">
|
||||||
<ul class="navbar-nav flex-column" id="navbarVerticalNav" hx-boost="true" hx-target="#main_content" hx-select="#main_content" hx-swap="outerHTML" hx-select-oob="#toast-container">
|
<ul class="navbar-nav flex-column" id="navbarVerticalNav" hx-boost="true" hx-target="#main_content" hx-select="#main_content" hx-swap="outerHTML" hx-select-oob="#toast-container" hx-indicator="#spinner">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<p class="navbar-vertical-label text-primary fs-8 text-truncate">{{request.dealer|default:"Apps"}}</p>
|
<p class="navbar-vertical-label text-primary fs-8 text-truncate">{{request.dealer|default:"Apps"}}</p>
|
||||||
<hr class="navbar-vertical-line" />
|
<hr class="navbar-vertical-line" />
|
||||||
|
|||||||
@ -34,7 +34,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<!---->
|
<!---->
|
||||||
<div class="row justify-content-center mt-5 mb-3
|
<div class="row justify-content-center mt-5 mb-3
|
||||||
{% if not vendor_exists %}disabled{% endif %}">
|
{% if not vendor_exists %}disabled{% endif %}" hx-boost="false">
|
||||||
<div class="col-lg-8 col-md-10">
|
<div class="col-lg-8 col-md-10">
|
||||||
<div class="card shadow-sm border-0 rounded-3">
|
<div class="card shadow-sm border-0 rounded-3">
|
||||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||||
|
|||||||
@ -43,6 +43,10 @@
|
|||||||
let deleteMessage = this.getAttribute("data-message");
|
let deleteMessage = this.getAttribute("data-message");
|
||||||
|
|
||||||
confirmDeleteBtn.setAttribute("href", deleteUrl);
|
confirmDeleteBtn.setAttribute("href", deleteUrl);
|
||||||
|
confirmDeleteBtn.setAttribute("hx-boost", "true");
|
||||||
|
confirmDeleteBtn.setAttribute("hx-select-oob", "#notesTable:outerHTML,#toast-container:outerHTML");
|
||||||
|
confirmDeleteBtn.setAttribute("hx-swap", "none");
|
||||||
|
confirmDeleteBtn.setAttribute("hx-on::after-request", "$('#deleteModal').modal('hide');");
|
||||||
deleteModalMessage.innerHTML = deleteMessage;
|
deleteModalMessage.innerHTML = deleteMessage;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user