auth ui: anon tooltip = dates only; move login button to header top-right
- timeline: RenderFlags.managerView drives the tooltip. Anonymous tier shows a minimal tooltip (date range + duration only) — no title, task №, status, manager or Planfix hint. Manager tier keeps the rich tooltip. - header: login/logout button pinned to the top-right corner (.auth-wrap, absolute) instead of sitting next to the view-mode switch. Keycloak: register bare origin in post.logout.redirect.uris so keycloak-js logout (redirectUri without trailing path) is accepted.
This commit is contained in:
parent
b6b9c27cb0
commit
1eda6c1e9d
@ -33,7 +33,6 @@
|
|||||||
<button type="button" class="mode-btn" id="mode-split">Карта и график</button>
|
<button type="button" class="mode-btn" id="mode-split">Карта и график</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="theme-toggle" id="theme-toggle" title="Переключить тему" aria-label="Переключить тему"></button>
|
<button type="button" class="theme-toggle" id="theme-toggle" title="Переключить тему" aria-label="Переключить тему"></button>
|
||||||
<button type="button" class="auth-btn" id="auth-btn" style="display:none">Войти</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Group 2: fields (4-column grid; dates + search on the 2nd row) -->
|
<!-- Group 2: fields (4-column grid; dates + search on the 2nd row) -->
|
||||||
@ -123,6 +122,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Login / logout button, pinned to the header's top-right corner -->
|
||||||
|
<div class="auth-wrap">
|
||||||
|
<button type="button" class="auth-btn" id="auth-btn" style="display:none">Войти</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Appearance ("+") button, pinned to the header's bottom-right corner -->
|
<!-- Appearance ("+") button, pinned to the header's bottom-right corner -->
|
||||||
<div class="decor-wrap">
|
<div class="decor-wrap">
|
||||||
<button type="button" class="decor-btn" id="decor-btn" aria-label="Оформление" data-tip="Оформление">+</button>
|
<button type="button" class="decor-btn" id="decor-btn" aria-label="Оформление" data-tip="Оформление">+</button>
|
||||||
|
|||||||
@ -130,12 +130,13 @@ function flags(): RenderFlags {
|
|||||||
// Anonymous tier: no labels (data is blanked anyway) and no collision
|
// Anonymous tier: no labels (data is blanked anyway) and no collision
|
||||||
// highlighting, so every bar renders in the single neutral status colour.
|
// highlighting, so every bar renders in the single neutral status colour.
|
||||||
if (!isManager()) {
|
if (!isManager()) {
|
||||||
return { withBrand: false, withCompany: false, withCollisions: false };
|
return { withBrand: false, withCompany: false, withCollisions: false, managerView: false };
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
withBrand: el.showBrand.checked,
|
withBrand: el.showBrand.checked,
|
||||||
withCompany: el.showCompany.checked,
|
withCompany: el.showCompany.checked,
|
||||||
withCollisions: el.showCollisions.checked,
|
withCollisions: el.showCollisions.checked,
|
||||||
|
managerView: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -313,6 +313,9 @@ input::placeholder { color: var(--text-muted); }
|
|||||||
}
|
}
|
||||||
.theme-toggle:hover { border-color: var(--border-strong); background: var(--surface); }
|
.theme-toggle:hover { border-color: var(--border-strong); background: var(--surface); }
|
||||||
|
|
||||||
|
/* Login / logout button — pinned to the header's top-right corner */
|
||||||
|
.auth-wrap { position: absolute; right: 18px; top: 12px; z-index: 3; }
|
||||||
|
|
||||||
/* Login / logout button (mirrors the toggle styling, sized to its text label) */
|
/* Login / logout button (mirrors the toggle styling, sized to its text label) */
|
||||||
.auth-btn {
|
.auth-btn {
|
||||||
border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg);
|
border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg);
|
||||||
|
|||||||
@ -11,6 +11,9 @@ export interface RenderFlags {
|
|||||||
withBrand: boolean;
|
withBrand: boolean;
|
||||||
withCompany: boolean;
|
withCompany: boolean;
|
||||||
withCollisions: boolean;
|
withCollisions: boolean;
|
||||||
|
// Manager tier: show the rich tooltip (title, task №, status, manager, Planfix
|
||||||
|
// hint). Anonymous tier gets a minimal tooltip with only the date range.
|
||||||
|
managerView: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TimelineElements {
|
export interface TimelineElements {
|
||||||
@ -81,7 +84,7 @@ export function createTimelineView(el: TimelineElements, appearance: Appearance)
|
|||||||
const itemsDS = new DataSet<any>();
|
const itemsDS = new DataSet<any>();
|
||||||
let lastBoards: Board[] = [];
|
let lastBoards: Board[] = [];
|
||||||
let lastBookings: Booking[] = [];
|
let lastBookings: Booking[] = [];
|
||||||
let lastFlags: RenderFlags = { withBrand: true, withCompany: false, withCollisions: true };
|
let lastFlags: RenderFlags = { withBrand: true, withCompany: false, withCollisions: true, managerView: true };
|
||||||
let onBoardClick: ((boardId: string) => void) | null = null;
|
let onBoardClick: ((boardId: string) => void) | null = null;
|
||||||
|
|
||||||
const options: TimelineOptions = {
|
const options: TimelineOptions = {
|
||||||
@ -315,11 +318,17 @@ export function createTimelineView(el: TimelineElements, appearance: Appearance)
|
|||||||
const statusColor = colorForStatus(appearance, bk.task_status);
|
const statusColor = colorForStatus(appearance, bk.task_status);
|
||||||
const color = isCollision ? appearance.collisionColor : statusColor;
|
const color = isCollision ? appearance.collisionColor : statusColor;
|
||||||
|
|
||||||
// ---- rich hover tooltip (structured HTML; values escaped) ----
|
// ---- hover tooltip (structured HTML; values escaped) ----
|
||||||
const esc = escapeHtml;
|
const esc = escapeHtml;
|
||||||
|
const dur = durationLabel(bk.start_date, bk.end_date);
|
||||||
|
const datesRow = `<div class="tt-row"><span class="tt-ico">📅</span><span>${fmtDate(bk.start_date)} – ${fmtDate(bk.end_date)}${dur ? ' · ' + esc(dur) : ''}</span></div>`;
|
||||||
|
let tooltipHtml: string;
|
||||||
|
if (!flags.managerView) {
|
||||||
|
// Anonymous tier: date range only — no title, task №, status or Planfix hint.
|
||||||
|
tooltipHtml = `<div class="tt" style="--tt-accent:${color}">${datesRow}</div>`;
|
||||||
|
} else {
|
||||||
const title = brand || company || 'Без названия';
|
const title = brand || company || 'Без названия';
|
||||||
const managers = (bk.manager || []).filter(Boolean).join(', ');
|
const managers = (bk.manager || []).filter(Boolean).join(', ');
|
||||||
const dur = durationLabel(bk.start_date, bk.end_date);
|
|
||||||
const tt: string[] = [];
|
const tt: string[] = [];
|
||||||
tt.push('<div class="tt-head">');
|
tt.push('<div class="tt-head">');
|
||||||
tt.push(`<span class="tt-title">${esc(title)}</span>`);
|
tt.push(`<span class="tt-title">${esc(title)}</span>`);
|
||||||
@ -327,7 +336,7 @@ export function createTimelineView(el: TimelineElements, appearance: Appearance)
|
|||||||
tt.push('</div>');
|
tt.push('</div>');
|
||||||
if (brand && company) tt.push(`<div class="tt-sub">${esc(company)}</div>`);
|
if (brand && company) tt.push(`<div class="tt-sub">${esc(company)}</div>`);
|
||||||
tt.push('<div class="tt-sep"></div>');
|
tt.push('<div class="tt-sep"></div>');
|
||||||
tt.push(`<div class="tt-row"><span class="tt-ico">📅</span><span>${fmtDate(bk.start_date)} – ${fmtDate(bk.end_date)}${dur ? ' · ' + esc(dur) : ''}</span></div>`);
|
tt.push(datesRow);
|
||||||
if (bk.task_status) tt.push(`<div class="tt-row"><span class="tt-dot" style="background:${statusColor}"></span><span>${esc(bk.task_status)}</span></div>`);
|
if (bk.task_status) tt.push(`<div class="tt-row"><span class="tt-dot" style="background:${statusColor}"></span><span>${esc(bk.task_status)}</span></div>`);
|
||||||
if (managers) tt.push(`<div class="tt-row"><span class="tt-ico">👤</span><span>${esc(managers)}</span></div>`);
|
if (managers) tt.push(`<div class="tt-row"><span class="tt-ico">👤</span><span>${esc(managers)}</span></div>`);
|
||||||
if (isCollision) {
|
if (isCollision) {
|
||||||
@ -340,7 +349,8 @@ export function createTimelineView(el: TimelineElements, appearance: Appearance)
|
|||||||
tt.push('</div>');
|
tt.push('</div>');
|
||||||
}
|
}
|
||||||
tt.push('<div class="tt-hint">↗ Клик — открыть в ПланФикс</div>');
|
tt.push('<div class="tt-hint">↗ Клик — открыть в ПланФикс</div>');
|
||||||
const tooltipHtml = `<div class="tt" style="--tt-accent:${color}">${tt.join('')}</div>`;
|
tooltipHtml = `<div class="tt" style="--tt-accent:${color}">${tt.join('')}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: bk.board_id + '__' + idx,
|
id: bk.board_id + '__' + idx,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user