473 lines
No EOL
20 KiB
PHP
473 lines
No EOL
20 KiB
PHP
{{--
|
|
DHL Shipment Creation Modal
|
|
|
|
This view is optimized to work with data prepared by DhlModalService.
|
|
The service handles address parsing, validation, and data preparation.
|
|
--}}
|
|
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">
|
|
<i class="fas fa-shipping-fast text-primary"></i>
|
|
@if($modalMode === 'search')
|
|
DHL Sendung erstellen
|
|
@elseif($modalMode === 'info')
|
|
DHL Sendungen - Bestellung #{{ $order->id }}
|
|
@elseif($modalMode === 'create')
|
|
DHL Sendung erstellen - Bestellung #{{ $order->id }}
|
|
@endif
|
|
</h5>
|
|
<button type="button" class="close" data-dismiss="modal">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
</div>
|
|
|
|
{{-- Display any errors at the top --}}
|
|
@if(!empty($errors))
|
|
<div class="alert alert-danger mx-3 mt-3">
|
|
<i class="fas fa-exclamation-triangle"></i>
|
|
<strong>Fehler:</strong>
|
|
<ul class="mb-0 mt-1">
|
|
@foreach($errors as $error)
|
|
<li>{{ $error }}</li>
|
|
@endforeach
|
|
</ul>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- Display warnings --}}
|
|
@if(!empty($warnings))
|
|
<div class="alert alert-warning mx-3 mt-3">
|
|
<i class="fas fa-exclamation-circle"></i>
|
|
<strong>Achtung:</strong>
|
|
<ul class="mb-0 mt-1">
|
|
@foreach($warnings as $warning)
|
|
<li>{{ $warning }}</li>
|
|
@endforeach
|
|
</ul>
|
|
</div>
|
|
@endif
|
|
|
|
@if($modalMode === 'search')
|
|
@include('admin.dhl.modal_in_search_shipment')
|
|
@elseif($modalMode === 'info')
|
|
@include('admin.dhl.modal_in_shipment_info')
|
|
@elseif($modalMode === 'create')
|
|
@include('admin.dhl.modal_in_order_shipment')
|
|
@endif
|
|
</div>
|
|
|
|
<script>
|
|
$(document).ready(function() {
|
|
@if($modalMode === 'search')
|
|
// Load order functionality
|
|
$('#load-order-btn').click(function() {
|
|
var orderId = $('#order-id-input').val();
|
|
|
|
if (!orderId || orderId < 1) {
|
|
alert('Bitte geben Sie eine gültige Bestellungs-ID ein.');
|
|
return;
|
|
}
|
|
|
|
// Reload modal with order ID
|
|
var btn = $(this);
|
|
btn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Laden...');
|
|
|
|
$.post('{{ route('modal_load') }}', {
|
|
action: 'create-dhl-shipment',
|
|
id: orderId,
|
|
_token: '{{ csrf_token() }}'
|
|
}).done(function(response) {
|
|
console.log(response);
|
|
$('#modals-load-content .modal-dialog').html(response.html);
|
|
}).fail(function() {
|
|
alert('Bestellung konnte nicht geladen werden.');
|
|
btn.prop('disabled', false).html('<i class="fas fa-search"></i> Laden');
|
|
});
|
|
});
|
|
|
|
// Enter key handling
|
|
$('#order-id-input').keypress(function(e) {
|
|
if (e.which === 13) {
|
|
$('#load-order-btn').click();
|
|
}
|
|
});
|
|
@endif
|
|
|
|
@if($modalMode === 'create')
|
|
// Form submission with improved validation
|
|
$('#modal-create-shipment-form').on('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
var form = $(this);
|
|
var formData = form.serialize();
|
|
var submitBtn = $('#create-shipment-btn');
|
|
|
|
// Client-side validation
|
|
var isValid = validateForm();
|
|
if (!isValid) {
|
|
return;
|
|
}
|
|
|
|
if ($('#dhl-preflight-confirmed').val() !== '1') {
|
|
validateDhlAddress(formData, submitBtn, function() {
|
|
$('#dhl-preflight-confirmed').val('1');
|
|
submitBtn.prop('disabled', false).html('<i class="fas fa-shipping-fast"></i> Sendung jetzt erstellen');
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
validateDhlAddress(formData, submitBtn, function() {
|
|
submitDhlShipment(form, formData, submitBtn);
|
|
});
|
|
});
|
|
|
|
function validateDhlAddress(formData, submitBtn, onSuccess) {
|
|
submitBtn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Prüfe Sendung...');
|
|
|
|
$.ajax({
|
|
url: '{{ route('admin.dhl.validate-address') }}',
|
|
method: 'POST',
|
|
data: formData,
|
|
success: function(response) {
|
|
renderDhlPreflightStatus(response);
|
|
|
|
onSuccess();
|
|
},
|
|
error: function(xhr) {
|
|
var response = xhr.responseJSON || {};
|
|
|
|
renderDhlPreflightStatus(response);
|
|
resetDhlPreflight(true);
|
|
}
|
|
});
|
|
}
|
|
|
|
function renderDhlPreflightStatus(response) {
|
|
var status = response.status || 'error';
|
|
var product = (response.preflight && response.preflight.product) ? response.preflight.product : {};
|
|
var address = (response.preflight && response.preflight.address) ? response.preflight.address : {};
|
|
var normalized = address.normalized || {};
|
|
var errors = response.errors || [];
|
|
var warnings = response.warnings || [];
|
|
var statusClass = status === 'valid' ? 'success' : (status === 'warning' ? 'warning' : 'danger');
|
|
var statusIcon = status === 'valid' ? 'check-circle' : (status === 'warning' ? 'exclamation-triangle' : 'times-circle');
|
|
var productScope = product.scope_label || 'Nicht geprüft';
|
|
var productCode = product.code || $('#modal-product-code').val() || '-';
|
|
var countryLabel = product.country_label || $('#shipping_country option:selected').text().trim() || '-';
|
|
var validationBadgeClass = address.validation_available ? 'info' : 'warning';
|
|
var validationBadgeText = address.validation_available ? 'Formale DACH-Prüfung' : 'Basisprüfung';
|
|
var validationMessage = address.validation_message || 'Adressvalidierung wurde nicht eindeutig bestimmt.';
|
|
|
|
var listHtml = '';
|
|
if (errors.length) {
|
|
listHtml += '<div class="alert alert-danger mb-2"><strong>Fehler:</strong><ul class="mb-0 pl-3">';
|
|
errors.forEach(function(error) {
|
|
listHtml += '<li>' + escapeHtml(error) + '</li>';
|
|
});
|
|
listHtml += '</ul></div>';
|
|
}
|
|
if (warnings.length) {
|
|
listHtml += '<div class="alert alert-warning mb-2"><strong>Hinweise:</strong><ul class="mb-0 pl-3">';
|
|
warnings.forEach(function(warning) {
|
|
listHtml += '<li>' + escapeHtml(warning) + '</li>';
|
|
});
|
|
listHtml += '</ul></div>';
|
|
}
|
|
|
|
if (!errors.length && !warnings.length) {
|
|
listHtml = '<div class="alert alert-success mb-2">Alle Vorabprüfungen sind erfolgreich.</div>';
|
|
}
|
|
|
|
$('#dhl-preflight-status')
|
|
.removeClass('border-secondary border-success border-warning border-danger')
|
|
.addClass('border-' + statusClass)
|
|
.html(`
|
|
<div class="card-header d-flex align-items-center">
|
|
<i class="fas fa-${statusIcon} text-${statusClass} mr-2"></i>
|
|
<strong>Vorabprüfung: ${escapeHtml(response.message || 'Prüfung abgeschlossen')}</strong>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<small class="text-muted d-block">Produktcode</small>
|
|
<strong>${escapeHtml(productCode)}</strong>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<small class="text-muted d-block">Sendungsart</small>
|
|
<strong>${escapeHtml(productScope)}</strong>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<small class="text-muted d-block">Zielland</small>
|
|
<strong>${escapeHtml(countryLabel)}</strong>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<small class="text-muted d-block">Lieferadresse</small>
|
|
<strong>${escapeHtml([normalized.street, normalized.house_number].filter(Boolean).join(' '))}</strong>,
|
|
${escapeHtml([normalized.postal_code, normalized.city].filter(Boolean).join(' '))}
|
|
</div>
|
|
<div class="mb-3">
|
|
<small class="text-muted d-block">Adressvalidierung</small>
|
|
<span class="badge badge-${validationBadgeClass} mr-2">${escapeHtml(validationBadgeText)}</span>
|
|
<span>${escapeHtml(validationMessage)}</span>
|
|
</div>
|
|
${listHtml}
|
|
${response.can_create_label ? '<small class="text-muted">Prüfung bestätigt. Klicken Sie erneut auf „Sendung jetzt erstellen“, um das Label zu erzeugen.</small>' : ''}
|
|
</div>
|
|
`);
|
|
}
|
|
|
|
function resetDhlPreflight(keepStatus) {
|
|
$('#dhl-preflight-confirmed').val('0');
|
|
$('#create-shipment-btn').prop('disabled', false).html('<i class="fas fa-clipboard-check"></i> Vorabprüfung durchführen');
|
|
|
|
if (keepStatus) {
|
|
return;
|
|
}
|
|
|
|
$('#dhl-preflight-status')
|
|
.removeClass('border-success border-warning border-danger')
|
|
.addClass('border-secondary')
|
|
.html(`
|
|
<div class="card-header d-flex align-items-center">
|
|
<i class="fas fa-clipboard-check text-secondary mr-2"></i>
|
|
<strong>Vorabprüfung vor Labelerstellung</strong>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-muted mb-0">
|
|
Formular geändert. Bitte führen Sie die Vorabprüfung erneut aus, bevor das Label erstellt wird.
|
|
</p>
|
|
</div>
|
|
`);
|
|
}
|
|
|
|
function escapeHtml(value) {
|
|
return $('<div>').text(value || '').html();
|
|
}
|
|
|
|
function submitDhlShipment(form, formData, submitBtn) {
|
|
// Disable submit button
|
|
submitBtn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Wird erstellt...');
|
|
|
|
$.ajax({
|
|
url: form.attr('action'),
|
|
method: 'POST',
|
|
data: formData,
|
|
success: function(response) {
|
|
if (response.success) {
|
|
console.log(response);
|
|
|
|
// Show success message
|
|
if (typeof showAlert === 'function') {
|
|
showAlert('success', response.message);
|
|
} else {
|
|
alert(response.message);
|
|
}
|
|
|
|
// Switch to info mode instead of closing modal
|
|
setTimeout(function() {
|
|
// Show loading indicator
|
|
var loadingHtml = `
|
|
<div class="modal-body text-center py-5">
|
|
<div class="spinner-border text-primary mb-3" role="status">
|
|
<span class="sr-only">Laden...</span>
|
|
</div>
|
|
<h5>Sendung erfolgreich erstellt!</h5>
|
|
<p class="text-muted">Lade Sendungsinformationen...</p>
|
|
</div>
|
|
`;
|
|
$('#modals-load-content .modal-dialog').html(loadingHtml);
|
|
|
|
// Reload modal in info mode to show the created shipment
|
|
$.post('{{ route('modal_load') }}', {
|
|
action: 'create-dhl-shipment',
|
|
id: {{ $order->id }},
|
|
_token: '{{ csrf_token() }}'
|
|
}).done(function(response) {
|
|
$('#modals-load-content .modal-dialog').html(response.html);
|
|
|
|
// Show success message in the new modal content
|
|
setTimeout(function() {
|
|
if (typeof showAlert === 'function') {
|
|
showAlert('success', 'Sendung erfolgreich erstellt! Die Sendungsinformationen werden angezeigt.');
|
|
}
|
|
}, 100);
|
|
}).fail(function() {
|
|
alert('Fehler beim Laden der Sendungsinformationen.');
|
|
});
|
|
}, 1000); // Wait 1 seconds to show success message
|
|
} else {
|
|
renderDhlCreationErrorStatus(response);
|
|
resetDhlPreflight(true);
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
var response = xhr.responseJSON || {
|
|
message: 'Fehler beim Erstellen der Sendung.'
|
|
};
|
|
if (xhr.responseJSON && xhr.responseJSON.message) {
|
|
response.message = xhr.responseJSON.message;
|
|
} else if (xhr.responseText) {
|
|
try {
|
|
var errorData = JSON.parse(xhr.responseText);
|
|
if (errorData.errors) {
|
|
response.errors = Object.values(errorData.errors).flat();
|
|
}
|
|
} catch (e) {
|
|
// Ignore JSON parse errors
|
|
}
|
|
}
|
|
|
|
renderDhlCreationErrorStatus(response);
|
|
resetDhlPreflight(true);
|
|
}
|
|
});
|
|
}
|
|
|
|
function renderDhlCreationErrorStatus(response) {
|
|
var errors = response.errors || [response.message || 'DHL hat die Labelerstellung abgelehnt.'];
|
|
|
|
renderDhlPreflightStatus({
|
|
status: 'error',
|
|
can_create_label: false,
|
|
message: 'DHL hat die Adresse bei der Labelerstellung abgelehnt.',
|
|
errors: errors,
|
|
warnings: [],
|
|
preflight: {
|
|
product: {
|
|
code: $('#modal-product-code').val(),
|
|
scope_label: $('#modal-product-code option:selected').text().trim()
|
|
},
|
|
address: {
|
|
validation_available: true,
|
|
validation_message: 'DHL-Leitcodierung über mustEncode wurde bei der Labelerstellung geprüft.',
|
|
normalized: {
|
|
street: $('#shipping_address').val(),
|
|
house_number: $('#shipping_houseNumber').val(),
|
|
postal_code: $('#shipping_zipcode').val(),
|
|
city: $('#shipping_city').val()
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Enhanced form validation
|
|
function validateForm() {
|
|
var isValid = true;
|
|
|
|
// Clear previous validation states
|
|
$('.form-control').removeClass('is-invalid');
|
|
$('.invalid-feedback').remove();
|
|
|
|
// Weight validation
|
|
var weight = parseFloat($('#modal-weight').val());
|
|
if (!weight || weight < 0.1 || weight > 31.5) {
|
|
$('#modal-weight').addClass('is-invalid').after('<div class="invalid-feedback">Gewicht muss zwischen 0.1 und 31.5 kg liegen.</div>');
|
|
isValid = false;
|
|
}
|
|
|
|
// Required fields validation
|
|
var requiredFields = [
|
|
'#shipping_firstname',
|
|
'#shipping_lastname',
|
|
'#shipping_address',
|
|
'#shipping_zipcode',
|
|
'#shipping_city'
|
|
];
|
|
|
|
requiredFields.forEach(function(fieldId) {
|
|
var field = $(fieldId);
|
|
if (!field.val().trim()) {
|
|
field.addClass('is-invalid').after('<div class="invalid-feedback">Dieses Feld ist erforderlich.</div>');
|
|
isValid = false;
|
|
}
|
|
});
|
|
|
|
var reference = $('#modal-reference').val();
|
|
if (reference && reference.length > 35) {
|
|
$('#modal-reference').addClass('is-invalid').after('<div class="invalid-feedback">Referenz darf maximal 35 Zeichen lang sein.</div>');
|
|
isValid = false;
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
// Real-time weight validation
|
|
$('#modal-weight').on('input', function() {
|
|
var weight = parseFloat($(this).val());
|
|
var input = $(this);
|
|
|
|
input.removeClass('is-invalid');
|
|
input.siblings('.invalid-feedback').remove();
|
|
|
|
if (weight && (weight < 0.1 || weight > 31.5)) {
|
|
input.addClass('is-invalid');
|
|
if (weight < 0.1) {
|
|
input.after('<div class="invalid-feedback">Mindestgewicht: 0.1 kg</div>');
|
|
} else {
|
|
input.after('<div class="invalid-feedback">Maximalgewicht: 31.5 kg</div>');
|
|
}
|
|
}
|
|
});
|
|
|
|
var dhlProductSuggestions = @json($productSuggestions ?? []);
|
|
|
|
$('#shipping_country').on('change', function() {
|
|
var countryCode = $(this).find(':selected').data('country-code');
|
|
var suggestedProduct = dhlProductSuggestions[countryCode];
|
|
|
|
if (suggestedProduct && $('#modal-product-code option[value="' + suggestedProduct + '"]').length) {
|
|
$('#modal-product-code').val(suggestedProduct);
|
|
}
|
|
});
|
|
|
|
$('#modal-create-shipment-form').on('input change', 'input, select', function() {
|
|
if ($(this).attr('id') !== 'dhl-preflight-confirmed') {
|
|
resetDhlPreflight();
|
|
}
|
|
});
|
|
|
|
// Real-time required field validation
|
|
$('input[required], select[required]').on('blur', function() {
|
|
var field = $(this);
|
|
if (!field.val().trim()) {
|
|
field.addClass('is-invalid');
|
|
if (!field.siblings('.invalid-feedback').length) {
|
|
field.after('<div class="invalid-feedback">Dieses Feld ist erforderlich.</div>');
|
|
}
|
|
} else {
|
|
field.removeClass('is-invalid');
|
|
field.siblings('.invalid-feedback').remove();
|
|
}
|
|
});
|
|
@endif
|
|
|
|
// Helper function for showing alerts
|
|
window.showAlert = function(type, message) {
|
|
var alertClass = type === 'success' ? 'alert-success' : 'alert-danger';
|
|
var iconClass = type === 'success' ? 'fas fa-check-circle' : 'fas fa-exclamation-circle';
|
|
|
|
var alertHtml = `
|
|
<div class="alert ${alertClass} alert-dismissible fade show" role="alert">
|
|
<i class="${iconClass}"></i>
|
|
${message}
|
|
<button type="button" class="close" data-dismiss="alert">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
</div>
|
|
`;
|
|
|
|
$('.modal-body').prepend(alertHtml);
|
|
|
|
// Auto-hide success alerts after 5 seconds
|
|
if (type === 'success') {
|
|
setTimeout(function() {
|
|
$('.alert-success').alert('close');
|
|
}, 5000);
|
|
}
|
|
}
|
|
});
|
|
</script> |