Compare commits
13 Commits
3ff302cc02
...
0392043ee3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0392043ee3 | ||
|
|
e3dfdef76f | ||
|
|
40b94fa891 | ||
|
|
0fabb0c478 | ||
|
|
2968d5e386 | ||
|
|
9bc8799b6b | ||
|
|
e569e2d26c | ||
|
|
57cb2f1017 | ||
|
|
0fc2d3f2c3 | ||
|
|
2b230e756e | ||
|
|
b049e2490b | ||
|
|
16fe1b0db3 | ||
|
|
322c9575f8 |
@@ -42,6 +42,13 @@ if ! command -v rsync >/dev/null 2>&1; then
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# Skip on CI environments where ~/.codex is not present and CODEX_HOME is not set.
|
||||
# Codex skills sync is a local developer tool; CI runners do not have a Codex installation.
|
||||
if [[ -z "${CODEX_HOME:-}" ]] && [[ ! -d "${HOME}/.codex" ]]; then
|
||||
echo "[SKIP] Codex home not found and CODEX_HOME not set — skipping on this host."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_root="$(cd -- "${script_dir}/.." && pwd)"
|
||||
source_dir="${repo_root}/.agents/skills"
|
||||
|
||||
72
deploy/docker-compose.yml
Normal file
72
deploy/docker-compose.yml
Normal file
@@ -0,0 +1,72 @@
|
||||
services:
|
||||
|
||||
# ── Init: копирует web/ из образа в shared volume (нужен nginx) ─────────
|
||||
web_init:
|
||||
image: breadcrumb-the-shire:latest
|
||||
volumes:
|
||||
- web_assets:/target
|
||||
command: sh -c "cp -rp /var/www/web/. /target/ && echo 'Web assets OK'"
|
||||
restart: "no"
|
||||
|
||||
# ── nginx: отдаёт статику + FastCGI → php ───────────────────────────────
|
||||
nginx:
|
||||
image: nginx:1.25-alpine
|
||||
ports:
|
||||
- "127.0.0.1:8084:80" # host nginx проксирует сюда
|
||||
volumes:
|
||||
- ./nginx/site.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- web_assets:/var/www/web:ro
|
||||
depends_on:
|
||||
web_init:
|
||||
condition: service_completed_successfully
|
||||
php:
|
||||
condition: service_started
|
||||
restart: unless-stopped
|
||||
|
||||
# ── PHP-FPM: весь код внутри образа ─────────────────────────────────────
|
||||
php:
|
||||
image: breadcrumb-the-shire:latest
|
||||
env_file: .env
|
||||
volumes:
|
||||
- ./storage:/var/www/storage
|
||||
- ./var:/var/www/var
|
||||
depends_on:
|
||||
- db
|
||||
- memcached
|
||||
restart: unless-stopped
|
||||
|
||||
# ── Планировщик задач ────────────────────────────────────────────────────
|
||||
scheduler:
|
||||
image: breadcrumb-the-shire:latest
|
||||
env_file: .env
|
||||
volumes:
|
||||
- ./storage:/var/www/storage
|
||||
- ./var:/var/www/var
|
||||
depends_on:
|
||||
- db
|
||||
- memcached
|
||||
command: sh -c "while true; do php -d display_errors=0 bin/console scheduler:run || true; sleep 60; done"
|
||||
restart: unless-stopped
|
||||
|
||||
# ── MariaDB ──────────────────────────────────────────────────────────────
|
||||
db:
|
||||
image: mariadb:11.4
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
env_file: .env
|
||||
environment:
|
||||
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
|
||||
MARIADB_DATABASE: ${DB_NAME}
|
||||
MARIADB_USER: ${DB_USER}
|
||||
MARIADB_PASSWORD: ${DB_PASS}
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
|
||||
# ── Memcached ────────────────────────────────────────────────────────────
|
||||
memcached:
|
||||
image: memcached:1.6-alpine
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
web_assets: # статика из образа (web/)
|
||||
db_data: # данные MariaDB
|
||||
41
deploy/nginx/site.conf
Normal file
41
deploy/nginx/site.conf
Normal file
@@ -0,0 +1,41 @@
|
||||
# Docker nginx — только HTTP (SSL терминируется на host nginx)
|
||||
server {
|
||||
listen 80;
|
||||
server_name projects.breadcrumb-online.de;
|
||||
|
||||
client_max_body_size 10M;
|
||||
|
||||
root /var/www/web;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
# Docker DNS resolver — re-resolves php hostname on each request
|
||||
resolver 127.0.0.11 valid=5s;
|
||||
|
||||
location ~ \.php$ {
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
# SSL терминирован на host nginx — передаём это в PHP
|
||||
fastcgi_param HTTPS on;
|
||||
fastcgi_param HTTP_X_FORWARDED_PROTO https;
|
||||
set $php_upstream php:9000;
|
||||
fastcgi_pass $php_upstream;
|
||||
fastcgi_hide_header Cache-Control;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, private" always;
|
||||
}
|
||||
|
||||
location ~* \.mjs$ {
|
||||
default_type application/javascript;
|
||||
try_files $uri =404;
|
||||
expires 30d;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location ~* \.(css|js|png|jpg|jpeg|gif|svg|ico|webp|woff2?)$ {
|
||||
expires 30d;
|
||||
access_log off;
|
||||
}
|
||||
}
|
||||
36
deploy/setup.sh
Normal file
36
deploy/setup.sh
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
# setup.sh — первоначальная настройка на сервере
|
||||
# Запускать один раз: bash setup.sh
|
||||
set -e
|
||||
|
||||
DEPLOY_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$DEPLOY_DIR"
|
||||
|
||||
echo "=== [1/4] Загружаем образ в Docker ==="
|
||||
docker load < breadcrumb-the-shire.tar.gz
|
||||
|
||||
echo "=== [2/4] Создаём папки для данных ==="
|
||||
mkdir -p storage/logs storage/runtime storage/users
|
||||
mkdir -p var/cache var/log var/tmp
|
||||
mkdir -p nginx/certs
|
||||
|
||||
echo "=== [3/4] Права на папки ==="
|
||||
chmod 755 storage var
|
||||
chmod 775 storage/logs storage/runtime storage/users
|
||||
chmod 775 var/cache var/log var/tmp
|
||||
|
||||
echo "=== [4/4] Готово! ==="
|
||||
echo ""
|
||||
echo "Следующие шаги:"
|
||||
echo " 1. Получи SSL-сертификат:"
|
||||
echo " certbot certonly --standalone -d projects.breadcrumb-online.de"
|
||||
echo ""
|
||||
echo " 2. Скопируй сертификаты:"
|
||||
echo " cp /etc/letsencrypt/live/projects.breadcrumb-online.de/fullchain.pem nginx/certs/"
|
||||
echo " cp /etc/letsencrypt/live/projects.breadcrumb-online.de/privkey.pem nginx/certs/"
|
||||
echo ""
|
||||
echo " 3. Запусти стек:"
|
||||
echo " docker compose up -d"
|
||||
echo ""
|
||||
echo " 4. Проверь логи:"
|
||||
echo " docker compose logs -f"
|
||||
@@ -1,4 +1,4 @@
|
||||
@layer pages;
|
||||
@layer pages {
|
||||
|
||||
/* Address book list — dense identity layout */
|
||||
|
||||
@@ -143,3 +143,5 @@
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
} /* end @layer pages */
|
||||
|
||||
@@ -403,6 +403,7 @@
|
||||
"Date": "Datum",
|
||||
"Checkbox": "Checkbox",
|
||||
"Select": "Auswahl",
|
||||
"User selection": "Benutzerauswahl",
|
||||
"Text content": "Textinhalt",
|
||||
"Maximum %d fields allowed": "Maximal %d Felder erlaubt",
|
||||
"Field %d: unknown type \"%s\"": "Feld %d: unbekannter Typ \"%s\"",
|
||||
@@ -488,6 +489,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",
|
||||
|
||||
@@ -403,6 +403,7 @@
|
||||
"Date": "Date",
|
||||
"Checkbox": "Checkbox",
|
||||
"Select": "Select",
|
||||
"User selection": "User selection",
|
||||
"Text content": "Text content",
|
||||
"Maximum %d fields allowed": "Maximum %d fields allowed",
|
||||
"Field %d: unknown type \"%s\"": "Field %d: unknown type \"%s\"",
|
||||
@@ -488,6 +489,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",
|
||||
|
||||
@@ -38,7 +38,7 @@ class SoftwareProductService
|
||||
return $this->repository->listWithSchema();
|
||||
}
|
||||
|
||||
private const ALLOWED_FIELD_TYPES = ['heading', 'paragraph', 'text', 'textarea', 'number', 'date', 'checkbox', 'select'];
|
||||
private const ALLOWED_FIELD_TYPES = ['heading', 'paragraph', 'text', 'textarea', 'number', 'date', 'checkbox', 'select', 'user-select'];
|
||||
private const DISPLAY_ONLY_TYPES = ['heading', 'paragraph'];
|
||||
private const MAX_SCHEMA_FIELDS = 50;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
$schemaFields = is_array($schemaFields ?? null) ? $schemaFields : [];
|
||||
$fieldValues = is_array($fieldValues ?? null) ? $fieldValues : [];
|
||||
$fieldDiff = is_array($fieldDiff ?? null) ? $fieldDiff : [];
|
||||
$tenantUsers = is_array($tenantUsers ?? null) ? $tenantUsers : [];
|
||||
$readonly = !empty($readonly);
|
||||
$hasDiff = $fieldDiff !== [];
|
||||
?>
|
||||
@@ -56,6 +57,43 @@ $hasDiff = $fieldDiff !== [];
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php elseif ($type === 'user-select'): ?>
|
||||
<?php
|
||||
$selectedIds = [];
|
||||
if ($value !== '') {
|
||||
$decoded = json_decode($value, true);
|
||||
$selectedIds = is_array($decoded) ? array_map('strval', $decoded) : [$value];
|
||||
}
|
||||
?>
|
||||
<div class="handover-diff-field<?php e($diffClass); ?>">
|
||||
<label for="<?php e($inputId); ?>">
|
||||
<?php e($label); ?>
|
||||
<?php if ($required): ?><span class="handover-form-required"> *</span><?php endif; ?>
|
||||
</label>
|
||||
<select
|
||||
id="<?php e($inputId); ?>"
|
||||
name="field_<?php e($key); ?>[]"
|
||||
multiple
|
||||
size="5"
|
||||
<?php if ($required): ?> aria-required="true"<?php endif; ?>
|
||||
<?php if ($readonly): ?> disabled<?php endif; ?>
|
||||
>
|
||||
<?php foreach ($tenantUsers as $u):
|
||||
$uId = (string) ($u['u']['id'] ?? $u['id'] ?? '');
|
||||
$uName = trim((string) ($u['u']['display_name'] ?? $u['display_name'] ?? ''));
|
||||
$uEmail = (string) ($u['u']['email'] ?? $u['email'] ?? '');
|
||||
$uLabel = $uName !== '' ? $uName : $uEmail;
|
||||
?>
|
||||
<option value="<?php e($uId); ?>"<?php if (in_array($uId, $selectedIds, true)): ?> selected<?php endif; ?>>
|
||||
<?php e($uLabel); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php if ($diff !== null && $diff['type'] === 'changed'): ?>
|
||||
<small class="app-muted handover-diff-indicator" aria-label="<?php e(t('Changed')); ?>"><?php e(t('was: %s', (string) ($diff['old'] ?? ''))); ?></small>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php elseif ($type === 'select'): ?>
|
||||
<div class="handover-diff-field<?php e($diffClass); ?>">
|
||||
<label for="<?php e($inputId); ?>">
|
||||
|
||||
@@ -230,6 +230,17 @@ if ($step === $protocolStep) {
|
||||
? $product['name']
|
||||
: (string) ($product['bc_description'] ?? $wizardData['product_code']);
|
||||
|
||||
// Load tenant users — always available for user-select schema fields
|
||||
$rows = DB::select(
|
||||
'SELECT u.id, u.display_name, u.email'
|
||||
. ' FROM users u'
|
||||
. ' JOIN user_tenants ut ON ut.user_id = u.id'
|
||||
. ' WHERE ut.tenant_id = ? AND u.active = 1'
|
||||
. ' ORDER BY u.display_name ASC',
|
||||
(string) $currentTenantId
|
||||
);
|
||||
$tenantUsers = is_array($rows) ? $rows : [];
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
if (!Session::checkCsrfToken()) {
|
||||
$modeParam = $canManage ? '&mode=' . ($wizardData['mode'] ?? 'self') : '';
|
||||
@@ -246,6 +257,9 @@ if ($step === $protocolStep) {
|
||||
$type = $field['type'] ?? 'text';
|
||||
if ($type === 'checkbox') {
|
||||
$fieldValues[$key] = $request->body('field_' . $key, '') !== '' ? '1' : '0';
|
||||
} elseif ($type === 'user-select') {
|
||||
$ids = $request->body('field_' . $key, []);
|
||||
$fieldValues[$key] = json_encode(array_values(array_filter((array) $ids)));
|
||||
} else {
|
||||
$fieldValues[$key] = (string) $request->body('field_' . $key, '');
|
||||
}
|
||||
|
||||
@@ -149,11 +149,24 @@ 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>
|
||||
<select id="wizard-domain" name="domain_no" disabled>
|
||||
<option value=""><?php e(t('Select a customer first...')); ?></option>
|
||||
</select>
|
||||
<div class="app-wizard-domain-select-wrap">
|
||||
<select id="wizard-domain" disabled>
|
||||
<option value=""><?php e(t('Select a customer first...')); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
<input type="hidden" name="domain_no" id="wizard-domain-no" value="<?php e($form['domain_no'] ?? ''); ?>">
|
||||
<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; ?>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use MintyPHP\Buffer;
|
||||
use MintyPHP\DB;
|
||||
use MintyPHP\Http\SessionStoreInterface;
|
||||
use MintyPHP\Module\Helpdesk\HelpdeskAuthorizationPolicy;
|
||||
use MintyPHP\Module\Helpdesk\Service\HandoverRevisionService;
|
||||
@@ -147,6 +148,9 @@ if ($request->isMethod('POST') && $canEdit) {
|
||||
$type = $field['type'] ?? 'text';
|
||||
if ($type === 'checkbox') {
|
||||
$submittedValues[$key] = $request->body('field_' . $key, '') !== '' ? '1' : '0';
|
||||
} elseif ($type === 'user-select') {
|
||||
$ids = $request->body('field_' . $key, []);
|
||||
$submittedValues[$key] = json_encode(array_values(array_filter((array) $ids)));
|
||||
} else {
|
||||
$submittedValues[$key] = (string) $request->body('field_' . $key, '');
|
||||
}
|
||||
@@ -201,6 +205,17 @@ if ($request->isMethod('POST') && $canEdit) {
|
||||
$validationSummaryErrors = $errorBag->toArray();
|
||||
$errors = $errorBag->toFlatList();
|
||||
|
||||
// Load tenant users — always available for user-select schema fields
|
||||
$rows = DB::select(
|
||||
'SELECT u.id, u.display_name, u.email'
|
||||
. ' FROM users u'
|
||||
. ' JOIN user_tenants ut ON ut.user_id = u.id'
|
||||
. ' WHERE ut.tenant_id = ? AND u.active = 1'
|
||||
. ' ORDER BY u.display_name ASC',
|
||||
(string) $tenantId
|
||||
);
|
||||
$tenantUsers = is_array($rows) ? $rows : [];
|
||||
|
||||
// Load revision data
|
||||
$revisions = $revisionService->listByHandover($tenantId, $handoverId);
|
||||
$activeRevisionNumber = (int) $request->query('revision', 0);
|
||||
|
||||
@@ -53,6 +53,7 @@ $handoverSchemaFields = is_array($handoverSchemaFields ?? null) ? $handoverSchem
|
||||
'type_date' => t('Date'),
|
||||
'type_checkbox' => t('Checkbox'),
|
||||
'type_select' => t('Select'),
|
||||
'type_user-select' => t('User selection'),
|
||||
'text_content' => t('Text content'),
|
||||
'field' => t('Field'),
|
||||
'preview' => t('Preview'),
|
||||
|
||||
@@ -23,8 +23,12 @@ export function initHandoverDomainSelect(root = document) {
|
||||
const scope = group.closest('form') || document;
|
||||
const lookupContainer = scope.querySelector('[data-app-lookup]');
|
||||
const domainSelect = group.querySelector('#wizard-domain');
|
||||
const domainNoInput = group.querySelector('#wizard-domain-no');
|
||||
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,14 +71,33 @@ 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) {
|
||||
if (domainNoInput) domainNoInput.value = '';
|
||||
if (domainUrlInput) domainUrlInput.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
const resetDomain = () => {
|
||||
clearOptions();
|
||||
addPlaceholder(textInitial);
|
||||
domainSelect.disabled = true;
|
||||
if (domainUrlInput) {
|
||||
domainUrlInput.value = '';
|
||||
}
|
||||
if (domainNoInput) domainNoInput.value = '';
|
||||
if (domainUrlInput) domainUrlInput.value = '';
|
||||
setFeedback('', '');
|
||||
showManual(false);
|
||||
};
|
||||
|
||||
const loadDomains = async (debitorNo) => {
|
||||
@@ -95,9 +118,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');
|
||||
@@ -121,11 +146,24 @@ export function initHandoverDomainSelect(root = document) {
|
||||
|
||||
on(domainSelect, 'change', () => {
|
||||
const selected = domainSelect.options[domainSelect.selectedIndex];
|
||||
if (domainUrlInput) {
|
||||
domainUrlInput.value = selected?.dataset.url || '';
|
||||
}
|
||||
if (domainNoInput) domainNoInput.value = selected?.value || '';
|
||||
if (domainUrlInput) domainUrlInput.value = selected?.dataset.url || '';
|
||||
});
|
||||
|
||||
// Sync manual inputs → hidden domain_no / domain_url fields
|
||||
if (manualNoInput) {
|
||||
on(manualNoInput, 'input', () => {
|
||||
if (domainNoInput) domainNoInput.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) {
|
||||
@@ -148,6 +186,9 @@ export function initHandoverDomainSelect(root = document) {
|
||||
domainSelect.dispatchEvent(new Event('change'));
|
||||
}
|
||||
});
|
||||
} else if (domainNoInput && domainNoInput.value) {
|
||||
// Restore hidden value on page reload with validation errors (manual mode)
|
||||
if (manualNoInput) manualNoInput.value = domainNoInput.value;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
import { resolveHost } from '/js/core/app-dom.js';
|
||||
|
||||
const FIELD_TYPES = ['heading', 'paragraph', 'text', 'textarea', 'number', 'date', 'checkbox', 'select'];
|
||||
const FIELD_TYPES = ['heading', 'paragraph', 'text', 'textarea', 'number', 'date', 'checkbox', 'select', 'user-select'];
|
||||
const DISPLAY_ONLY_TYPES = ['heading', 'paragraph'];
|
||||
const EMPTY_API = Object.freeze({ destroy: () => {} });
|
||||
|
||||
@@ -385,6 +385,12 @@ function init(root) {
|
||||
option.textContent = opt.label || '';
|
||||
input.appendChild(option);
|
||||
});
|
||||
} else if (field.type === 'user-select') {
|
||||
input = document.createElement('select');
|
||||
const placeholder = document.createElement('option');
|
||||
placeholder.value = '';
|
||||
placeholder.textContent = t['type_user-select'] || 'User selection';
|
||||
input.appendChild(placeholder);
|
||||
} else {
|
||||
input = document.createElement('input');
|
||||
input.type = field.type === 'number' ? 'number' : field.type === 'date' ? 'date' : 'text';
|
||||
|
||||
Reference in New Issue
Block a user