feat: wählbares Gruppen-Icon, geteilter Kalender markiert, Ersteller bei Gruppen-Terminen

- Gruppe: wählbares Emoji-Icon (groups.icon-Spalte + PUT /api/groups/{id});
  wird in der Sidebar statt des Zahnrads angezeigt; Verwalten jetzt klares "⋯".
  Gruppe umbenennen möglich (war vorher gesperrt).
- "Meine Kalender": der aktuell für Gruppen sichtbare Kalender wird mit 👥
  gekennzeichnet.
- Gruppenansicht: Gruppenkalender-Termine zeigen, wer sie hinzugefügt hat
  (👥 Vorname: Titel) und sind nach Ersteller eingefärbt; jeder kann weiterhin
  Termine im Gruppenkalender anlegen. Version v34.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-05-31 18:34:59 +02:00
parent 682f9613ec
commit 7429a309c3
8 changed files with 104 additions and 12 deletions

View File

@@ -175,6 +175,12 @@ def _migrate():
conn.commit()
except Exception:
pass
try:
conn.execute(text("ALTER TABLE groups ADD COLUMN icon VARCHAR(16)"))
conn.commit()
logging.info("Migration: added icon to groups")
except Exception:
pass
_migrate()

View File

@@ -268,6 +268,7 @@ class Group(Base):
id = Column(Integer, primary_key=True, index=True)
name = Column(String(100), nullable=False)
icon = Column(String(16), nullable=True) # emoji shown for the group
created_by = Column(Integer, ForeignKey("users.id"), nullable=False)
created_at = Column(String(50), nullable=True) # ISO 8601

View File

@@ -34,6 +34,12 @@ def _now_iso() -> str:
class GroupCreate(BaseModel):
name: str
member_ids: List[int] = []
icon: Optional[str] = None
class GroupUpdate(BaseModel):
name: Optional[str] = None
icon: Optional[str] = None
class MemberAdd(BaseModel):
@@ -86,7 +92,8 @@ def create_group(
db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user),
):
group = models.Group(name=data.name, created_by=current_user.id, created_at=_now_iso())
group = models.Group(name=data.name, icon=(data.icon or None),
created_by=current_user.id, created_at=_now_iso())
db.add(group)
db.flush()
@@ -135,6 +142,7 @@ def list_groups(
out.append({
"id": group.id,
"name": group.name,
"icon": group.icon,
"role": m.role,
"member_count": member_count,
"group_calendar_id": _group_calendar_id(db, group.id),
@@ -155,12 +163,30 @@ def _group_detail(db: Session, group: models.Group, current_user: models.User) -
return {
"id": group.id,
"name": group.name,
"icon": group.icon,
"created_by": group.created_by,
"members": member_dicts,
"group_calendar_id": _group_calendar_id(db, group.id),
}
@router.put("/{group_id}")
def update_group(
group_id: int,
data: GroupUpdate,
db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user),
):
group = _get_group_or_404(db, group_id)
_require_owner(db, group, current_user)
if data.name is not None and data.name.strip():
group.name = data.name.strip()
if data.icon is not None:
group.icon = data.icon or None
db.commit()
return _group_detail(db, group, current_user)
@router.get("/{group_id}")
def get_group(
group_id: int,