Einige kleine verbesserungen #1
@@ -1840,6 +1840,32 @@ a { color: var(--primary); text-decoration: none; }
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--text-1);
|
color: var(--text-1);
|
||||||
}
|
}
|
||||||
|
/* Per-member filter bar under the group banner (hide individual people). */
|
||||||
|
.group-members-filter {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: color-mix(in srgb, var(--accent) 7%, var(--bg-app));
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
.gmf-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: var(--bg-surface);
|
||||||
|
color: var(--text-1);
|
||||||
|
font-size: 12.5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: opacity var(--transition), background var(--transition);
|
||||||
|
}
|
||||||
|
.gmf-chip:hover { background: var(--bg-hover); }
|
||||||
|
.gmf-chip.gmf-off { opacity: 0.4; text-decoration: line-through; }
|
||||||
|
.gmf-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
|
||||||
|
|
||||||
.group-item-active {
|
.group-item-active {
|
||||||
background: var(--bg-surface);
|
background: var(--bg-surface);
|
||||||
background: color-mix(in srgb, var(--accent) 18%, transparent);
|
background: color-mix(in srgb, var(--accent) 18%, transparent);
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ let state = {
|
|||||||
selectedEventColor: '', // '' = use calendar color
|
selectedEventColor: '', // '' = use calendar color
|
||||||
groups: [],
|
groups: [],
|
||||||
activeGroupId: null, // when set, the calendar shows the combined group view
|
activeGroupId: null, // when set, the calendar shows the combined group view
|
||||||
|
activeGroupDetail: null, // full detail (members + colours) of the active group
|
||||||
|
hiddenGroupMembers: new Set(), // member ids / 'gc' hidden in the group overlay
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── URL state ────────────────────────────────────────────
|
// ── URL state ────────────────────────────────────────────
|
||||||
@@ -456,6 +458,16 @@ function renderView() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function filterEvents(events) {
|
function filterEvents(events) {
|
||||||
|
// Group overlay: hide individual members' calendars / the group calendar
|
||||||
|
// (Outlook-style), filtered client-side by event owner.
|
||||||
|
const hidden = state.hiddenGroupMembers;
|
||||||
|
if (state.activeGroupId && hidden && hidden.size) {
|
||||||
|
return events.filter(ev => {
|
||||||
|
if (ev.is_group_event) return !hidden.has('gc');
|
||||||
|
const oid = ev.owner && ev.owner.id;
|
||||||
|
return oid == null || !hidden.has(oid);
|
||||||
|
});
|
||||||
|
}
|
||||||
// If dimPast is enabled, events are still shown but CSS handles opacity via .past class
|
// If dimPast is enabled, events are still shown but CSS handles opacity via .past class
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
@@ -2316,23 +2328,68 @@ function openGroupEventModal() {
|
|||||||
updatePrivateRow(false);
|
updatePrivateRow(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function enterGroupView(groupId) {
|
async function enterGroupView(groupId) {
|
||||||
state.activeGroupId = groupId;
|
state.activeGroupId = groupId;
|
||||||
|
state.hiddenGroupMembers = new Set();
|
||||||
|
state.activeGroupDetail = null;
|
||||||
const g = state.groups.find(x => x.id === groupId);
|
const g = state.groups.find(x => x.id === groupId);
|
||||||
document.getElementById('group-view-banner').classList.remove('hidden');
|
document.getElementById('group-view-banner').classList.remove('hidden');
|
||||||
document.getElementById('group-view-label').textContent =
|
document.getElementById('group-view-label').textContent =
|
||||||
t('group_view_label', { name: g ? g.name : '' });
|
t('group_view_label', { name: g ? g.name : '' });
|
||||||
renderGroupList();
|
renderGroupList();
|
||||||
|
// Load the member list (with server colours) for the per-member filter.
|
||||||
|
try { state.activeGroupDetail = await api.get(`/groups/${groupId}`); } catch (e) { /* ignore */ }
|
||||||
|
renderGroupMemberFilter();
|
||||||
fetchAndRender(true);
|
fetchAndRender(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function exitGroupView() {
|
function exitGroupView() {
|
||||||
state.activeGroupId = null;
|
state.activeGroupId = null;
|
||||||
|
state.activeGroupDetail = null;
|
||||||
|
state.hiddenGroupMembers = new Set();
|
||||||
document.getElementById('group-view-banner').classList.add('hidden');
|
document.getElementById('group-view-banner').classList.add('hidden');
|
||||||
|
renderGroupMemberFilter();
|
||||||
renderGroupList();
|
renderGroupList();
|
||||||
fetchAndRender(true);
|
fetchAndRender(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Per-member filter bar shown under the group banner: tap a member (or the
|
||||||
|
// group calendar) to hide/show their events in the combined overlay.
|
||||||
|
function renderGroupMemberFilter() {
|
||||||
|
const banner = document.getElementById('group-view-banner');
|
||||||
|
if (!banner) return;
|
||||||
|
let bar = document.getElementById('group-members-filter');
|
||||||
|
if (!state.activeGroupId || !state.activeGroupDetail) {
|
||||||
|
if (bar) bar.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!bar) {
|
||||||
|
bar = document.createElement('div');
|
||||||
|
bar.id = 'group-members-filter';
|
||||||
|
bar.className = 'group-members-filter';
|
||||||
|
banner.insertAdjacentElement('afterend', bar);
|
||||||
|
}
|
||||||
|
const g = state.activeGroupDetail;
|
||||||
|
const chip = (key, name, color) => {
|
||||||
|
const off = state.hiddenGroupMembers.has(key);
|
||||||
|
return `<button class="gmf-chip${off ? ' gmf-off' : ''}" data-gmf="${key}">
|
||||||
|
<span class="gmf-dot" style="background:${color || '#4285f4'}"></span>${escHtml(name)}</button>`;
|
||||||
|
};
|
||||||
|
const parts = (g.members || []).map(m => chip(String(m.id), m.display_name || '—', m.color));
|
||||||
|
parts.push(chip('gc', t('group_calendar'), g.group_calendar_color));
|
||||||
|
bar.innerHTML = parts.join('');
|
||||||
|
bar.querySelectorAll('[data-gmf]').forEach(b => {
|
||||||
|
b.addEventListener('click', () => {
|
||||||
|
const raw = b.dataset.gmf;
|
||||||
|
const key = raw === 'gc' ? 'gc' : parseInt(raw);
|
||||||
|
if (state.hiddenGroupMembers.has(key)) state.hiddenGroupMembers.delete(key);
|
||||||
|
else state.hiddenGroupMembers.add(key);
|
||||||
|
renderGroupMemberFilter();
|
||||||
|
renderView();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Open the group modal in create mode (no id) or manage mode (existing group).
|
// Open the group modal in create mode (no id) or manage mode (existing group).
|
||||||
async function openGroupModal(groupId) {
|
async function openGroupModal(groupId) {
|
||||||
const modal = document.getElementById('modal-group');
|
const modal = document.getElementById('modal-group');
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ const translations = {
|
|||||||
group_created: 'Gruppe erstellt',
|
group_created: 'Gruppe erstellt',
|
||||||
group_view_label: 'Gruppenansicht: {name}',
|
group_view_label: 'Gruppenansicht: {name}',
|
||||||
group_exit: 'Gruppenansicht verlassen',
|
group_exit: 'Gruppenansicht verlassen',
|
||||||
|
group_calendar: 'Gruppenkalender',
|
||||||
group_icon: 'Icon',
|
group_icon: 'Icon',
|
||||||
group_visible_flag: 'Für deine Gruppen sichtbar',
|
group_visible_flag: 'Für deine Gruppen sichtbar',
|
||||||
group_new_event: '+ Gruppentermin',
|
group_new_event: '+ Gruppentermin',
|
||||||
@@ -396,6 +397,7 @@ const translations = {
|
|||||||
group_created: 'Group created',
|
group_created: 'Group created',
|
||||||
group_view_label: 'Group view: {name}',
|
group_view_label: 'Group view: {name}',
|
||||||
group_exit: 'Exit group view',
|
group_exit: 'Exit group view',
|
||||||
|
group_calendar: 'Group calendar',
|
||||||
group_icon: 'Icon',
|
group_icon: 'Icon',
|
||||||
group_visible_flag: 'Visible to your groups',
|
group_visible_flag: 'Visible to your groups',
|
||||||
group_new_event: '+ Group event',
|
group_new_event: '+ Group event',
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
// Increment APP_VERSION with every code change
|
// Increment APP_VERSION with every code change
|
||||||
export const APP_VERSION = 'v41';
|
export const APP_VERSION = 'v42';
|
||||||
|
|||||||
Reference in New Issue
Block a user