diff --git a/i18n/default_de.json b/i18n/default_de.json index a7345cf..8d77617 100644 --- a/i18n/default_de.json +++ b/i18n/default_de.json @@ -336,6 +336,9 @@ "Remove": "Entfernen", "Allowed file types: SVG, PNG, JPG, WEBP": "Erlaubte Dateitypen: SVG, PNG, JPG, WEBP", "Favicon": "Favicon", + "Tenant favicon": "Mandanten-Favicon", + "Square PNG. Icons are center-cropped.": "Quadratisches PNG. Symbole werden mittig zugeschnitten.", + "Delete this favicon?": "Dieses Favicon löschen?", "Upload favicon": "Favicon hochladen", "Favicon updated": "Favicon aktualisiert", "Favicon removed": "Favicon entfernt", diff --git a/i18n/default_en.json b/i18n/default_en.json index 471d487..d372196 100644 --- a/i18n/default_en.json +++ b/i18n/default_en.json @@ -336,6 +336,9 @@ "Remove": "Remove", "Allowed file types: SVG, PNG, JPG, WEBP": "Allowed file types: SVG, PNG, JPG, WEBP", "Favicon": "Favicon", + "Tenant favicon": "Tenant favicon", + "Square PNG. Icons are center-cropped.": "Square PNG. Icons are center-cropped.", + "Delete this favicon?": "Delete this favicon?", "Upload favicon": "Upload favicon", "Favicon updated": "Favicon updated", "Favicon removed": "Favicon removed", diff --git a/pages/admin/tenants/_form.phtml b/pages/admin/tenants/_form.phtml index db0427b..a9170d9 100644 --- a/pages/admin/tenants/_form.phtml +++ b/pages/admin/tenants/_form.phtml @@ -235,6 +235,35 @@ $openOverrideCard = $detailsOpenAll;
+
> + +
+ 'favicon', + 'accept' => 'image/png', + 'hint' => t('Square PNG. Icons are center-cropped.'), + 'currentSrc' => $faviconPresent && $tenantFaviconUuid !== '' + ? asset('favicon/tenants/' . $tenantFaviconUuid . '/favicon/favicon-32x32.png') + : '', + 'currentLabel' => t('Favicon'), + 'formId' => 'tenant-favicon-form', + 'deleteFormId' => $faviconPresent ? 'tenant-favicon-delete-form' : '', + 'deleteConfirm' => t('Delete this favicon?'), + ]; + require templatePath('partials/app-file-upload.phtml'); + ?> + +
+ +
+ +
+
>
diff --git a/pages/admin/tenants/edit(default).phtml b/pages/admin/tenants/edit(default).phtml index 5544936..40c54da 100644 --- a/pages/admin/tenants/edit(default).phtml +++ b/pages/admin/tenants/edit(default).phtml @@ -77,6 +77,14 @@ $hasFavicon = $tenantUuid !== '' && $tenantFaviconService->hasFavicon($tenantUui + + hasFavicon($tenantUui


- -
- - - -
- -
-
- 'favicon', - 'accept' => 'image/png', - 'hint' => t('Allowed file types: PNG'), - 'currentSrc' => $hasFavicon ? asset('favicon/tenants/' . $tenantUuid . '/favicon/favicon-32x32.png') : '', - 'currentLabel' => t('Favicon'), - 'deleteAction' => $hasFavicon ? 'admin/tenants/favicon-delete/' . $tenantUuid : '', - ]; - require templatePath('partials/app-file-upload.phtml'); - ?> - - -
-
- -
-
data-current-src="" data-current-label=""> +
data-current-src="">
- + <?php e($uploadCurrentLabel); ?>
- + diff --git a/templates/partials/tenant-logo-upload.phtml b/templates/partials/tenant-logo-upload.phtml index dcce165..5912ecc 100644 --- a/templates/partials/tenant-logo-upload.phtml +++ b/templates/partials/tenant-logo-upload.phtml @@ -22,7 +22,7 @@ $previewUrl = $hasLogo : ''; ?>
- + uses the standard form-label rule (block, + margin-bottom, --app-color, text-sm) so it matches other inputs. */ .tenant-logo-slot { display: flex; flex-direction: column; - gap: 0.5rem; min-width: 0; } - .tenant-logo-slot-label { - font-size: var(--text-sm); - color: var(--app-muted-color); - margin: 0; - } - .tenant-logo-actions { display: flex; justify-content: flex-end; diff --git a/web/js/components/app-file-upload.js b/web/js/components/app-file-upload.js index ee523ff..403ec12 100644 --- a/web/js/components/app-file-upload.js +++ b/web/js/components/app-file-upload.js @@ -68,27 +68,16 @@ export function initFileUpload(root) { const filenameEl = root.querySelector('.app-file-upload-filename'); const filesizeEl = root.querySelector('.app-file-upload-filesize'); const clearButton = root.querySelector('.app-file-upload-clear'); - const currentImage = root.querySelector('.app-file-upload-current-image'); - const currentLabel = root.querySelector('.app-file-upload-current-label'); const replaceButton = root.querySelector('.app-file-upload-replace-button'); + const currentImage = root.querySelector('.app-file-upload-current-image'); const cleanupFns = []; let dragCounter = 0; - // ── Initialize current file state from data attributes ── + // Server rendered the initial state (img src + label + has-current class). + // `data-current-src` is kept so clearFile() can restore the server file + // after a user previewed a different selection. const currentSrc = (root.dataset.currentSrc || '').trim(); - const currentLabelText = (root.dataset.currentLabel || '').trim(); - - if (currentSrc) { - root.classList.add('has-current'); - if (currentImage) { - currentImage.src = currentSrc; - currentImage.alt = currentLabelText; - } - if (currentLabel) { - currentLabel.textContent = currentLabelText; - } - } // ── Show pending file (new selection) ── const showFile = (file) => { @@ -175,15 +164,19 @@ export function initFileUpload(root) { cleanupFns.push(() => clearButton.removeEventListener('click', onClear)); } - // ── Replace button → open file dialog ── + // ── Replace button + click on current preview → open file dialog ── + const openPicker = (e) => { + e.preventDefault(); + e.stopPropagation(); + input.click(); + }; if (replaceButton) { - const onReplace = (e) => { - e.preventDefault(); - e.stopPropagation(); - input.click(); - }; - replaceButton.addEventListener('click', onReplace); - cleanupFns.push(() => replaceButton.removeEventListener('click', onReplace)); + replaceButton.addEventListener('click', openPicker); + cleanupFns.push(() => replaceButton.removeEventListener('click', openPicker)); + } + if (currentImage) { + currentImage.addEventListener('click', openPicker); + cleanupFns.push(() => currentImage.removeEventListener('click', openPicker)); } // ── Drag and drop ──