feat(helpdesk): show manual domain input when customer has no domains

When the domain API returns an empty list for a selected customer, the
domain select is replaced with two text inputs: domain name (required)
and URL (optional). JS syncs the manual values into the existing hidden
domain_no/domain_url fields so the server-side validation and save logic
is unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
aminovfariz
2026-06-08 10:04:27 +02:00
parent 2b230e756e
commit 0fc2d3f2c3
4 changed files with 63 additions and 4 deletions

View File

@@ -488,6 +488,9 @@
"Select a customer first...": "Bitte zuerst einen Kunden auswählen...",
"Loading domains...": "Domains werden geladen...",
"No domains found for this customer": "Keine Domains für diesen Kunden gefunden",
"No domains found for this customer. Enter domain name manually:": "Keine Domains für diesen Kunden gefunden. Domain-Name manuell eingeben:",
"Domain name (e.g. example.de)": "Domain-Name (z. B. example.de)",
"URL (optional, e.g. https://example.de)": "URL (optional, z. B. https://example.de)",
"Failed to load domains": "Domains konnten nicht geladen werden",
"Please select a domain": "Bitte wählen Sie eine Domain",
"Delete": "Löschen",

View File

@@ -488,6 +488,9 @@
"Select a customer first...": "Select a customer first...",
"Loading domains...": "Loading domains...",
"No domains found for this customer": "No domains found for this customer",
"No domains found for this customer. Enter domain name manually:": "No domains found for this customer. Enter domain name manually:",
"Domain name (e.g. example.de)": "Domain name (e.g. example.de)",
"URL (optional, e.g. https://example.de)": "URL (optional, e.g. https://example.de)",
"Failed to load domains": "Failed to load domains",
"Please select a domain": "Please select a domain",
"Delete": "Delete",

View File

@@ -149,11 +149,23 @@ if ($canManage && $mode === 'assign' && $step === 2) {
data-initial-domain="<?php e($form['domain_no'] ?? ''); ?>"
>
<label for="wizard-domain"><?php e(t('Domain')); ?> <span class="handover-form-required">*</span></label>
<div class="app-wizard-domain-select-wrap">
<select id="wizard-domain" name="domain_no" disabled>
<option value=""><?php e(t('Select a customer first...')); ?></option>
</select>
</div>
<input type="hidden" name="domain_url" id="wizard-domain-url" value="<?php e($form['domain_url'] ?? ''); ?>">
<div id="wizard-domain-feedback" class="app-wizard-field-feedback" role="alert" hidden></div>
<!-- Manual entry — shown by JS when no domains exist for this customer -->
<div id="wizard-domain-manual" hidden>
<p class="app-wizard-field-hint"><?php e(t('No domains found for this customer. Enter domain name manually:')); ?></p>
<input type="text" id="wizard-domain-manual-no" placeholder="<?php e(t('Domain name (e.g. example.de)')); ?>"
value="<?php e($form['domain_no'] ?? ''); ?>" autocomplete="off">
<input type="text" id="wizard-domain-manual-url" placeholder="<?php e(t('URL (optional, e.g. https://example.de)')); ?>"
value="<?php e($form['domain_url'] ?? ''); ?>" autocomplete="off" style="margin-top:0.4rem;">
</div>
<?php if (!empty($errors['domain_no'])): ?>
<p class="field-error"><?php e($errors['domain_no']); ?></p>
<?php endif; ?>

View File

@@ -25,6 +25,9 @@ export function initHandoverDomainSelect(root = document) {
const domainSelect = group.querySelector('#wizard-domain');
const domainUrlInput = group.querySelector('#wizard-domain-url');
const feedback = group.querySelector('#wizard-domain-feedback');
const manualBlock = group.querySelector('#wizard-domain-manual');
const manualNoInput = group.querySelector('#wizard-domain-manual-no');
const manualUrlInput = group.querySelector('#wizard-domain-manual-url');
if (!lookupContainer || !domainSelect) {
return EMPTY_API;
}
@@ -67,6 +70,24 @@ export function initHandoverDomainSelect(root = document) {
feedback.dataset.variant = variant || '';
};
const showManual = (show) => {
if (!manualBlock) {
return;
}
manualBlock.hidden = !show;
domainSelect.closest('.app-wizard-domain-select-wrap')?.toggleAttribute('hidden', show);
if (!show && manualNoInput) {
manualNoInput.value = '';
}
if (!show && manualUrlInput) {
manualUrlInput.value = '';
}
// When showing manual, clear the hidden domain_no/url so only manual values submit
if (show && domainUrlInput) {
domainUrlInput.value = '';
}
};
const resetDomain = () => {
clearOptions();
addPlaceholder(textInitial);
@@ -75,6 +96,7 @@ export function initHandoverDomainSelect(root = document) {
domainUrlInput.value = '';
}
setFeedback('', '');
showManual(false);
};
const loadDomains = async (debitorNo) => {
@@ -95,9 +117,11 @@ export function initHandoverDomainSelect(root = document) {
if (items.length === 0) {
domainSelect.disabled = true;
setFeedback(textEmpty, 'warning');
setFeedback('', '');
showManual(true);
return;
}
showManual(false);
items.forEach((item) => {
const opt = document.createElement('option');
@@ -126,6 +150,23 @@ export function initHandoverDomainSelect(root = document) {
}
});
// Sync manual inputs → hidden domain_no / domain_url fields
if (manualNoInput) {
on(manualNoInput, 'input', () => {
const hiddenNo = scope.querySelector('input[name="domain_no"]');
if (hiddenNo) {
hiddenNo.value = manualNoInput.value.trim();
}
});
}
if (manualUrlInput) {
on(manualUrlInput, 'input', () => {
if (domainUrlInput) {
domainUrlInput.value = manualUrlInput.value.trim();
}
});
}
on(lookupContainer, 'lookup:select', (event) => {
const debitorNo = event.detail?.value || '';
if (debitorNo) {