complete first version of main proccess
This commit is contained in:
parent
6ff4740d04
commit
f2fc2362a7
61 changed files with 3280 additions and 28 deletions
|
@ -0,0 +1,159 @@
|
|||
{% extends '_base.html' %}
|
||||
{% load static %}
|
||||
{% load processes_tags %}
|
||||
{% load humanize %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% include 'sidebars/admin.html' %}
|
||||
{% endblock sidebar %}
|
||||
|
||||
{% block navbar %}
|
||||
{% include 'navbars/admin.html' %}
|
||||
{% endblock navbar %}
|
||||
|
||||
{% block title %}{{ step.name }} - درخواست {{ instance.code }}{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
<link rel="stylesheet" href="{% static 'assets/vendor/libs/bs-stepper/bs-stepper.css' %}">
|
||||
|
||||
<!-- Persian Date Picker CSS -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/persian-datepicker@latest/dist/css/persian-datepicker.min.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include '_toasts.html' %}
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="row">
|
||||
<div class="col-12 mb-4">
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<div>
|
||||
<h4 class="mb-1">{{ step.name }}: {{ instance.process.name }}</h4>
|
||||
<small class="text-muted d-block">
|
||||
اشتراک آب: {{ instance.well.water_subscription_number|default:"-" }}
|
||||
| نماینده: {{ instance.representative.profile.national_code|default:"-" }}
|
||||
</small>
|
||||
</div>
|
||||
<a href="{% url 'processes:request_list' %}" class="btn btn-outline-secondary">بازگشت</a>
|
||||
</div>
|
||||
|
||||
<div class="bs-stepper wizard-vertical vertical mt-2">
|
||||
{% stepper_header instance step %}
|
||||
|
||||
<div class="bs-stepper-content">
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">نصاب</label>
|
||||
<select name="installer_id" class="form-select" required>
|
||||
<option value="">انتخاب کنید...</option>
|
||||
{% for p in installers %}
|
||||
<option value="{{ p.user.id }}" {% if assignment.installer and p.user.id == assignment.installer.id %}selected{% endif %}>{{ p.user.get_full_name }} ({{ p.user.username }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">تاریخ مراجعه نصاب</label>
|
||||
<input type="text" id="id_scheduled_date_display" class="form-control" placeholder="انتخاب تاریخ" readonly required value="{% if assignment.scheduled_date %}{{ assignment.scheduled_date|date:'Y/m/d' }}{% endif %}">
|
||||
<input type="hidden" id="id_scheduled_date" name="scheduled_date" value="{% if assignment.scheduled_date %}{{ assignment.scheduled_date|date:'Y-m-d' }}{% endif %}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
{% if previous_step %}
|
||||
<a href="{% url 'processes:step_detail' instance.id previous_step.id %}" class="btn btn-label-secondary">قبلی</a>
|
||||
{% else %}
|
||||
<span></span>
|
||||
{% endif %}
|
||||
<button class="btn btn-primary" type="submit">ثبت و ادامه</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
|
||||
<!-- Persian Date Picker JS -->
|
||||
<script src="https://unpkg.com/persian-date@latest/dist/persian-date.min.js"></script>
|
||||
<script src="https://unpkg.com/persian-datepicker@latest/dist/js/persian-datepicker.min.js"></script>
|
||||
<script>
|
||||
(function(){
|
||||
function convertPersianToEnglishNumbers(str) {
|
||||
const persianNumbers = '۰۱۲۳۴۵۶۷۸۹';
|
||||
const englishNumbers = '0123456789';
|
||||
return String(str || '').split('').map(function(char){
|
||||
const index = persianNumbers.indexOf(char);
|
||||
return index !== -1 ? englishNumbers[index] : char;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function initPersianDatePicker() {
|
||||
if ($.fn.persianDatepicker && $('#id_scheduled_date_display').length) {
|
||||
try {
|
||||
var $display = $('#id_scheduled_date_display');
|
||||
var $hidden = $('#id_scheduled_date');
|
||||
|
||||
// Prefill from hidden Gregorian to visible Jalali
|
||||
var initialGregorian = $hidden.val();
|
||||
if (initialGregorian) {
|
||||
try {
|
||||
var initialJalali = new window.persianDate(new Date(initialGregorian)).format('YYYY/MM/DD');
|
||||
$display.val(initialJalali);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
$display.persianDatepicker({
|
||||
calendarType: 'persian',
|
||||
altField: '#id_scheduled_date',
|
||||
format: 'YYYY/MM/DD',
|
||||
altFormat: 'YYYY-MM-DD',
|
||||
observer: true,
|
||||
autoClose: true,
|
||||
initialValue: false,
|
||||
calendar:{ persian: { leapYearMode: 'astronomical' } },
|
||||
onSelect: function (unixDate) {
|
||||
var g = new window.persianDate(unixDate).toCalendar('gregorian').format('YYYY-MM-DD');
|
||||
g = convertPersianToEnglishNumbers(g);
|
||||
$hidden.val(g);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Error initializing Persian Date Picker:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initPersianDatePicker);
|
||||
})();
|
||||
|
||||
// Require date and show success toast on submit
|
||||
(function(){
|
||||
const form = document.querySelector('form');
|
||||
if (!form) return;
|
||||
form.addEventListener('submit', function(ev){
|
||||
const display = document.getElementById('id_scheduled_date_display');
|
||||
const hidden = document.getElementById('id_scheduled_date');
|
||||
if (!display.value || !hidden.value) {
|
||||
ev.preventDefault(); ev.stopPropagation();
|
||||
if (typeof showToast === 'function') showToast('تاریخ مراجعه نصاب را انتخاب کنید', 'danger');
|
||||
display.scrollIntoView({behavior:'smooth', block:'center'});
|
||||
return false;
|
||||
}
|
||||
try { sessionStorage.setItem('assign_saved', '1'); } catch(_) {}
|
||||
}, false);
|
||||
|
||||
try {
|
||||
if (sessionStorage.getItem('assign_saved') === '1') {
|
||||
sessionStorage.removeItem('assign_saved');
|
||||
if (typeof showToast === 'function') showToast('با موفقیت ثبت شد', 'success');
|
||||
}
|
||||
} catch(_) {}
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -0,0 +1,448 @@
|
|||
{% extends '_base.html' %}
|
||||
{% load static %}
|
||||
{% load processes_tags %}
|
||||
{% load common_tags %}
|
||||
{% load humanize %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% include 'sidebars/admin.html' %}
|
||||
{% endblock sidebar %}
|
||||
|
||||
{% block navbar %}
|
||||
{% include 'navbars/admin.html' %}
|
||||
{% endblock navbar %}
|
||||
|
||||
{% block title %}{{ step.name }} - درخواست {{ instance.code }}{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
<link rel="stylesheet" href="{% static 'assets/vendor/libs/bs-stepper/bs-stepper.css' %}">
|
||||
|
||||
<!-- Persian Date Picker CSS -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/persian-datepicker@latest/dist/css/persian-datepicker.min.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include '_toasts.html' %}
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="row">
|
||||
<div class="col-12 mb-4">
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<div>
|
||||
<h4 class="mb-1">{{ step.name }}: {{ instance.process.name }}</h4>
|
||||
<small class="text-muted d-block">
|
||||
اشتراک آب: {{ instance.well.water_subscription_number|default:"-" }}
|
||||
| نماینده: {{ instance.representative.profile.national_code|default:"-" }}
|
||||
</small>
|
||||
</div>
|
||||
<a href="{% url 'processes:request_list' %}" class="btn btn-outline-secondary">بازگشت</a>
|
||||
</div>
|
||||
|
||||
<div class="bs-stepper wizard-vertical vertical mt-2">
|
||||
{% stepper_header instance step %}
|
||||
|
||||
<div class="bs-stepper-content">
|
||||
|
||||
{% if report and not edit_mode %}
|
||||
<div class="card mb-3 border">
|
||||
<div class="card-header d-flex justify-content-end">
|
||||
<a href="?edit=1" class="btn btn-primary">ویرایش گزارش نصب</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p class="text-nowrap mb-2"><i class="bx bx-calendar-event bx-sm me-2"></i>تاریخ مراجعه: {{ report.visited_date|to_jalali|default:'-' }}</p>
|
||||
<p class="text-nowrap mb-2"><i class="bx bx-purchase-tag bx-sm me-2"></i>سریال جدید: {{ report.new_water_meter_serial|default:'-' }}</p>
|
||||
<p class="text-nowrap mb-2"><i class="bx bx-lock-alt bx-sm me-2"></i>شماره پلمپ: {{ report.seal_number|default:'-' }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="text-nowrap mb-2"><i class="bx bx-help-circle bx-sm me-2"></i>کنتور مشکوک: {{ report.is_meter_suspicious|yesno:'بله,خیر' }}</p>
|
||||
<p class="text-nowrap mb-2"><i class="bx bx-map bx-sm me-2"></i>UTM X: {{ report.utm_x|default:'-' }}</p>
|
||||
<p class="text-nowrap mb-2"><i class="bx bx-map-pin bx-sm me-2"></i>UTM Y: {{ report.utm_y|default:'-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% if report.description %}
|
||||
<div class="mt-2">
|
||||
<p class="mb-0"><i class="bx bx-text bx-sm me-2"></i><strong>توضیحات:</strong></p>
|
||||
<div class="text-muted">{{ report.description|default:'-' }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<hr>
|
||||
<h6>عکسها</h6>
|
||||
<div class="row">
|
||||
{% for p in report.photos.all %}
|
||||
<div class="col-6 col-md-3 mb-2"><img class="img-fluid rounded border" src="{{ p.image.url }}" alt="photo"></div>
|
||||
{% empty %}
|
||||
<div class="text-muted">بدون عکس</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<h6 class="mb-2">اقلام</h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:40px">نوع</th>
|
||||
<th>آیتم</th>
|
||||
<th>تعداد</th>
|
||||
<th>قیمت واحد</th>
|
||||
<th>قیمت کل</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for ch in report.item_changes.all %}
|
||||
<tr>
|
||||
<td>{% if ch.change_type == 'add' %}<span class="text-success"><i class="bx bx-plus"></i></span>{% else %}<span class="text-danger"><i class="bx bx-minus"></i></span>{% endif %}</td>
|
||||
<td>{{ ch.item.name }}</td>
|
||||
<td>{{ ch.quantity }}</td>
|
||||
<td>{% if ch.unit_price %}{{ ch.unit_price|floatformat:0|intcomma:False }}{% else %}-{% endif %}</td>
|
||||
<td>
|
||||
{% if ch.total_price %}
|
||||
{{ ch.total_price|floatformat:0|intcomma:False }}
|
||||
{% elif ch.unit_price %}
|
||||
{{ ch.unit_price|floatformat:0|intcomma:False }}
|
||||
{% else %}-{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="5" class="text-center text-muted">تغییری ثبت نشده است</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Persistent nav in edit mode (outside cards) -->
|
||||
<div class="d-flex justify-content-between mt-3">
|
||||
{% if previous_step %}
|
||||
<a href="{% url 'processes:step_detail' instance.id previous_step.id %}" class="btn btn-label-secondary">قبلی</a>
|
||||
{% else %}
|
||||
<span></span>
|
||||
{% endif %}
|
||||
{% if next_step %}
|
||||
<a href="{% url 'processes:step_detail' instance.id next_step.id %}" class="btn btn-primary">بعدی</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<div class="">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">تاریخ مراجعه</label>
|
||||
<input type="text" id="id_visited_date_display" class="form-control" placeholder="انتخاب تاریخ" readonly required value="{% if report and edit_mode and report.visited_date %}{{ report.visited_date|date:'Y/m/d' }}{% endif %}">
|
||||
<input type="hidden" id="id_visited_date" name="visited_date" value="{% if report and edit_mode and report.visited_date %}{{ report.visited_date|date:'Y-m-d' }}{% endif %}">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">سریال کنتور جدید</label>
|
||||
<input type="text" class="form-control" name="new_water_meter_serial">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">شماره پلمپ</label>
|
||||
<input type="text" class="form-control" name="seal_number">
|
||||
</div>
|
||||
<div class="col-md-3 d-flex align-items-end">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="is_meter_suspicious" id="id_is_meter_suspicious">
|
||||
<label class="form-check-label" for="id_is_meter_suspicious">کنتور مشکوک است</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">UTM X</label>
|
||||
<input type="number" step="0.000001" class="form-control" name="utm_x" value="{% if instance.well.utm_x %}{{ instance.well.utm_x }}{% endif %}">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">UTM Y</label>
|
||||
<input type="number" step="0.000001" class="form-control" name="utm_y" value="{% if instance.well.utm_y %}{{ instance.well.utm_y }}{% endif %}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<label class="form-label">توضیحات (اختیاری)</label>
|
||||
<textarea class="form-control" rows="3" name="description"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label class="form-label mb-0">عکسها</label>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" id="btnAddPhoto"><i class="bx bx-plus"></i> افزودن عکس</button>
|
||||
</div>
|
||||
{% if report %}
|
||||
<div class="row mt-2">
|
||||
{% for p in report.photos.all %}
|
||||
<div class="col-6 col-md-3 mb-2" id="existing-photo-{{ p.id }}">
|
||||
<div class="position-relative border rounded p-1">
|
||||
<img class="img-fluid rounded" src="{{ p.image.url }}" alt="photo">
|
||||
<button type="button" class="btn btn-sm btn-danger position-absolute" style="top:6px; left:6px;" onclick="markDeletePhoto({{ p.id }})" title="حذف/برگردان"><i class='bx bx-trash'></i></button>
|
||||
<input type="hidden" name="del_photo_{{ p.id }}" id="del-photo-{{ p.id }}" value="0">
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row mt-2" id="photosPreview"></div>
|
||||
<div id="photoInputs" class="d-none"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">اقلام</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-12 mb-4">
|
||||
<h6 class="mb-2">اقلام انتخابشده قبلی <small class="text-muted">(برای حذف در نصب تیک بزنید)</small></h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:40px">حذف</th>
|
||||
<th>آیتم</th>
|
||||
<th>قیمت واحد</th>
|
||||
<th style="width:140px">تعداد</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for qi in quote_items %}
|
||||
<tr>
|
||||
<td>
|
||||
<input type="checkbox" class="form-check-input" name="rem_{{ qi.item.id }}_type" value="remove" title="حذف در نصب" {% if removed_qty|get_item:qi.item.id %}checked{% endif %}>
|
||||
<input type="hidden" name="rem_{{ qi.item.id }}_qty" value="{% if removed_qty|get_item:qi.item.id %}{{ removed_qty|get_item:qi.item.id }}{% else %}{{ qi.quantity }}{% endif %}">
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="fw-semibold">{{ qi.item.name }}</span>
|
||||
{% if qi.item.description %}<small class="text-muted">{{ qi.item.description }}</small>{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ qi.unit_price|floatformat:0|intcomma:False }} تومان</td>
|
||||
<td>
|
||||
<span class="text-muted">{% if removed_qty|get_item:qi.item.id %}{{ removed_qty|get_item:qi.item.id }}{% else %}{{ qi.quantity }}{% endif %}</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="4" class="text-center text-muted">اقلامی ثبت نشده است</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="col-12">
|
||||
<h6 class="mb-2">افزودن اقلام جدید</h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:40px"></th>
|
||||
<th>آیتم</th>
|
||||
<th>قیمت واحد</th>
|
||||
<th style="width:140px">تعداد</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for it in all_items %}
|
||||
<tr>
|
||||
<td>
|
||||
{% with add_entry=added_map|get_item:it.id %}
|
||||
<input type="checkbox" name="add_{{ it.id }}_type" value="add" class="form-check-input" {% if add_entry %}checked{% endif %}>
|
||||
<input type="hidden" name="add_{{ it.id }}_price" value="{{ it.unit_price }}">
|
||||
{% endwith %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="fw-semibold">{{ it.name }}</span>
|
||||
{% if it.description %}<small class="text-muted">{{ it.description }}</small>{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ it.unit_price|floatformat:0|intcomma:False }} تومان</td>
|
||||
<td>
|
||||
{% with add_entry=added_map|get_item:it.id %}
|
||||
<input class="form-control form-control-sm" type="number" min="1" name="add_{{ it.id }}_qty" value="{% if add_entry %}{{ add_entry.qty }}{% endif %}">
|
||||
{% endwith %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-3 d-flex justify-content-between">
|
||||
{% if previous_step %}
|
||||
<a href="{% url 'processes:step_detail' instance.id previous_step.id %}" class="btn btn-label-secondary">قبلی</a>
|
||||
{% else %}
|
||||
<span></span>
|
||||
{% endif %}
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">ثبت گزارش</button>
|
||||
{% if next_step %}
|
||||
<a href="{% url 'processes:step_detail' instance.id next_step.id %}" class="btn btn-success">بعدی</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<!-- Persian Date Picker JS -->
|
||||
<script src="https://unpkg.com/persian-date@latest/dist/persian-date.min.js"></script>
|
||||
<script src="https://unpkg.com/persian-datepicker@latest/dist/js/persian-datepicker.min.js"></script>
|
||||
<script>
|
||||
// Persian datepicker for visited_date (exact pattern like sample: display + altField)
|
||||
(function(){
|
||||
function convertPersianToEnglishNumbers(str) {
|
||||
const persianNumbers = '۰۱۲۳۴۵۶۷۸۹';
|
||||
const englishNumbers = '0123456789';
|
||||
return String(str || '').split('').map(function(char){
|
||||
const index = persianNumbers.indexOf(char);
|
||||
return index !== -1 ? englishNumbers[index] : char;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
if (window.$ && $.fn.persianDatepicker && $('#id_visited_date_display').length) {
|
||||
try {
|
||||
var $display = $('#id_visited_date_display');
|
||||
var $hidden = $('#id_visited_date');
|
||||
|
||||
// Prefill from hidden Gregorian to visible Jalali
|
||||
var initialGregorian = $hidden.val();
|
||||
if (initialGregorian) {
|
||||
try {
|
||||
var initialJalali = new window.persianDate(new Date(initialGregorian)).format('YYYY/MM/DD');
|
||||
$display.val(initialJalali);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// Initialize datepicker with altField exactly like the sample
|
||||
var picker = $display.persianDatepicker({
|
||||
calendarType: 'persian',
|
||||
altField: '#id_visited_date',
|
||||
format: 'YYYY/MM/DD',
|
||||
altFormat: 'YYYY-MM-DD',
|
||||
observer: true,
|
||||
autoClose: true,
|
||||
initialValue: false,
|
||||
calendar:{ persian: { leapYearMode: 'astronomical' } },
|
||||
onSelect: function (unixDate) {
|
||||
var g = new window.persianDate(unixDate).toCalendar('gregorian').format('YYYY-MM-DD');
|
||||
g = convertPersianToEnglishNumbers(g);
|
||||
$hidden.val(g);
|
||||
}
|
||||
});
|
||||
} catch (e) { console.error('Error initializing Persian Date Picker:', e); }
|
||||
}
|
||||
})();
|
||||
|
||||
// Require date and show success toast on submit (persist across redirect)
|
||||
(function(){
|
||||
const form = document.querySelector('form[enctype]') || document.querySelector('form');
|
||||
if (!form) return;
|
||||
form.addEventListener('submit', function(ev){
|
||||
const display = document.getElementById('id_visited_date_display');
|
||||
const hidden = document.getElementById('id_visited_date');
|
||||
if (!display || !hidden) return;
|
||||
if (!display.value || !hidden.value) {
|
||||
ev.preventDefault(); ev.stopPropagation();
|
||||
showToast('تاریخ مراجعه را انتخاب کنید', 'danger');
|
||||
display.scrollIntoView({behavior:'smooth', block:'center'});
|
||||
return false;
|
||||
}
|
||||
try { sessionStorage.setItem('install_report_saved', '1'); } catch(_) {}
|
||||
}, false);
|
||||
// on load, if saved flag exists, show toast
|
||||
try {
|
||||
if (sessionStorage.getItem('install_report_saved') === '1') {
|
||||
sessionStorage.removeItem('install_report_saved');
|
||||
showToast('گزارش نصب با موفقیت ثبت شد', 'success');
|
||||
}
|
||||
} catch(_) {}
|
||||
})();
|
||||
|
||||
// Dynamic photo add/remove
|
||||
(function(){
|
||||
const photoInputs = document.getElementById('photoInputs');
|
||||
const photosPreview = document.getElementById('photosPreview');
|
||||
const btnAddPhoto = document.getElementById('btnAddPhoto');
|
||||
let photoCounter = 0;
|
||||
function createPhotoInput() {
|
||||
photoCounter += 1;
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.name = 'photos';
|
||||
input.accept = 'image/*';
|
||||
input.className = 'd-none';
|
||||
input.dataset.key = String(photoCounter);
|
||||
input.addEventListener('change', function(){
|
||||
const file = input.files && input.files[0];
|
||||
if (!file) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(){
|
||||
const col = document.createElement('div');
|
||||
col.className = 'col-6 col-md-3 mb-2';
|
||||
col.id = 'photo-preview-' + input.dataset.key;
|
||||
col.innerHTML = `
|
||||
<div class="position-relative border rounded p-1">
|
||||
<img src="${reader.result}" class="img-fluid rounded" alt="photo">
|
||||
<button type="button" class="btn btn-sm btn-danger position-absolute" style="top:6px; left:6px;" data-key="${input.dataset.key}"><i class=\"bx bx-trash\"></i></button>
|
||||
</div>
|
||||
`;
|
||||
photosPreview.appendChild(col);
|
||||
col.querySelector('button').addEventListener('click', function(ev){
|
||||
const key = ev.currentTarget.getAttribute('data-key');
|
||||
const preview = document.getElementById('photo-preview-' + key);
|
||||
if (preview) preview.remove();
|
||||
const inp = photoInputs.querySelector(`input[data-key="${key}"]`);
|
||||
if (inp) inp.remove();
|
||||
});
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
photoInputs.appendChild(input);
|
||||
input.click();
|
||||
}
|
||||
if (btnAddPhoto) btnAddPhoto.addEventListener('click', createPhotoInput);
|
||||
})();
|
||||
|
||||
// Mark delete for existing photos
|
||||
function markDeletePhoto(id){
|
||||
const hidden = document.getElementById('del-photo-' + id);
|
||||
const wrap = document.getElementById('existing-photo-' + id);
|
||||
if (hidden && wrap){
|
||||
// toggle behavior
|
||||
if (hidden.value === '1') {
|
||||
hidden.value = '0';
|
||||
wrap.style.opacity = '1';
|
||||
// update button title back to delete
|
||||
const btn = wrap.querySelector('button');
|
||||
if (btn) btn.title = 'حذف';
|
||||
} else {
|
||||
hidden.value = '1';
|
||||
wrap.style.opacity = '0.5';
|
||||
wrap.style.position = 'relative';
|
||||
// update button title to undo
|
||||
const btn = wrap.querySelector('button');
|
||||
if (btn) btn.title = 'انصراف از حذف';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue