345 lines
15 KiB
HTML
345 lines
15 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block head %}
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container mt-4">
|
|
<h2>New Calibration Batch</h2>
|
|
|
|
<form method="POST" action="{{ url_for('calibrations.create_calibrations') }}" onsubmit="return validateForm()">
|
|
<input type="hidden" name="calibrated_by" value="{{ user.id }}">
|
|
<div class="card mb-4">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Batch Information</h5>
|
|
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label for="work_order_id" class="form-label">Work Order</label>
|
|
<select class="form-select select2" id="work_order_id" name="work_order_id" required>
|
|
<option value="">Select Work Order</option>
|
|
{% for wo in work_orders %}
|
|
<option value="{{ wo.id }}">{{ wo.order_number }} - {{ wo.customers.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<label for="std_used" class="form-label">Standard Used</label>
|
|
<select class="form-select select2" id="std_used" name="std_used" required>
|
|
<option value="">Select Standard</option>
|
|
{% for std in standards %}
|
|
<option value="{{ std.id }}">{{ std.make }} {{ std.model }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<label for="date" class="form-label">Calibration Date</label>
|
|
<input type="date" class="form-control" id="date" name="date" required>
|
|
</div>
|
|
|
|
<div class="col-md-3">
|
|
<label for="probe_model" class="form-label">Probe Model</label>
|
|
<select class="form-select select2" id="probe_model" name="probe_model" required>
|
|
<option value="">Select Model</option>
|
|
{% for model in probe_models %}
|
|
<option value="{{ model.id }}">{{ model.model_name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="parameter" class="form-label">Parameter</label>
|
|
<select class="form-select select2" id="parameter" name="parameter" required>
|
|
<option value="">Select Parameter</option>
|
|
{% for param in parameters %}
|
|
<option value="{{ param.id }}">{{ param.parameter_name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="text-end mt-3">
|
|
<button type="button" id="confirmBatch" class="btn btn-success">Confirm Batch Info</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mb-4" id="channelsSection" style="display: none;">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h5 class="card-title mb-0">Channels</h5>
|
|
<button type="button" class="btn btn-sm btn-outline-primary" id="addChannel">
|
|
<i class="bi bi-plus"></i> Add Channel
|
|
</button>
|
|
</div>
|
|
|
|
<div id="channelRows">
|
|
<div class="channel-row mb-3">
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label">Channel</label>
|
|
<select class="form-select select2" name="channel_serial[]" required>
|
|
<option value="">Select Channel</option>
|
|
{% for channel in channels %}
|
|
<option value="{{ channel.serial_number }}">{{ channel.serial_number }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Scale</label>
|
|
<input type="number" step="0.001" class="form-control" name="scale[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Offset</label>
|
|
<input type="number" step="0.001" class="form-control" name="offset[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Dev Low</label>
|
|
<input type="number" step="0.001" class="form-control" name="deviation_low[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Dev Mid</label>
|
|
<input type="number" step="0.001" class="form-control" name="deviation_mid[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Dev High</label>
|
|
<input type="number" step="0.001" class="form-control" name="deviation_high[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Set Low</label>
|
|
<input type="number" step="0.001" class="form-control" name="set_low[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Set Mid</label>
|
|
<input type="number" step="0.001" class="form-control" name="set_mid[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Set High</label>
|
|
<input type="number" step="0.001" class="form-control" name="set_high[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<div class="form-check form-switch mt-4">
|
|
<input class="form-check-input" type="checkbox" name="passed[]" checked>
|
|
<label class="form-check-label">Passed</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="text-end">
|
|
<button type="submit" class="btn btn-primary" id="saveBatch" disabled>Save Calibration Batch</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
$(function() {
|
|
// Initialize Select2 and confirm button
|
|
$('.select2').select2();
|
|
|
|
console.log('Initializing calibration form...');
|
|
|
|
// Handle batch confirmation
|
|
$('#confirmBatch').on('click', function(e) {
|
|
e.preventDefault();
|
|
console.log('Confirm Batch clicked');
|
|
|
|
// Debug: Check if jQuery is working
|
|
console.log('jQuery version:', $.fn.jquery);
|
|
// Validate required fields
|
|
if (!$('#work_order_id').val() || !$('#std_used').val() ||
|
|
!$('#date').val() || !$('#probe_model').val() || !$('#parameter').val()) {
|
|
alert('Please complete all Batch Information fields');
|
|
return;
|
|
}
|
|
|
|
// Validate date is not in future
|
|
const selectedDate = new Date($('#date').val());
|
|
const today = new Date();
|
|
if (selectedDate > today) {
|
|
alert('Calibration date cannot be in the future');
|
|
return;
|
|
}
|
|
|
|
// Disable and gray out batch section
|
|
$('.card-body', $(this).closest('.card')).find('input, select').prop('disabled', true);
|
|
$(this).closest('.card').addClass('bg-light');
|
|
$('#confirmBatch').prop('disabled', true).removeClass('btn-success').addClass('btn-secondary');
|
|
|
|
// Show channels section with filtered options
|
|
$('#channelsSection').show();
|
|
updateChannelDropdowns($('#probe_model').val(), $('#parameter').val());
|
|
$('#saveBatch').prop('disabled', false);
|
|
|
|
// Remove the initial empty channel row if present
|
|
if ($('.channel-row').length === 1 && !$('select[name="channel_serial[]"]').val()) {
|
|
$('.channel-row').remove();
|
|
}
|
|
|
|
// Add first channel row with filtered options
|
|
addChannelRow();
|
|
});
|
|
|
|
// Function to update channel dropdowns
|
|
function updateChannelDropdowns(probeModelId, parameterId) {
|
|
if (!probeModelId || !parameterId) {
|
|
return;
|
|
}
|
|
|
|
$.get('/calibrations/filtered-channels', {
|
|
probe_model: probeModelId,
|
|
parameter: parameterId
|
|
}, function(data) {
|
|
window.filteredChannels = data; // Cache the filtered channels
|
|
updateAllChannelDropdowns(data);
|
|
});
|
|
}
|
|
|
|
function updateAllChannelDropdowns(channels) {
|
|
$('select[name="channel_serial[]"]').each(function() {
|
|
const $select = $(this);
|
|
const currentVal = $select.val();
|
|
$select.empty().append('<option value="">Select Channel</option>');
|
|
|
|
channels.forEach(function(channel) {
|
|
$select.append($('<option>', {
|
|
value: channel.serial_number,
|
|
text: channel.serial_number
|
|
}));
|
|
});
|
|
|
|
if (currentVal && channels.some(c => c.serial_number === currentVal)) {
|
|
$select.val(currentVal);
|
|
}
|
|
$select.trigger('change');
|
|
});
|
|
}
|
|
|
|
// Watch for changes in probe model and parameter
|
|
$('#probe_model, #parameter').on('change', function() {
|
|
const probeModelId = $('#probe_model').val();
|
|
const parameterId = $('#parameter').val();
|
|
updateChannelDropdowns(probeModelId, parameterId);
|
|
});
|
|
|
|
// Add channel row with current filter
|
|
function addChannelRow() {
|
|
const channelOptions = window.filteredChannels ?
|
|
window.filteredChannels.map(c =>
|
|
`<option value="${c.serial_number}">${c.serial_number}</option>`
|
|
).join('') :
|
|
'';
|
|
|
|
const newRow = `
|
|
<div class="channel-row mb-3">
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label">Channel</label>
|
|
<select class="form-select select2" name="channel_serial[]" required>
|
|
<option value="">Select Channel</option>
|
|
${channelOptions}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Scale</label>
|
|
<input type="number" step="0.001" class="form-control" name="scale[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Offset</label>
|
|
<input type="number" step="0.001" class="form-control" name="offset[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Dev Low</label>
|
|
<input type="number" step="0.001" class="form-control" name="deviation_low[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Dev Mid</label>
|
|
<input type="number" step="0.001" class="form-control" name="deviation_mid[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Dev High</label>
|
|
<input type="number" step="0.001" class="form-control" name="deviation_high[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Set Low</label>
|
|
<input type="number" step="0.001" class="form-control" name="set_low[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Set Mid</label>
|
|
<input type="number" step="0.001" class="form-control" name="set_mid[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label">Set High</label>
|
|
<input type="number" step="0.001" class="form-control" name="set_high[]" required>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<div class="form-check form-switch mt-4">
|
|
<input class="form-check-input" type="checkbox" name="passed[]" checked>
|
|
<label class="form-check-label">Passed</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>`;
|
|
|
|
$('#channelRows').append(newRow);
|
|
$('.select2').select2();
|
|
}
|
|
|
|
$('#addChannel').click(addChannelRow);
|
|
|
|
// Initialize with any existing selections
|
|
if ($('#probe_model').val() && $('#parameter').val()) {
|
|
updateChannelDropdowns($('#probe_model').val(), $('#parameter').val());
|
|
}
|
|
|
|
// Form validation before submission
|
|
function validateForm() {
|
|
// Validate channel selections
|
|
const serialPattern = /^[0-9A-F]{16}$/;
|
|
const serials = $('select[name="channel_serial[]"]');
|
|
let valid = true;
|
|
|
|
serials.each(function() {
|
|
const serial = $(this).val();
|
|
if (!serial || !serialPattern.test(serial)) {
|
|
alert('Invalid channel serial number: ' + serial);
|
|
$(this).focus();
|
|
valid = false;
|
|
return false; // break loop
|
|
}
|
|
});
|
|
|
|
if (!valid) return false;
|
|
|
|
// Validate numeric fields
|
|
const numericFields = [
|
|
'scale[]', 'offset[]', 'deviation_low[]',
|
|
'deviation_mid[]', 'deviation_high[]',
|
|
'set_low[]', 'set_mid[]', 'set_high[]'
|
|
];
|
|
|
|
for (const field of numericFields) {
|
|
$(`input[name="${field}"]`).each(function() {
|
|
const val = parseFloat($(this).val());
|
|
if (isNaN(val)) {
|
|
alert('Please enter valid numbers for all fields');
|
|
$(this).focus();
|
|
valid = false;
|
|
return false;
|
|
}
|
|
});
|
|
if (!valid) break;
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|