mail + portal: самоподключение почты, веб-клиент и глобальный поиск
Сотрудник сам подключает ящик (autoconfig, вложения, пагинация списка, надёжный IMAP-поиск); в хедере работает /api/search по разделам и сущностям с ACL. Также устойчивость Mattermost-токена для чата. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+44
-7
@@ -516,18 +516,25 @@
|
||||
</button>
|
||||
|
||||
<!-- Поиск -->
|
||||
<div class="header-search" style="display:flex; align-items:center; gap:0.5rem; background:var(--color-muted); border:1px solid var(--color-border); border-radius:var(--radius); padding:0.4375rem 0.75rem; width:18rem;">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:var(--color-muted-foreground); flex-shrink:0;">
|
||||
<div class="header-search" id="header-search">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="header-search-icon">
|
||||
<circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/>
|
||||
</svg>
|
||||
<input type="text"
|
||||
<input type="search"
|
||||
id="portal-search-input"
|
||||
name="q"
|
||||
placeholder="{% trans 'Поиск...' %}"
|
||||
class="input"
|
||||
style="border:none; background:transparent; padding:0; box-shadow:none;"
|
||||
class="header-search-input"
|
||||
autocomplete="off"
|
||||
enterkeyhint="search"
|
||||
hx-get="/api/search"
|
||||
hx-trigger="keyup changed delay:300ms"
|
||||
hx-trigger="keyup changed delay:280ms, search, focus"
|
||||
hx-target="#search-results"
|
||||
name="q">
|
||||
hx-swap="innerHTML"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="search-results"
|
||||
aria-expanded="false">
|
||||
<div id="search-results" class="portal-search-results" hidden></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -849,6 +856,36 @@
|
||||
}
|
||||
setActiveNavItem();
|
||||
|
||||
(function initPortalSearch() {
|
||||
var wrap = document.getElementById('header-search');
|
||||
var input = document.getElementById('portal-search-input');
|
||||
var panel = document.getElementById('search-results');
|
||||
if (!wrap || !input || !panel) return;
|
||||
|
||||
function openSearch() {
|
||||
panel.hidden = false;
|
||||
input.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
function closeSearch() {
|
||||
panel.hidden = true;
|
||||
input.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
window.closePortalSearch = closeSearch;
|
||||
|
||||
input.addEventListener('focus', openSearch);
|
||||
document.addEventListener('click', function (e) {
|
||||
if (!wrap.contains(e.target)) closeSearch();
|
||||
});
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape') closeSearch();
|
||||
});
|
||||
document.body.addEventListener('htmx:afterSwap', function (e) {
|
||||
if (e.detail && e.detail.target && e.detail.target.id === 'search-results') {
|
||||
openSearch();
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
// Восстановление темы и фона
|
||||
var savedTheme = localStorage.getItem('theme') || 'light';
|
||||
document.querySelectorAll('.theme-option').forEach(function(btn) {
|
||||
|
||||
Reference in New Issue
Block a user