diff --git a/frontend/src/components/chat/ChatMessage.tsx b/frontend/src/components/chat/ChatMessage.tsx index 9d0a66a..f157c70 100644 --- a/frontend/src/components/chat/ChatMessage.tsx +++ b/frontend/src/components/chat/ChatMessage.tsx @@ -1,4 +1,5 @@ import React, { useRef, useState } from 'react'; +import Avatar from '@mui/material/Avatar'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import Paper from '@mui/material/Paper'; @@ -11,6 +12,18 @@ import RichMessageText from './RichMessageText'; import PollMessageContent from './PollMessageContent'; import MessageReactions from './MessageReactions'; +const SENDER_COLORS = [ + '#E53935', '#D81B60', '#8E24AA', '#5E35B1', + '#3949AB', '#1E88E5', '#00ACC1', '#00897B', + '#43A047', '#F4511E', +]; + +function getSenderColor(actorId: string): string { + let hash = 0; + for (let i = 0; i < actorId.length; i++) hash = ((hash << 5) - hash + actorId.charCodeAt(i)) | 0; + return SENDER_COLORS[Math.abs(hash) % SENDER_COLORS.length]; +} + interface ChatMessageProps { message: NextcloudMessage; isOwnMessage: boolean; @@ -126,9 +139,14 @@ const ChatMessage: React.FC = ({ message, isOwnMessage, isOneT }} > {!isOwnMessage && !isOneToOne && ( - - {message.actorDisplayName} - + + + {message.actorDisplayName.charAt(0).toUpperCase()} + + + {message.actorDisplayName} + + )} {/* Quoted parent message */} @@ -143,7 +161,7 @@ const ChatMessage: React.FC = ({ message, isOwnMessage, isOneT py: 0.25, bgcolor: isOwnMessage ? 'rgba(0,0,0,0.1)' : 'rgba(0,0,0,0.04)', }}> - + {message.parent.actorDisplayName} diff --git a/frontend/src/pages/MitgliedDetail.tsx b/frontend/src/pages/MitgliedDetail.tsx index 4c651e2..0a24535 100644 --- a/frontend/src/pages/MitgliedDetail.tsx +++ b/frontend/src/pages/MitgliedDetail.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { Container, Box, @@ -250,6 +250,13 @@ function MitgliedDetail() { // Edit form state — only the fields the user is allowed to change const [formData, setFormData] = useState({}); + // Merge Führerscheinklassen from profile + Fahrgenehmigungen (FDISK) + const displayedFuehrerscheinklassen = useMemo(() => { + const fromFG = fahrgenehmigungen.map(f => f.klasse).filter(Boolean); + const fromProfile = profile?.fuehrerscheinklassen ?? []; + return [...new Set([...fromProfile, ...fromFG])].sort(); + }, [fahrgenehmigungen, profile?.fuehrerscheinklassen]); + // ---------------------------------------------------------------- // Data loading // ---------------------------------------------------------------- @@ -305,7 +312,7 @@ function MitgliedDetail() { telefon_privat: member.profile.telefon_privat ?? undefined, notfallkontakt_name: member.profile.notfallkontakt_name ?? undefined, notfallkontakt_telefon: member.profile.notfallkontakt_telefon ?? undefined, - fuehrerscheinklassen: member.profile.fuehrerscheinklassen, + fuehrerscheinklassen: displayedFuehrerscheinklassen, tshirt_groesse: member.profile.tshirt_groesse ?? undefined, schuhgroesse: member.profile.schuhgroesse ?? undefined, bemerkungen: member.profile.bemerkungen ?? undefined, @@ -374,7 +381,7 @@ function MitgliedDetail() { telefon_privat: member.profile.telefon_privat ?? undefined, notfallkontakt_name: member.profile.notfallkontakt_name ?? undefined, notfallkontakt_telefon: member.profile.notfallkontakt_telefon ?? undefined, - fuehrerscheinklassen: member.profile.fuehrerscheinklassen, + fuehrerscheinklassen: displayedFuehrerscheinklassen, tshirt_groesse: member.profile.tshirt_groesse ?? undefined, schuhgroesse: member.profile.schuhgroesse ?? undefined, bemerkungen: member.profile.bemerkungen ?? undefined, @@ -869,9 +876,9 @@ function MitgliedDetail() { )} size="small" /> - ) : profile?.fuehrerscheinklassen && profile.fuehrerscheinklassen.length > 0 ? ( + ) : displayedFuehrerscheinklassen.length > 0 ? ( - {profile.fuehrerscheinklassen.map((k) => ( + {displayedFuehrerscheinklassen.map((k) => ( ))}