Capture doctor prescription units and other therapies, and let chemists search the doctor list.
Specific Folder FTP Upload / ftp-deploy (push) Successful in 42s Details

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ravi Gulhane 2026-10-07 16:26:36 +05:30
parent a462bb3100
commit 1c68104228
3 changed files with 219 additions and 33 deletions

View File

@ -4,6 +4,7 @@
@include('layouts.shared/title-meta', ['title' => "Doctor & Chemist Survey"])
@include('layouts.shared/head-css', ["mode" => $mode ?? '', "demo" => $demo ?? ''])
<link href="{{asset('assets/libs/sweetalert2/sweetalert2.min.css')}}" rel="stylesheet" type="text/css" />
<link href="{{ asset('assets/libs/select2/select2.min.css') }}" rel="stylesheet" type="text/css" />
<style>
.quick-action-form-card .form-control { font-size: 16px; }
.survey-section { border: 1px solid #d9d9d9; border-radius: 0.25rem; margin-bottom: 0.75rem; }
@ -74,6 +75,7 @@
</div>
@include('layouts.shared/footer-script')
<script src="{{asset('assets/libs/sweetalert2/sweetalert2.min.js')}}"></script>
<script src="{{ asset('assets/libs/select2/select2.min.js') }}"></script>
@php
$savedAnswers = [];
$savedSource = null;
@ -144,6 +146,7 @@ $(function () {
});
syncOtherFields();
syncServiceFields();
syncProductUnits();
}
function syncOtherFields() {
@ -178,6 +181,20 @@ $(function () {
});
}
function syncProductUnits() {
$('#doctor-fields .product-row').each(function () {
var row = $(this);
var show = row.find('.product-choice').val() === 'Yes' && !$('#doctor-fields').prop('disabled');
var units = row.find('.product-units');
units.toggle(show);
var input = units.find('input');
input.prop('required', show).prop('disabled', !show);
if (!show) {
input.val('');
}
});
}
function fillSelect(select, contacts) {
var current = select.value;
select.innerHTML = '<option value="">-- Select --</option>';
@ -193,6 +210,12 @@ $(function () {
option.setAttribute('data-stockist-id', contact.stockist_id || '');
select.appendChild(option);
});
if (select.classList.contains('stockist-pick')) {
var other = document.createElement('option');
other.value = 'others';
other.textContent = 'Others';
select.appendChild(other);
}
if (current) {
select.value = current;
}
@ -212,12 +235,16 @@ $(function () {
return item.contact && String(item.contact.type || '').toLowerCase() === 'ebt_stockist';
});
document.querySelectorAll('#chemist-fields .rcpa-doctor').forEach(function (select) {
if ($(select).data('select2')) {
$(select).select2('destroy');
}
fillSelect(select, doctors);
});
document.querySelectorAll('#chemist-fields .stockist-pick').forEach(function (select) {
fillSelect(select, stockists);
});
matchSavedContacts();
initDoctorSearch();
},
error: function () {
contactsReady = true;
@ -225,7 +252,11 @@ $(function () {
});
}
var doctorLock = false;
function applyDoctor(select) {
if (doctorLock) {
return;
}
var row = $(select).closest('tr');
var option = select.options[select.selectedIndex];
var chosen = select.value;
@ -237,22 +268,30 @@ $(function () {
}
});
if (duplicate) {
select.value = '';
option = select.options[0];
doctorLock = true;
$(select).val('').trigger('change');
doctorLock = false;
row.find('.rcpa-name, .rcpa-mobile, .rcpa-speciality, .rcpa-code').val('');
fail('This doctor is already selected for this product.');
return;
}
}
row.find('.rcpa-name').val(select.value ? option.textContent : '');
row.find('.rcpa-mobile').val(select.value ? (option.getAttribute('data-phone') || '') : '');
row.find('.rcpa-speciality').val(select.value ? (option.getAttribute('data-speciality') || '') : '');
row.find('.rcpa-code').val(select.value ? (option.getAttribute('data-code') || '') : '');
if (!chosen || !option) {
row.find('.rcpa-name, .rcpa-mobile, .rcpa-speciality, .rcpa-code').val('');
return;
}
row.find('.rcpa-name').val(option.textContent || '');
row.find('.rcpa-mobile').val(option.getAttribute('data-phone') || '');
row.find('.rcpa-speciality').val(option.getAttribute('data-speciality') || '');
row.find('.rcpa-code').val(option.getAttribute('data-code') || '');
}
function applyStockist(select) {
function applyStockist(select, keepAddress) {
var block = $(select).closest('.stockist-block');
var option = select.options[select.selectedIndex];
var chosen = select.value;
if (chosen) {
var isOther = chosen === 'others';
if (chosen && !isOther) {
var duplicate = false;
$('#chemist-fields .stockist-pick').each(function () {
if (this !== select && this.value === chosen) {
@ -261,13 +300,33 @@ $(function () {
});
if (duplicate) {
select.value = '';
chosen = '';
isOther = false;
option = select.options[0];
fail('This stockist is already selected.');
}
}
block.find('.stockist-name').val(select.value ? option.textContent : '');
block.find('.stockist-location').val(select.value ? (option.getAttribute('data-location') || '') : '');
block.find('.stockist-id').val(select.value ? (option.getAttribute('data-stockist-id') || '') : '');
var location = block.find('.stockist-location');
var otherName = block.find('.stockist-other-name');
block.find('.stockist-other').toggle(isOther);
block.find('.stockist-id-wrap').toggle(!isOther);
location.prop('readonly', !isOther).prop('required', isOther).attr('tabindex', isOther ? null : '-1');
otherName.prop('required', isOther);
block.find('.stockist-address-label').html(isOther
? 'Other Stockist Address <span class="text-danger">*</span>'
: 'Stockist ' + (select.getAttribute('data-slot') || '') + ' Location');
if (isOther) {
block.find('.stockist-name').val(otherName.val());
block.find('.stockist-id').val('');
if (!keepAddress) {
location.val('');
}
return;
}
otherName.val('').prop('required', false);
block.find('.stockist-name').val(chosen ? option.textContent : '');
location.val(chosen ? (option.getAttribute('data-location') || '') : '');
block.find('.stockist-id').val(chosen ? (option.getAttribute('data-stockist-id') || '') : '');
}
function setVisit(type) {
@ -282,6 +341,33 @@ $(function () {
lockDoctorPrefix();
}
refreshSections();
if (doctor) {
$('#chemist-fields .rcpa-doctor').each(function () {
if ($(this).data('select2')) {
$(this).select2('destroy');
}
});
} else {
initDoctorSearch();
}
}
function initDoctorSearch() {
if ($('#field-visit_type').val() !== 'chemist') {
return;
}
$('#chemist-fields .rcpa-doctor').each(function () {
var field = $(this);
if (field.data('select2')) {
field.select2('destroy');
}
field.select2({
width: '100%',
dropdownParent: $('.quick-action-form-card'),
placeholder: '-- Select --',
allowClear: true
});
});
}
function lockDoctorPrefix() {
@ -388,11 +474,34 @@ $(function () {
selectSavedOption(select, $(select).closest('tr').find('.rcpa-name').val());
});
document.querySelectorAll('#chemist-fields .stockist-pick').forEach(function (select) {
selectSavedOption(select, $(select).closest('.stockist-block').find('.stockist-name').val());
var block = $(select).closest('.stockist-block');
var name = block.find('.stockist-name').val();
if (!name) {
return;
}
var matched = false;
for (var i = 0; i < select.options.length; i++) {
if (select.options[i].value !== 'others' && select.options[i].textContent === name) {
select.value = select.options[i].value;
matched = true;
break;
}
}
if (!matched) {
select.value = 'others';
block.find('.stockist-other-name').val(name);
applyStockist(select, true);
return;
}
applyStockist(select, true);
});
}
$('#field-doctor_name').on('input', lockDoctorPrefix);
$('#doctor-fields').on('change', '.product-choice', syncProductUnits);
$('#chemist-fields').on('input', '.stockist-other-name', function () {
$(this).closest('.stockist-block').find('.stockist-name').val(this.value);
});
$('#field-visit_type').on('change', function () { setVisit(this.value); });
$('#report-form').on('input change', '.survey-section :input', refreshSections);
$('#chemist-fields').on('change', '.rcpa-doctor', function () { applyDoctor(this); refreshSections(); });
@ -413,6 +522,20 @@ $(function () {
return /^[A-Za-z0-9]+$/.test(value);
}
function addressOk(value) {
return /^[A-Za-z0-9]+(?:[ ]+[A-Za-z0-9]+)*$/.test(value);
}
function therapiesFilled(root) {
var filled = false;
$(root).find('.therapy-units').each(function () {
if (this.value !== '') {
filled = true;
}
});
return filled;
}
function mobileTaken(type, mobile) {
return surveyContacts.some(function (item) {
var contact = item.contact || {};
@ -456,8 +579,8 @@ $(function () {
fail('A doctor with this mobile number already exists.');
return;
}
if (!alphaOk($('#field-doctor_location').val().trim())) {
fail('Doctor location can contain letters, spaces and periods only.');
if (!addressOk($('#field-doctor_location').val().trim())) {
fail('Doctor address can contain letters, numbers and spaces only.');
return;
}
if (!/^[1-9][0-9]{5}$/.test($('#field-doctor_pincode').val())) {
@ -472,6 +595,24 @@ $(function () {
fail('Enter the other specialty in letters only.');
return;
}
var missingUnits = false;
$('#doctor-fields .product-choice').each(function () {
if ($(this).val() !== 'Yes') {
return;
}
var units = $(this).closest('.product-row').find('.product-units input').val();
if (units === '' || !/^\d+$/.test(units)) {
missingUnits = true;
}
});
if (missingUnits) {
fail('Enter the number of units in a month for each product marked Yes. 0 is allowed.');
return;
}
if (!therapiesFilled('#doctor-fields')) {
fail('Enter at least one other therapy. 0 is allowed.');
return;
}
var photos = ['visiting_card', 'rx_pad', 'doctor_board'];
var hasPhoto = false;
for (var i = 0; i < photos.length; i++) {
@ -489,8 +630,12 @@ $(function () {
return;
}
} else {
if (!alphaOk($('#field-chemist_name').val().trim()) || !alphaOk($('#field-chemist_location').val().trim())) {
fail('Chemist name and location can contain letters, spaces and periods only.');
if (!alphaOk($('#field-chemist_name').val().trim())) {
fail('Chemist name can contain letters, spaces and periods only.');
return;
}
if (!addressOk($('#field-chemist_location').val().trim())) {
fail('Chemist address can contain letters, numbers and spaces only.');
return;
}
if (!/^[6-9][0-9]{9}$/.test($('#field-chemist_mobile').val())) {
@ -505,14 +650,25 @@ $(function () {
fail('A chemist with this mobile number already exists.');
return;
}
var missingDoctor = false;
$('#chemist-fields .rcpa-doctor[data-slot="1"]').each(function () {
if (!this.value) {
missingDoctor = true;
var badStockist = false;
$('#chemist-fields .stockist-pick').each(function () {
if (this.value !== 'others') {
return;
}
var block = $(this).closest('.stockist-block');
var otherName = block.find('.stockist-other-name').val().trim();
var otherAddress = block.find('.stockist-location').val().trim();
block.find('.stockist-name').val(otherName);
if (!addressOk(otherName) || !addressOk(otherAddress)) {
badStockist = true;
}
});
if (missingDoctor) {
fail('Select doctor 1 for each product.');
if (badStockist) {
fail('Other stockist name and address can contain letters, numbers and spaces only.');
return;
}
if (!therapiesFilled('#chemist-fields')) {
fail('Enter at least one other therapy. 0 is allowed.');
return;
}
var missingCharge = false;

View File

@ -21,7 +21,7 @@ $services = ['BP Monitoring' => 'chemist_bp_monitoring','Weighing Machine' => 'c
<div class="section-body">
<div class="mb-2"><label class="form-label">Chemist Name <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-chemist_name" name="chemist_name" data-gate required></div>
<div class="mb-2"><label class="form-label">Mobile Number <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-chemist_mobile" name="mobile_number" inputmode="numeric" maxlength="10" data-gate required></div>
<div class="mb-2"><label class="form-label">Chemist Location <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-chemist_location" name="chemist_location" data-gate required></div>
<div class="mb-2"><label class="form-label">Chemist Address <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-chemist_location" name="chemist_location" data-gate required></div>
<div class="mb-2"><label class="form-label">Chemist Pincode <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-chemist_pincode" name="chemist_pincode" inputmode="numeric" maxlength="6" data-gate required></div>
<div class="mb-2"><label class="form-label">Chemist Type <span class="text-danger">*</span></label><select class="form-control" name="chemist_type" data-gate required><option value="">-- Select --</option>@foreach($chemistTypes as $item)<option value="{{$item}}">{{$item}}</option>@endforeach</select></div>
<div class="mb-2"><label class="form-label" for="field-chemist_manager_joint_visit">Manager (EBM) Joint Visit Status <span class="text-danger">*</span></label><select class="form-control" id="field-chemist_manager_joint_visit" name="manager_joint_visit" data-gate required><option value="">-- Select --</option><option value="Yes">Yes</option><option value="No">No</option></select></div>
@ -45,7 +45,7 @@ $services = ['BP Monitoring' => 'chemist_bp_monitoring','Weighing Machine' => 'c
<table class="table table-sm align-middle mb-0 rcpa-doctors">
<thead>
<tr>
<th>Name</th>
<th>Prescribing Dr</th>
<th>Mobile</th>
<th>Speciality</th>
<th>Code</th>
@ -55,7 +55,7 @@ $services = ['BP Monitoring' => 'chemist_bp_monitoring','Weighing Machine' => 'c
@foreach([1, 2, 3] as $doctor)
<tr data-product="{{$code}}">
<td>
<select class="form-control rcpa-doctor" data-product="{{$code}}" data-slot="{{$doctor}}" aria-label="Doctor {{$doctor}} name" @if($doctor === 1) data-gate required @endif>
<select class="form-control rcpa-doctor" data-product="{{$code}}" data-slot="{{$doctor}}" aria-label="Prescribing Dr {{$doctor}}">
<option value="">-- Select --</option>
</select>
<input type="hidden" name="{{$code}}_doctor_{{$doctor}}_name" class="rcpa-name">
@ -78,9 +78,10 @@ $services = ['BP Monitoring' => 'chemist_bp_monitoring','Weighing Machine' => 'c
@foreach([1,2] as $n)
<div class="stockist-block mb-3" data-slot="{{$n}}">
<div class="mb-2"><label class="form-label">Stockist {{$n}} Name</label><select class="form-control stockist-pick" data-slot="{{$n}}"><option value="">-- Select --</option></select><input type="hidden" name="stockist_{{$n}}_name" class="stockist-name"></div>
<div class="mb-2"><label class="form-label">Stockist {{$n}} Location</label><input type="text" class="form-control stockist-location" name="stockist_{{$n}}_location" readonly tabindex="-1"></div>
<div class="mb-2"><label class="form-label">Stockist {{$n}} ID</label><input type="text" class="form-control stockist-id" name="stockist_{{$n}}_id" readonly tabindex="-1"></div>
<div class="mb-2"><label class="form-label">Frequency of Supply</label><input type="number" min="0" step="1" class="form-control" name="stockist_{{$n}}_frequency"></div>
<div class="mb-2 stockist-other" style="display:none"><label class="form-label">Other Stockist Name <span class="text-danger">*</span></label><input type="text" class="form-control stockist-other-name"></div>
<div class="mb-2"><label class="form-label stockist-address-label">Stockist {{$n}} Location</label><input type="text" class="form-control stockist-location" name="stockist_{{$n}}_location" readonly tabindex="-1"></div>
<div class="mb-2 stockist-id-wrap"><label class="form-label">Stockist {{$n}} ID</label><input type="text" class="form-control stockist-id" name="stockist_{{$n}}_id" readonly tabindex="-1"></div>
<div class="mb-2"><label class="form-label">Frequency of Supply (No. of times in a Month)</label><input type="number" min="0" step="1" class="form-control" name="stockist_{{$n}}_frequency"></div>
</div>
@endforeach
</div>
@ -89,8 +90,9 @@ $services = ['BP Monitoring' => 'chemist_bp_monitoring','Weighing Machine' => 'c
<div class="survey-section">
<button type="button" class="section-head">Other therapies</button>
<div class="section-body">
<p class="text-muted">Enter at least one. 0 is allowed. <span class="text-danger">*</span></p>
@foreach($therapies as $label => $code)
<div class="mb-2"><label class="form-label">{{$label}}</label><input type="number" min="0" step="1" class="form-control" name="{{$code}}"></div>
<div class="mb-2"><label class="form-label">{{$label}}</label><input type="number" min="0" step="1" class="form-control therapy-units" name="{{$code}}"></div>
@endforeach
</div>
</div>

View File

@ -17,7 +17,7 @@ $products = ['Asthalin Inhaler' => 'asthalin_inhaler','Asthalin Rotacap' => 'ast
<div class="mb-2" id="wrap-other_degree" style="display:none"><label class="form-label">Other Degree <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-other_degree" name="other_degree" disabled></div>
<div class="mb-2"><label class="form-label">Specialty <span class="text-danger">*</span></label><select class="form-control" id="field-specialty" name="specialty" data-gate required><option value="">-- Select --</option>@foreach($specialties as $item)<option value="{{$item}}">{{$item}}</option>@endforeach</select></div>
<div class="mb-2" id="wrap-other_specialty" style="display:none"><label class="form-label">Other Specialty <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-other_specialty" name="other_specialty" disabled></div>
<div class="mb-2"><label class="form-label">Doctor Location <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-doctor_location" name="doctor_location" data-gate required></div>
<div class="mb-2"><label class="form-label">Doctor Address <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-doctor_location" name="doctor_location" data-gate required></div>
<div class="mb-2"><label class="form-label">Doctor Pincode <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-doctor_pincode" name="doctor_pincode" inputmode="numeric" maxlength="6" data-gate required></div>
<div class="mb-2"><label class="form-label">Doctor Type <span class="text-danger">*</span></label><select class="form-control" id="field-doctor_type" name="doctor_type" data-gate required><option value="">-- Select --</option>@foreach($doctorTypes as $item)<option value="{{$item}}">{{$item}}</option>@endforeach</select></div>
<div class="mb-2"><label class="form-label">No. of Total Patients Seen in a Day <span class="text-danger">*</span></label><select class="form-control" name="total_patients" data-gate required><option value="">-- Select --</option>@foreach($bands as $item)<option value="{{$item}}">{{$item}}</option>@endforeach</select></div>
@ -35,10 +35,38 @@ $products = ['Asthalin Inhaler' => 'asthalin_inhaler','Asthalin Rotacap' => 'ast
</div>
<div class="survey-section">
<button type="button" class="section-head">Is the Doctor Prescriber of our Products?</button>
<button type="button" class="section-head">Doctor Prescription (No. of Units in a Month)</button>
<div class="section-body">
@foreach($products as $label => $code)
<div class="mb-2"><label class="form-label">{{$label}} <span class="text-danger">*</span></label><select class="form-control" name="{{$code}}" data-gate required><option value="">-- Select --</option><option value="Yes">Yes</option><option value="No">No</option></select></div>
<div class="mb-2 product-row">
<label class="form-label">{{$label}} <span class="text-danger">*</span></label>
<select class="form-control product-choice" name="{{$code}}" data-gate required><option value="">-- Select --</option><option value="Yes">Yes</option><option value="No">No</option></select>
<div class="mt-2 product-units" style="display:none">
<label class="form-label">No. of Units in a Month <span class="text-danger">*</span></label>
<input type="number" min="0" step="1" class="form-control" name="{{$code}}_units" disabled>
</div>
</div>
@endforeach
</div>
</div>
<div class="survey-section">
<button type="button" class="section-head">Other therapies</button>
<div class="section-body">
<p class="text-muted">Enter at least one. 0 is allowed. <span class="text-danger">*</span></p>
@foreach([
'Nebulization No. of Respules per Month (all included)' => 'neb_respules',
'Foracort Inhaler No. of Units per Month' => 'foracort_inhaler_units',
'Foracort Rotacaps No. of Units per Month' => 'foracort_rotacaps_units',
'Asthalin Syrup No. of Units per Month' => 'asthalin_syrup_units',
'Levolin Syrup No. of Units per Month' => 'levolin_syrup_units',
'Deriphyllin No. of Units per Month' => 'deriphyllin_units',
'Beclomethasone Tablet No. of Units per Month' => 'beclomethasone_units',
'Methylprednisolone Tablet No. of Units per Month' => 'methylprednisolone_units',
'Prednisolone Tablet No. of Units per Month' => 'prednisolone_units',
'Cough Syrups No. of Units per Month' => 'cough_syrups_units',
] as $label => $code)
<div class="mb-2"><label class="form-label">{{$label}}</label><input type="number" min="0" step="1" class="form-control therapy-units" name="{{$code}}"></div>
@endforeach
</div>
</div>
@ -46,7 +74,7 @@ $products = ['Asthalin Inhaler' => 'asthalin_inhaler','Asthalin Rotacap' => 'ast
<div class="survey-section">
<button type="button" class="section-head">Upload photos</button>
<div class="section-body">
<p class="text-muted">Upload one of visiting card, Rx pad or doctor board. JPG, JPEG or PNG, max 10 MB.</p>
<p class="text-muted">Upload one of visiting card, Rx pad or doctor board. JPG, JPEG or PNG, max 10 MB. <span class="text-danger">*</span></p>
<div class="mb-2"><label class="form-label">Visiting Card</label><input type="file" class="form-control" id="field-visiting_card" name="visiting_card" accept=".jpg,.jpeg,.png,image/jpeg,image/png" capture="environment"></div>
<div class="mb-2"><label class="form-label">Rx Pad</label><input type="file" class="form-control" id="field-rx_pad" name="rx_pad" accept=".jpg,.jpeg,.png,image/jpeg,image/png" capture="environment"></div>
<div class="mb-2"><label class="form-label">Doctor Board</label><input type="file" class="form-control" id="field-doctor_board" name="doctor_board" accept=".jpg,.jpeg,.png,image/jpeg,image/png" capture="environment"></div>