519 lines
No EOL
20 KiB
HTML
519 lines
No EOL
20 KiB
HTML
{% extends '_base.html' %}
|
|
{% load static %}
|
|
|
|
{% block sidebar %}
|
|
{% include 'sidebars/admin.html' %}
|
|
{% endblock sidebar %}
|
|
|
|
{% block navbar %}
|
|
{% include 'navbars/admin.html' %}
|
|
{% endblock navbar %}
|
|
|
|
|
|
{% block title %}کاربرها{% endblock %}
|
|
|
|
{% block style %}
|
|
<!-- DataTables CSS -->
|
|
<link rel="stylesheet" href="{% static 'assets/vendor/libs/datatables-bs5/datatables.bootstrap5.css' %}">
|
|
<link rel="stylesheet" href="{% static 'assets/vendor/libs/datatables-responsive-bs5/responsive.bootstrap5.css' %}">
|
|
<link rel="stylesheet" href="{% static 'assets/vendor/libs/datatables-buttons-bs5/buttons.bootstrap5.css' %}">
|
|
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- Include Toasts -->
|
|
{% include '_toasts.html' %}
|
|
|
|
|
|
<div class="container-xxl flex-grow-1 container-p-y">
|
|
|
|
<div class="row py-3 mb-4 card-header flex-column flex-md-row pb-0">
|
|
<div class="d-md-flex justify-content-between align-items-center dt-layout-start col-md-auto me-auto mt-0">
|
|
<h5 class="card-title mb-0 text-md-start text-center fw-bold">لیست کاربرها</h5>
|
|
</div>
|
|
<div class="d-md-flex justify-content-between align-items-center dt-layout-end col-md-auto ms-auto mt-0">
|
|
<div class="dt-buttons btn-group flex-wrap mb-0">
|
|
<div class="btn-group">
|
|
<button class="btn buttons-collection btn-label-primary dropdown-toggle me-4" tabindex="0" aria-controls="DataTables_Table_0" type="button" aria-haspopup="dialog" aria-expanded="false">
|
|
<span>
|
|
<span class="d-flex align-items-center gap-2">
|
|
<i class="icon-base bx bx-export me-sm-1">
|
|
</i>
|
|
<span class="d-none d-sm-inline-block">
|
|
خروجی
|
|
</span>
|
|
</span>
|
|
</span>
|
|
</button>
|
|
<button class="btn btn-primary " onclick="prepareAddForm()">
|
|
<i class="bx bx-plus me-1"></i>
|
|
افزودن کاربر جدید
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-datatable table-responsive">
|
|
<table class="datatables-basic table border-top" id="customers-table">
|
|
<thead>
|
|
<tr>
|
|
<th>ردیف</th>
|
|
<th>کاربر</th>
|
|
<th>کد ملی</th>
|
|
<th>تلفن</th>
|
|
<th>آدرس</th>
|
|
<th>بانک</th>
|
|
<th>وضعیت</th>
|
|
<th>عملیات</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for customer in customers %}
|
|
<tr>
|
|
<td>{{forloop.counter}}</td>
|
|
<td>
|
|
<div class="d-flex justify-content-start align-items-center user-name">
|
|
<div class="avatar-wrapper">
|
|
<div class="avatar me-2">
|
|
{% if customer.pic and customer.pic.url %}
|
|
<img src="{{ customer.pic.url }}" alt="Avatar" class="rounded-circle" style="width: 40px; height: 40px; object-fit: cover;">
|
|
{% else %}
|
|
<span class="avatar-initial rounded-circle bg-label-primary">
|
|
{% if customer.user.first_name and customer.user.last_name %}
|
|
{{ customer.user.first_name|first|upper }}{{ customer.user.last_name|first|upper }}
|
|
{% elif customer.user.first_name %}
|
|
{{ customer.user.first_name|first|upper }}
|
|
{% elif customer.user.last_name %}
|
|
{{ customer.user.last_name|first|upper }}
|
|
{% else %}
|
|
{{ customer.user.username|first|upper }}
|
|
{% endif %}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="d-flex flex-column">
|
|
<span class="emp_name text-truncate text-heading">{{ customer.user.get_full_name|default:customer.user.username }}</span>
|
|
<small class="emp_post text-truncate">{{ customer.user.username }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>{{ customer.national_code|default:"کد ملی ثبت نشده" }}</td>
|
|
<td>
|
|
<div class="d-flex flex-column">
|
|
{% if customer.phone_number_1 %}
|
|
<span class="fw-medium">{{ customer.phone_number_1 }}</span>
|
|
{% endif %}
|
|
{% if customer.phone_number_2 %}
|
|
<small class="text-muted">{{ customer.phone_number_2 }}</small>
|
|
{% endif %}
|
|
{% if not customer.phone_number_1 and not customer.phone_number_2 %}
|
|
<span class="text-muted">تلفن ثبت نشده</span>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
<td>
|
|
{% if customer.address %}
|
|
<span class="text-truncate d-inline-block" style="max-width: 200px;" title="{{ customer.address }}">
|
|
{{ customer.address|truncatechars:50 }}
|
|
</span>
|
|
{% else %}
|
|
<span class="text-muted">آدرس ثبت نشده</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<div class="d-flex flex-column">
|
|
{% if customer.bank_name %}
|
|
<span class="fw-medium">{{ customer.get_bank_name_display }}</span>
|
|
<span class="text-muted">{{ customer.card_number }}</span>
|
|
<span class="text-muted">{{ customer.account_number }}</span>
|
|
{% else %}
|
|
<span class="text-muted">بانک ثبت نشده</span>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
<td>
|
|
{% if customer.is_completed %}
|
|
<span class="badge bg-label-success">تکمیل شده</span>
|
|
{% else %}
|
|
<span class="badge bg-label-warning">ناقص</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<div class="d-inline-block">
|
|
<a href="javascript:;" class="btn btn-icon dropdown-toggle hide-arrow" data-bs-toggle="dropdown">
|
|
<i class="icon-base bx bx-dots-vertical-rounded"></i>
|
|
</a>
|
|
<ul class="dropdown-menu dropdown-menu-end m-0">
|
|
<li>
|
|
<a href="#" class="dropdown-item" data-customer-id="{{ customer.id }}" onclick="viewCustomer(this.getAttribute('data-customer-id'))">
|
|
<i class="bx bx-show me-1"></i>مشاهده جزئیات
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="#" class="dropdown-item" data-customer-id="{{ customer.id }}" onclick="editCustomer(this.getAttribute('data-customer-id'))">
|
|
<i class="bx bx-edit me-1"></i>ویرایش
|
|
</a>
|
|
</li>
|
|
<div class="dropdown-divider"></div>
|
|
<li>
|
|
<a href="#" class="dropdown-item text-danger" data-customer-id="{{ customer.id }}" data-customer-name="{{ customer.user.get_full_name|default:customer.user.username }}" onclick="deleteCustomer(this.getAttribute('data-customer-id'), this.getAttribute('data-customer-name'))">
|
|
<i class="bx bx-trash me-1"></i>حذف
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<a href="#" class="btn btn-icon item-edit" data-customer-id="{{ customer.id }}" onclick="editCustomer(this.getAttribute('data-customer-id'))">
|
|
<i class="icon-base bx bx-edit icon-sm"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td class="text-center py-4">
|
|
<div class="d-flex flex-column align-items-center">
|
|
<i class="bx bx-user-x bx-lg text-muted mb-2"></i>
|
|
<span class="text-muted">هیچ کاربری یافت نشد</span>
|
|
</div>
|
|
</td>
|
|
<td></td>
|
|
<td></td>
|
|
<td></td>
|
|
<td></td>
|
|
<td></td>
|
|
<td></td>
|
|
<td></td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal to add/edit record -->
|
|
<div class="offcanvas offcanvas-end" id="add-new-record">
|
|
<div class="offcanvas-header border-bottom">
|
|
<h5 class="offcanvas-title" id="exampleModalLabel">افزودن کاربر جدید</h5>
|
|
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
|
</div>
|
|
<div class="offcanvas-body flex-grow-1">
|
|
<form class="add-new-record pt-0 row g-2" id="form-add-new-record" method="post" enctype="multipart/form-data">
|
|
{% csrf_token %}
|
|
<input type="hidden" id="customer-id" name="customer_id" value="">
|
|
|
|
<!-- User Information -->
|
|
|
|
<div class="col-sm-6">
|
|
<label class="form-label fw-bold" for="{{ form.first_name.id_for_label }}">{{ form.first_name.label }}</label>
|
|
<div class="input-group input-group-merge">
|
|
<span class="input-group-text"><i class="bx bx-user"></i></span>
|
|
{{ form.first_name }}
|
|
</div>
|
|
{% if form.first_name.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.first_name.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-sm-6">
|
|
<label class="form-label fw-bold" for="{{ form.last_name.id_for_label }}">{{ form.last_name.label }}</label>
|
|
<div class="input-group input-group-merge">
|
|
<span class="input-group-text"><i class="bx bx-user"></i></span>
|
|
{{ form.last_name }}
|
|
</div>
|
|
{% if form.last_name.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.last_name.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-sm-6">
|
|
<label class="form-label fw-bold" for="{{ form.phone_number_1.id_for_label }}">{{ form.phone_number_1.label }}</label>
|
|
<div class="input-group input-group-merge">
|
|
<span class="input-group-text"><i class="bx bx-phone"></i></span>
|
|
{{ form.phone_number_1 }}
|
|
</div>
|
|
{% if form.phone_number_1.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.phone_number_1.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-sm-6">
|
|
<label class="form-label fw-bold" for="{{ form.phone_number_2.id_for_label }}">{{ form.phone_number_2.label }}</label>
|
|
<div class="input-group input-group-merge">
|
|
<span class="input-group-text"><i class="bx bx-phone"></i></span>
|
|
{{ form.phone_number_2 }}
|
|
</div>
|
|
{% if form.phone_number_2.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.phone_number_2.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-sm-12">
|
|
<label class="form-label fw-bold" for="{{ form.national_code.id_for_label }}">{{ form.national_code.label }}</label>
|
|
<div class="input-group input-group-merge">
|
|
<span class="input-group-text"><i class="bx bx-id-card"></i></span>
|
|
{{ form.national_code }}
|
|
</div>
|
|
{% if form.national_code.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.national_code.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-sm-12">
|
|
<label class="form-label fw-bold" for="{{ form.bank_name.id_for_label }}">{{ form.bank_name.label }}</label>
|
|
<div class="input-group input-group-merge">
|
|
<span class="input-group-text"><i class="bx bx-credit-card"></i></span>
|
|
{{ form.bank_name }}
|
|
</div>
|
|
{% if form.bank_name.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.bank_name.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-sm-12">
|
|
<label class="form-label fw-bold" for="{{ form.card_number.id_for_label }}">{{ form.card_number.label }}</label>
|
|
<div class="input-group input-group-merge">
|
|
<span class="input-group-text"><i class="bx bx-credit-card"></i></span>
|
|
{{ form.card_number }}
|
|
</div>
|
|
{% if form.card_number.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.card_number.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-sm-12">
|
|
<label class="form-label fw-bold" for="{{ form.account_number.id_for_label }}">{{ form.account_number.label }}</label>
|
|
<div class="input-group input-group-merge">
|
|
<span class="input-group-text"><i class="bx bx-credit-card"></i></span>
|
|
{{ form.account_number }}
|
|
</div>
|
|
{% if form.account_number.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.account_number.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-sm-12">
|
|
<label class="form-label fw-bold" for="{{ form.address.id_for_label }}">{{ form.address.label }}</label>
|
|
<div class="input-group input-group-merge">
|
|
<span class="input-group-text"><i class="bx bx-map"></i></span>
|
|
{{ form.address }}
|
|
</div>
|
|
{% if form.address.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.address.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-sm-12">
|
|
<button type="submit" class="btn btn-primary data-submit me-sm-3 me-1">ذخیره</button>
|
|
<button type="reset" class="btn btn-outline-secondary" data-bs-dismiss="offcanvas">انصراف</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delete Confirmation Modal -->
|
|
<div class="modal fade" id="deleteConfirmModal" tabindex="-1" aria-labelledby="deleteConfirmModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="deleteConfirmModalLabel">تایید حذف</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p id="deleteConfirmText">آیا از حذف این کاربر اطمینان دارید؟</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">انصراف</button>
|
|
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">حذف</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %}
|
|
|
|
{% block script %}
|
|
<!-- DataTables JS -->
|
|
<script src="{% static 'assets/vendor/libs/datatables-bs5/datatables-bootstrap5.js' %}"></script>
|
|
<!-- Persian DataTable Language -->
|
|
<script src="{% static 'assets/js/persian-datatable.js' %}"></script>
|
|
|
|
<script>
|
|
$(document).ready(function() {
|
|
// Initialize DataTable with Persian language
|
|
$('#customers-table').DataTable({
|
|
pageLength: 10,
|
|
lengthMenu: [[10, 25, 50, -1], [10, 25, 50, "همه"]],
|
|
order: [[0, 'asc']],
|
|
responsive: true,
|
|
});
|
|
|
|
// Handle form submission
|
|
$('#form-add-new-record').on('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const form = this;
|
|
const formData = new FormData(form);
|
|
const customerId = $('#customer-id').val();
|
|
|
|
// Determine URL based on whether we're editing or adding
|
|
const url = customerId ? '{% url "accounts:edit_customer_ajax" 0 %}'.replace('0', customerId) : '{% url "accounts:add_customer_ajax" %}';
|
|
|
|
// Show loading state
|
|
const submitBtn = $(form).find('button[type="submit"]');
|
|
const originalText = submitBtn.text();
|
|
submitBtn.prop('disabled', true).text('در حال ذخیره...');
|
|
|
|
$.ajax({
|
|
url: url,
|
|
type: 'POST',
|
|
data: formData,
|
|
processData: false,
|
|
contentType: false,
|
|
success: function(response) {
|
|
if (response.success) {
|
|
// Show success message
|
|
showToast(response.message, 'success');
|
|
|
|
// Close offcanvas and reset form
|
|
$('#add-new-record').offcanvas('hide');
|
|
form.reset();
|
|
|
|
// Reload page to show new customer
|
|
setTimeout(function() {
|
|
location.reload();
|
|
}, 1500);
|
|
} else {
|
|
// Show error message
|
|
showToast(response.message, 'danger');
|
|
|
|
// Show form errors if any
|
|
if (response.errors) {
|
|
Object.keys(response.errors).forEach(function(field) {
|
|
const errorMsg = response.errors[field][0];
|
|
const fieldElement = $('[name="' + field + '"]');
|
|
fieldElement.addClass('is-invalid');
|
|
fieldElement.siblings('.invalid-feedback').remove();
|
|
fieldElement.after('<div class="invalid-feedback d-block">' + errorMsg + '</div>');
|
|
});
|
|
}
|
|
}
|
|
},
|
|
error: function(xhr, status, error) {
|
|
// Show error message
|
|
showToast('خطا در ارتباط با سرور', 'danger');
|
|
},
|
|
complete: function() {
|
|
// Reset button state
|
|
submitBtn.prop('disabled', false).text(originalText);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Reset form when offcanvas is hidden
|
|
$('#add-new-record').on('hidden.bs.offcanvas', function() {
|
|
const form = $('#form-add-new-record')[0];
|
|
form.reset();
|
|
|
|
// Reset form for adding new customer
|
|
$('#customer-id').val('');
|
|
$('#exampleModalLabel').text('افزودن کاربر جدید');
|
|
$('.data-submit').text('ذخیره');
|
|
|
|
// Clear validation errors
|
|
$('.is-invalid').removeClass('is-invalid');
|
|
$('.invalid-feedback').remove();
|
|
});
|
|
|
|
// Clear validation errors when user starts typing
|
|
$('input, textarea').on('input', function() {
|
|
$(this).removeClass('is-invalid');
|
|
$(this).siblings('.invalid-feedback').remove();
|
|
});
|
|
});
|
|
|
|
// Customer functions
|
|
function viewCustomer(id) {
|
|
// Implement view functionality
|
|
console.log('View customer:', id);
|
|
}
|
|
|
|
function editCustomer(id) {
|
|
// Load customer data and open edit form
|
|
$.ajax({
|
|
url: '{% url "accounts:get_customer_data" 0 %}'.replace('0', id),
|
|
type: 'GET',
|
|
success: function(response) {
|
|
if (response.success) {
|
|
const customer = response.customer;
|
|
|
|
// Fill form with customer data
|
|
const fieldsMap = {
|
|
'customer-id': customer.id,
|
|
'id_first_name': customer.first_name,
|
|
'id_last_name': customer.last_name,
|
|
'id_phone_number_1': customer.phone_number_1,
|
|
'id_phone_number_2': customer.phone_number_2,
|
|
'id_national_code': customer.national_code,
|
|
'id_card_number': customer.card_number,
|
|
'id_account_number': customer.account_number,
|
|
'id_address': customer.address
|
|
};
|
|
|
|
// Loop through fields for easier maintenance
|
|
Object.keys(fieldsMap).forEach(function(fieldId) {
|
|
$('#' + fieldId).val(fieldsMap[fieldId] || '');
|
|
});
|
|
|
|
// Update modal title and button
|
|
$('#exampleModalLabel').text('ویرایش کاربر');
|
|
$('.data-submit').text('ویرایش');
|
|
|
|
// Open modal
|
|
$('#add-new-record').offcanvas('show');
|
|
} else {
|
|
showToast('خطا در بارگذاری اطلاعات کاربر', 'danger');
|
|
}
|
|
},
|
|
error: function() {
|
|
showToast('خطا در ارتباط با سرور', 'danger');
|
|
}
|
|
});
|
|
}
|
|
|
|
function deleteCustomer(id, name) {
|
|
// Set modal content
|
|
document.getElementById('deleteConfirmText').textContent = `آیا از حذف کاربر "${name}" اطمینان دارید؟`;
|
|
|
|
// Show modal
|
|
const modal = new bootstrap.Modal(document.getElementById('deleteConfirmModal'));
|
|
modal.show();
|
|
|
|
// Handle confirm button click
|
|
document.getElementById('confirmDeleteBtn').onclick = function() {
|
|
// Implement delete functionality
|
|
console.log('Delete customer:', id);
|
|
showToast('کاربر با موفقیت حذف شد.', 'success');
|
|
modal.hide();
|
|
};
|
|
}
|
|
|
|
function prepareAddForm() {
|
|
// Reset form for adding new customer
|
|
const form = $('#form-add-new-record')[0];
|
|
form.reset();
|
|
$('#customer-id').val('');
|
|
$('#exampleModalLabel').text('افزودن کاربر جدید');
|
|
$('.data-submit').text('ذخیره');
|
|
|
|
// Clear validation errors
|
|
$('.is-invalid').removeClass('is-invalid');
|
|
$('.invalid-feedback').remove();
|
|
|
|
// Open modal
|
|
$('#add-new-record').offcanvas('show');
|
|
}
|
|
</script>
|
|
{% endblock %} |