clean up proccess and req_list app.

This commit is contained in:
aminhashemi92 2025-09-07 11:06:21 +03:30
parent 35799b7754
commit 6f3ce51ab9
26 changed files with 287 additions and 744 deletions

View file

@ -28,17 +28,113 @@
<div class="container-xxl flex-grow-1 container-p-y">
<div class="d-flex align-items-center justify-content-between mb-3">
<h4 class="mb-0">درخواست‌ها</h4>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#requestModal">
<i class="bx bx-plus"></i>
درخواست جدید
</button>
<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 d-none" type="button">
<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" data-bs-toggle="modal" data-bs-target="#requestModal">
<i class="bx bx-plus me-1"></i>
درخواست جدید
</button>
</div>
</div>
</div>
</div>
<!-- Summary Cards -->
<div class="row g-4 mb-4">
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>کل درخواست‌ها</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2">{{ total_count }}</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-primary">
<i class="bx bx-list-ul bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>تکمیل‌شده</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2">{{ completed_count }}</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-success">
<i class="bx bx-badge-check bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>در حال انجام</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2">{{ in_progress_count }}</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-info">
<i class="bx bx-loader-circle bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>در انتظار</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2">{{ pending_count }}</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-warning">
<i class="bx bx-time bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="table-responsive">
<table id="requestsTable" class="table table-striped">
<div class="card-datatable table-responsive">
<table id="requests-table" class="datatables-basic table border-top">
<thead>
<tr>
<th>شناسه</th>
@ -46,8 +142,8 @@
<th>مرحله فعلی</th>
<th>شماره اشتراک آب</th>
<th>نماینده</th>
<th>درخواست‌کننده</th>
<th>اولویت</th>
<th>استان</th>
<th>امور</th>
<th>وضعیت</th>
<th>تاریخ ایجاد</th>
<th>عملیات</th>
@ -61,9 +157,9 @@
<td class="text-primary">{{ inst.current_step.name|default:"--" }}</td>
<td>{{ inst.well.water_subscription_number }}</td>
<td>{% if inst.representative %}{{ inst.representative.get_full_name }}{% else %}-{% endif %}</td>
<td>{% if inst.requester %}{{ inst.requester.get_full_name }}{% else %}-{% endif %}</td>
<td>{{ inst.get_priority_display }}</td>
<td>{{ inst.get_status_display }}</td>
<td>{% if inst.well and inst.well.county %}{{ inst.well.county }}{% else %}-{% endif %}</td>
<td>{% if inst.well and inst.well.affairs %}{{ inst.well.affairs }}{% else %}-{% endif %}</td>
<td>{{ inst.get_status_display_with_color|safe }}</td>
<td>{{ inst.jcreated }}</td>
<td>
<div class="d-inline-block">
@ -126,7 +222,7 @@
<div class="col-sm-12">
<label class="form-label">شماره اشتراک آب</label>
<div class="input-group">
<input type="text" class="form-control" id="req_water_sub" placeholder="مثال: 12345" required>
<input type="text" class="form-control" id="req_water_sub" name="water_subscription_number" data-field="water_subscription_number" placeholder="مثال: 12345" required>
<button class="btn btn-outline-secondary" type="button" id="btnLookupWell">
بررسی/افزودن چاه
</button>
@ -217,7 +313,7 @@
<div class="col-sm-12">
<label class="form-label">کد ملی نماینده</label>
<div class="input-group">
<input type="text" class="form-control" id="rep_national_code" placeholder="مثال: 0012345678">
<input type="text" class="form-control" id="rep_national_code" data-field="national_code" placeholder="مثال: 0012345678" maxlength="10" inputmode="numeric" pattern="\d*">
<button class="btn btn-outline-secondary" type="button" id="btnLookupRep">
بررسی/افزودن نماینده
</button>
@ -268,7 +364,7 @@
<hr class="mt-3 border border-dashed">
<div class="col-sm-12">
<label class="form-label">توضیحات</label>
<textarea class="form-control" rows="3" id="req_description"></textarea>
<textarea class="form-control" rows="3" id="req_description" name="description"></textarea>
</div>
</div>
</form>
@ -361,19 +457,13 @@
$(function() {
// if ($.fn.DataTable) {
// try {
// $('#requestsTable').DataTable({
// pageLength: 10,
// order: [[0, 'desc']]
// });
// } catch (e) {
// console.error('DataTable init failed', e);
// }
// } else {
// console.warn('DataTables library not loaded');
// }
// Initialize DataTable similar to customer_list
$('#requests-table').DataTable({
pageLength: 10,
lengthMenu: [[10, 25, 50, -1], [10, 25, 50, "همه"]],
order: [[0, 'desc']],
responsive: true,
});
let currentWellId = null;
let currentRepId = null;
let wellChecked = false;
@ -399,7 +489,7 @@
if (!$el.length) return false;
$el.addClass('is-invalid');
const $feedback = $('<div class="invalid-feedback inline-error"></div>').text(message);
const $grp = $el.closest('.input-group');
const $grp = $el.closest('.input-group, .form-group, .mb-3');
if ($grp.length) {
$feedback.insertAfter($grp);
} else {
@ -408,60 +498,49 @@
return true;
}
function mapWellFieldToSelector(field) {
switch (field) {
case 'water_subscription_number': return '#req_water_sub';
case 'electricity_subscription_number': return '#id_electricity_subscription_number';
case 'water_meter_serial_number': return '#id_water_meter_serial_number';
case 'water_meter_old_serial_number': return '#id_water_meter_old_serial_number';
case 'water_meter_manufacturer': return '#id_water_meter_manufacturer';
case 'new_manufacturer': return '#id_new_manufacturer';
case 'utm_x': return '#id_utm_x';
case 'utm_y': return '#id_utm_y';
case 'utm_zone': return '#id_utm_zone';
case 'utm_hemisphere': return '#id_utm_hemisphere';
case 'well_power': return '#id_well_power';
case 'reference_letter_number': return '#id_reference_letter_number';
case 'reference_letter_date': return '#id_reference_letter_date';
case 'representative_letter_file': return '#id_representative_letter_file';
case 'representative': return '#rep_national_code';
default: return '#id_' + field;
}
}
// Generic field resolution with small exception map
const exceptionMap = {
water_subscription_number: '#req_water_sub',
national_code: '#rep_national_code',
representative: '#rep_national_code'
};
function mapCustomerFieldToSelector(field) {
switch (field) {
case 'national_code': return $('#id_national_code').length ? '#id_national_code' : '#rep_national_code';
case 'first_name': return '#id_first_name';
case 'last_name': return '#id_last_name';
case 'phone_number_1': return '#id_phone_number_1';
case 'phone_number_2': return '#id_phone_number_2';
case 'card_number': return '#id_card_number';
case 'account_number': return '#id_account_number';
case 'bank_name': return '#id_bank_name';
case 'address': return '#id_address';
default: return '#id_' + field;
}
function findFieldSelector(field, context) {
const $ctx = context ? $(context) : $('#requestModal');
let $el = $ctx.find(`#id_${field}`).first();
if ($el.length) return $el;
$el = $ctx.find(`[name="${field}"]`).first();
if ($el.length) return $el;
$el = $ctx.find(`[data-field="${field}"]`).first();
if ($el.length) return $el;
const ex = exceptionMap[field];
return ex ? $(ex) : $();
}
function showInlineErrors(errors) {
if (!errors) return;
let nonFieldWell = '';
let nonFieldCustomer = '';
// Request-level errors (e.g., process)
if (errors.request) {
for (const key in errors.request) {
const msgs = Array.isArray(errors.request[key]) ? errors.request[key] : [errors.request[key]];
if (key === '__all__' || key === 'non_field_errors') { continue; }
applyErrorTo(findFieldSelector(key, '#requestForm'), msgs[0]);
}
}
if (errors.well) {
for (const key in errors.well) {
const msgs = Array.isArray(errors.well[key]) ? errors.well[key] : [errors.well[key]];
if (key === '__all__' || key === 'non_field_errors') { nonFieldWell = msgs.join('، '); continue; }
const sel = mapWellFieldToSelector(key);
applyErrorTo(sel, msgs[0]);
applyErrorTo(findFieldSelector(key, '#wellFormBlock'), msgs[0]);
}
}
if (errors.customer) {
for (const key in errors.customer) {
const msgs = Array.isArray(errors.customer[key]) ? errors.customer[key] : [errors.customer[key]];
if (key === '__all__' || key === 'non_field_errors') { nonFieldCustomer = msgs.join('، '); continue; }
const sel = mapCustomerFieldToSelector(key);
applyErrorTo(sel, msgs[0]);
applyErrorTo(findFieldSelector(key, '#repNewFields'), msgs[0]);
}
}
if (nonFieldWell) setStatus('#wellStatus', nonFieldWell, 'danger');
@ -536,7 +615,7 @@
$('#remove-file').val('false');
// Initialize Persian Date Picker after well form is shown
setTimeout(initPersianDatePicker, 100);
setStatus('#wellStatus', 'چاه یافت نشد. با ذخیره، ایجاد خواهد شد.', 'danger');
setStatus('#wellStatus', 'چاه یافت نشد. اطلاعات چاه را وارد کنید.', 'danger');
}
})
.fail(function(){ setStatus('#wellStatus', 'خطا در بررسی چاه', 'danger'); });
@ -594,46 +673,26 @@
});
$('#btnSaveRequest').on('click', function(){
const formData = new FormData();
formData.append('csrfmiddlewaretoken', $('input[name=csrfmiddlewaretoken]').val());
formData.append('process', $('#req_process').val());
formData.append('description', $('#req_description').val());
formData.append('water_subscription_number', $('#req_water_sub').val().trim());
clearInlineErrors();
// Use form's native FormData - much cleaner!
const formData = new FormData(document.getElementById('requestForm'));
// Add custom fields that aren't in the form
if (currentWellId) formData.append('well_id', currentWellId);
if (currentRepId) formData.append('representative_id', currentRepId);
// Send fields using CustomerForm names if visible
const ncField = $('#id_national_code').length ? $('#id_national_code').val() : '';
formData.append('national_code', (ncField || $('#rep_national_code').val().trim()));
formData.append('first_name', $('#id_first_name').val() || '');
formData.append('last_name', $('#id_last_name').val() || '');
formData.append('phone_number_1', $('#id_phone_number_1').val() || '');
formData.append('phone_number_2', $('#id_phone_number_2').val() || '');
formData.append('card_number', $('#id_card_number').val() || '');
formData.append('account_number', $('#id_account_number').val() || '');
formData.append('address', $('#id_address').val() || '');
formData.append('bank_name', $('#id_bank_name').val() || '');
// Include WellForm fields so edits are saved
if ($('#wellFormBlock').is(':visible')) {
formData.append('electricity_subscription_number', $('#id_electricity_subscription_number').val() || '');
formData.append('water_meter_serial_number', $('#id_water_meter_serial_number').val() || '');
formData.append('water_meter_old_serial_number', $('#id_water_meter_old_serial_number').val() || '');
formData.append('water_meter_manufacturer', $('#id_water_meter_manufacturer').is(':visible') ? ($('#id_water_meter_manufacturer').val() || '') : '');
formData.append('new_manufacturer', $('#id_new_manufacturer').is(':visible') ? ($('#id_new_manufacturer').val() || '') : '');
formData.append('utm_x', $('#id_utm_x').val() || '');
formData.append('utm_y', $('#id_utm_y').val() || '');
formData.append('utm_zone', $('#id_utm_zone').val() || '');
formData.append('utm_hemisphere', $('#id_utm_hemisphere').val() || '');
formData.append('well_power', $('#id_well_power').val() || '');
formData.append('reference_letter_number', $('#id_reference_letter_number').val() || '');
// Use gregorian date if available, otherwise use the field value
const gregorianDate = $('#id_reference_letter_date').attr('data-gregorian');
formData.append('reference_letter_date', gregorianDate || $('#id_reference_letter_date').val() || '');
// Remove flag
formData.append('remove_file', $('#remove-file').val() || 'false');
const repFile = document.getElementById('id_representative_letter_file');
if (repFile && repFile.files && repFile.files[0]) {
formData.append('representative_letter_file', repFile.files[0]);
}
// Handle special national_code logic (prefer visible field)
const ncField = $('#id_national_code').val();
if (ncField) {
formData.set('national_code', ncField);
} else {
formData.set('national_code', $('#rep_national_code').val().trim());
}
// Handle Persian date conversion
const gregorianDate = $('#id_reference_letter_date').attr('data-gregorian');
if (gregorianDate) {
formData.set('reference_letter_date', gregorianDate);
}
const $btn = $(this).prop('disabled', true).text('در حال ذخیره...');
@ -652,6 +711,10 @@
setTimeout(function(){ location.reload(); }, 1200);
}
} else {
clearInlineErrors();
if (resp.errors) {
showInlineErrors(resp.errors);
}
const msg = buildErrorMessage(resp);
showToast(msg, 'danger');
}
@ -659,6 +722,10 @@
let msg = 'خطا در ذخیره';
try {
const resp = JSON.parse(xhr.responseText);
clearInlineErrors();
if (resp && resp.errors) {
showInlineErrors(resp.errors);
}
msg = buildErrorMessage(resp) || msg;
} catch(e) {}
showToast(msg, 'danger');
@ -715,6 +782,14 @@
}
});
// Enforce digit-only and max length for national code input
$('#rep_national_code').on('input', function() {
const cleaned = (this.value || '').replace(/\D/g, '').slice(0, 10);
if (this.value !== cleaned) {
this.value = cleaned;
}
});
$('#requestModal').on('hidden.bs.modal', function(){
$('#requestForm')[0].reset();
$('#wellFormBlock').hide();