340 lines
15 KiB
HTML
340 lines
15 KiB
HTML
{% extends "base.html" %}
|
|
{% load i18n static crispy_forms_tags %}
|
|
|
|
{% block title %}{% if form.instance.pk %}{% trans "Edit Package" %}{% else %}{% trans "New Package" %}{% endif %} - Tenhal{% endblock %}
|
|
|
|
{% block css %}
|
|
<link href="{% static 'plugins/select2/css/select2.min.css' %}" rel="stylesheet" />
|
|
<style>
|
|
.form-control, .form-select {
|
|
border-radius: 0.375rem;
|
|
border: 1px solid #dee2e6;
|
|
padding: 0.5rem 0.75rem;
|
|
}
|
|
.form-control:focus, .form-select:focus {
|
|
border-color: #007bff;
|
|
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
|
}
|
|
.card {
|
|
border-radius: 0.5rem;
|
|
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
|
}
|
|
.card-header {
|
|
border-radius: 0.5rem 0.5rem 0 0 !important;
|
|
}
|
|
.form-check-input {
|
|
margin-top: 0.3rem;
|
|
}
|
|
.select2-container--default .select2-selection--single {
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 0.375rem;
|
|
min-height: 38px;
|
|
}
|
|
.select2-container--default.select2-container--focus .select2-selection--single {
|
|
border-color: #007bff;
|
|
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
|
}
|
|
.service-row {
|
|
background-color: #f8f9fa;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 0.375rem;
|
|
padding: 1rem;
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
.service-row:hover {
|
|
background-color: #e9ecef;
|
|
}
|
|
|
|
.btn-remove-service {
|
|
padding: 0.25rem 0.5rem;
|
|
font-size: 0.875rem;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<div>
|
|
<h1 class="page-header mb-0">
|
|
<i class="fas fa-box me-2"></i>
|
|
{% if form.instance.pk %}{% trans "Edit Package" %}{% else %}{% trans "New Package" %}{% endif %}
|
|
</h1>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">{% trans "Dashboard" %}</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'finance:package_list' %}">{% trans "Packages" %}</a></li>
|
|
<li class="breadcrumb-item active">{% if form.instance.pk %}{% trans "Edit" %}{% else %}{% trans "New" %}{% endif %}</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="post" id="packageForm">
|
|
{% csrf_token %}
|
|
<div class="row">
|
|
<div class="col-lg-8">
|
|
<div class="card mb-3">
|
|
<div class="card-header bg-primary text-white">
|
|
<h5 class="mb-0"><i class="fas fa-info-circle me-2"></i>{% trans "Package Information" %}</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">{% trans "Name (English)" %} <span class="text-danger">*</span></label>
|
|
{{ form.name_en }}
|
|
{% if form.name_en.errors %}
|
|
<div class="text-danger small">{{ form.name_en.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">{% trans "Name (Arabic)" %}</label>
|
|
{{ form.name_ar }}
|
|
{% if form.name_ar.errors %}
|
|
<div class="text-danger small">{{ form.name_ar.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">{% trans "Price" %} <span class="text-danger">*</span></label>
|
|
{{ form.price }}
|
|
{% if form.price.errors %}
|
|
<div class="text-danger small">{{ form.price.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">{% trans "Validity (days)" %} <span class="text-danger">*</span></label>
|
|
{{ form.validity_days }}
|
|
{% if form.validity_days.errors %}
|
|
<div class="text-danger small">{{ form.validity_days.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">{% trans "Description" %}</label>
|
|
{{ form.description }}
|
|
{% if form.description.errors %}
|
|
<div class="text-danger small">{{ form.description.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-check">
|
|
{{ form.is_active }}
|
|
<label class="form-check-label" for="{{ form.is_active.id_for_label }}">
|
|
{% trans "Active" %}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mb-3">
|
|
<div class="card-header bg-success text-white d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0"><i class="fas fa-list me-2"></i>{% trans "Services Included" %}</h5>
|
|
<div>
|
|
<span class="me-2">{% trans "Total Sessions" %}</span>
|
|
<span class="fw-bold fs-16px" id="totalSessionsDisplay">0</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
{{ service_formset.management_form }}
|
|
|
|
<div id="serviceRows">
|
|
{% for service_form in service_formset %}
|
|
<div class="service-row" data-form-index="{{ forloop.counter0 }}">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-7">
|
|
<label class="form-label small">{% trans "Service" %} <span class="text-danger">*</span></label>
|
|
{{ service_form.service }}
|
|
{% if service_form.service.errors %}
|
|
<div class="text-danger small">{{ service_form.service.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label small">{% trans "Sessions" %} <span class="text-danger">*</span></label>
|
|
{{ service_form.sessions }}
|
|
{% if service_form.sessions.errors %}
|
|
<div class="text-danger small">{{ service_form.sessions.errors }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="col-md-2 text-end">
|
|
<label class="form-label small d-block"> </label>
|
|
{% if not service_form.instance.pk or service_formset.can_delete %}
|
|
<button type="button" class="btn btn-outline-danger btn-sm btn-remove-service">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
{% endif %}
|
|
{{ service_form.id }}
|
|
<div style="display: none;">{{ service_form.DELETE }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-outline-success btn-sm mt-2" id="addServiceBtn">
|
|
<i class="fas fa-plus me-1"></i>{% trans "Add Service" %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<button type="submit" class="btn btn-primary btn-lg">
|
|
<i class="fas fa-save me-2"></i>{% trans "Save Package" %}
|
|
</button>
|
|
<a href="{% url 'finance:package_list' %}" class="btn btn-outline-secondary btn-lg">
|
|
<i class="fas fa-times me-2"></i>{% trans "Cancel" %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h6 class="mb-0"><i class="fas fa-question-circle me-2"></i>{% trans "Help" %}</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="small">{% trans "Create service packages for bundled pricing." %}</p>
|
|
<ul class="small">
|
|
<li>{% trans "Set package name and price" %}</li>
|
|
<li>{% trans "Add services and specify sessions for each" %}</li>
|
|
<li>{% trans "Total sessions are calculated automatically" %}</li>
|
|
<li>{% trans "Set validity period" %}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Empty form template for cloning -->
|
|
<script type="text/template" id="emptyFormTemplate">
|
|
<div class="service-row" data-form-index="__prefix__">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-7">
|
|
<label class="form-label small">{% trans "Service" %} <span class="text-danger">*</span></label>
|
|
{{ service_formset.empty_form.service }}
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label small">{% trans "Sessions" %} <span class="text-danger">*</span></label>
|
|
{{ service_formset.empty_form.sessions }}
|
|
</div>
|
|
<div class="col-md-2 text-end">
|
|
<label class="form-label small d-block"> </label>
|
|
<button type="button" class="btn btn-outline-danger btn-sm btn-remove-service">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
{{ service_formset.empty_form.id }}
|
|
<div style="display: none;">{{ service_formset.empty_form.DELETE }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</script>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script src="{% static 'plugins/select2/dist/js/select2.min.js' %}"></script>
|
|
<script>
|
|
$(document).ready(function() {
|
|
let formIndex = {{ service_formset.total_form_count }};
|
|
|
|
// Initialize Select2 for existing service dropdowns
|
|
initializeSelect2();
|
|
|
|
// Calculate total sessions on page load
|
|
calculateTotalSessions();
|
|
|
|
// Add new service row
|
|
$('#addServiceBtn').click(function() {
|
|
// Get the template HTML and replace __prefix__
|
|
let templateHtml = $('#emptyFormTemplate').html();
|
|
templateHtml = templateHtml.replace(/__prefix__/g, formIndex);
|
|
|
|
// Create new row from the modified HTML
|
|
const newRow = $(templateHtml);
|
|
|
|
// Append to service rows
|
|
$('#serviceRows').append(newRow);
|
|
|
|
// Update form count
|
|
const totalForms = $('#id_packageservice_set-TOTAL_FORMS');
|
|
totalForms.val(parseInt(totalForms.val()) + 1);
|
|
|
|
// Initialize Select2 for the new dropdown
|
|
newRow.find('select[name$="-service"]').select2({
|
|
placeholder: '{% trans "Select Service" %}',
|
|
allowClear: true,
|
|
width: '100%'
|
|
});
|
|
|
|
formIndex++;
|
|
|
|
// Recalculate total sessions
|
|
calculateTotalSessions();
|
|
});
|
|
|
|
// Remove service row
|
|
$(document).on('click', '.btn-remove-service', function() {
|
|
const row = $(this).closest('.service-row');
|
|
const deleteCheckbox = row.find('input[name$="-DELETE"]');
|
|
|
|
if (deleteCheckbox.length > 0) {
|
|
// Mark for deletion if it's an existing record
|
|
deleteCheckbox.prop('checked', true);
|
|
row.hide();
|
|
} else {
|
|
// Remove from DOM if it's a new record
|
|
row.remove();
|
|
}
|
|
|
|
// Recalculate total sessions
|
|
calculateTotalSessions();
|
|
});
|
|
|
|
// Recalculate total sessions when session count changes
|
|
$(document).on('input change', 'input[name$="-sessions"]', function() {
|
|
calculateTotalSessions();
|
|
});
|
|
|
|
function initializeSelect2() {
|
|
$('select[name$="-service"]').each(function() {
|
|
if (!$(this).hasClass('select2-hidden-accessible')) {
|
|
$(this).select2({
|
|
placeholder: '{% trans "Select Service" %}',
|
|
allowClear: true,
|
|
width: '100%'
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function calculateTotalSessions() {
|
|
let total = 0;
|
|
|
|
$('.service-row:visible').each(function() {
|
|
const deleteCheckbox = $(this).find('input[name$="-DELETE"]');
|
|
const isMarkedForDeletion = deleteCheckbox.length > 0 && deleteCheckbox.is(':checked');
|
|
|
|
if (!isMarkedForDeletion) {
|
|
const sessionsInput = $(this).find('input[name$="-sessions"]');
|
|
const sessions = parseInt(sessionsInput.val()) || 0;
|
|
total += sessions;
|
|
}
|
|
});
|
|
|
|
$('#totalSessionsDisplay').text(total);
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|