diff --git a/backend/routers/local_router.py b/backend/routers/local_router.py index 7144c8b..a4a9a5d 100644 --- a/backend/routers/local_router.py +++ b/backend/routers/local_router.py @@ -105,16 +105,36 @@ def list_calendars( .filter(models.CalendarShare.user_id == current_user.id) .all() ) + seen_ids = {c.id for c in own} for share in shares: cal = share.calendar - if cal is None: + if cal is None or cal.id in seen_ids: continue + seen_ids.add(cal.id) owner = db.query(models.User).filter(models.User.id == cal.user_id).first() result.append(_cal_dict( cal, owned=False, shared_by=owner.username if owner else None, permission=share.permission, )) + + # Group calendars the user can reach via membership (read_write), so members + # can select the group calendar in the editor and see it in their list. + group_cals = ( + db.query(models.LocalCalendar, models.Group.name) + .join(models.GroupCalendar, models.GroupCalendar.calendar_id == models.LocalCalendar.id) + .join(models.Group, models.Group.id == models.GroupCalendar.group_id) + .join(models.GroupMember, models.GroupMember.group_id == models.GroupCalendar.group_id) + .filter(models.GroupMember.user_id == current_user.id) + .all() + ) + for cal, group_name in group_cals: + if cal.id in seen_ids: + continue + seen_ids.add(cal.id) + d = _cal_dict(cal, owned=False, shared_by=group_name, permission="read_write") + d["group"] = True + result.append(d) return result diff --git a/backend/tests/test_collaboration.py b/backend/tests/test_collaboration.py index 740cc24..4f3eb45 100644 --- a/backend/tests/test_collaboration.py +++ b/backend/tests/test_collaboration.py @@ -134,6 +134,19 @@ def test_group_members_can_write_group_calendar(client): assert r.status_code == 200, r.text +def test_group_calendar_listed_for_member(client): + admin = register_admin(client) + b_id, b_tok = create_user(client, admin, "bob") + group = client.post("/api/groups/", headers=auth(admin), + json={"name": "Team", "member_ids": [b_id]}).json() + gcal = group["group_calendar_id"] + # Bob (member, not owner) sees the group calendar in his local list, flagged. + cals = client.get("/api/local/calendars", headers=auth(b_tok)).json() + gc = [c for c in cals if c["id"] == gcal] + assert gc and gc[0].get("group") is True + assert gc[0]["permission"] == "read_write" and gc[0]["owned"] is False + + def test_combined_view_marks_owner_and_group_event(client): admin = register_admin(client) b_id, b_tok = create_user(client, admin, "bob") diff --git a/frontend/css/app.css b/frontend/css/app.css index e519df7..0072ec8 100644 --- a/frontend/css/app.css +++ b/frontend/css/app.css @@ -1801,3 +1801,26 @@ a { color: var(--primary); text-decoration: none; } color: var(--text-2); font-style: italic; } + +/* ── Groups ─────────────────────────────────────────────────── */ +.group-view-banner { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 8px 16px; + background: rgba(66, 133, 244, 0.12); + border-bottom: 1px solid var(--border); + font-size: 14px; + color: var(--text-1); +} +.group-item-active { + background: rgba(66, 133, 244, 0.15); + border-radius: 8px; +} +.group-item .cal-item-name { cursor: pointer; flex: 1; } +.cal-list-empty { + padding: 6px 4px; + font-size: 13px; + color: var(--text-3); +} diff --git a/frontend/index.html b/frontend/index.html index df32297..a1f3bf8 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -198,6 +198,17 @@
+ + +
+
+ Gruppen + +
+
+
@@ -205,6 +216,10 @@
+
@@ -349,6 +364,32 @@ + + +