This commit is contained in:
Matthias Hochmeister
2026-03-16 16:26:56 +01:00
parent 177dd1395b
commit 69c508a5d8
2 changed files with 34 additions and 9 deletions

View File

@@ -1,4 +1,5 @@
import React, { useRef, useState } from 'react'; import React, { useRef, useState } from 'react';
import Avatar from '@mui/material/Avatar';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import Paper from '@mui/material/Paper'; import Paper from '@mui/material/Paper';
@@ -11,6 +12,18 @@ import RichMessageText from './RichMessageText';
import PollMessageContent from './PollMessageContent'; import PollMessageContent from './PollMessageContent';
import MessageReactions from './MessageReactions'; 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 { interface ChatMessageProps {
message: NextcloudMessage; message: NextcloudMessage;
isOwnMessage: boolean; isOwnMessage: boolean;
@@ -126,9 +139,14 @@ const ChatMessage: React.FC<ChatMessageProps> = ({ message, isOwnMessage, isOneT
}} }}
> >
{!isOwnMessage && !isOneToOne && ( {!isOwnMessage && !isOneToOne && (
<Typography variant="caption" sx={{ fontWeight: 600, display: 'block' }}> <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mb: 0.25 }}>
{message.actorDisplayName} <Avatar sx={{ width: 20, height: 20, fontSize: '0.7rem', bgcolor: getSenderColor(message.actorId) }}>
</Typography> {message.actorDisplayName.charAt(0).toUpperCase()}
</Avatar>
<Typography variant="body2" sx={{ fontWeight: 700, fontSize: '0.8rem', color: getSenderColor(message.actorId) }}>
{message.actorDisplayName}
</Typography>
</Box>
)} )}
{/* Quoted parent message */} {/* Quoted parent message */}
@@ -143,7 +161,7 @@ const ChatMessage: React.FC<ChatMessageProps> = ({ message, isOwnMessage, isOneT
py: 0.25, py: 0.25,
bgcolor: isOwnMessage ? 'rgba(0,0,0,0.1)' : 'rgba(0,0,0,0.04)', bgcolor: isOwnMessage ? 'rgba(0,0,0,0.1)' : 'rgba(0,0,0,0.04)',
}}> }}>
<Typography variant="caption" sx={{ display: 'block', fontWeight: 600, lineHeight: 1.4 }}> <Typography variant="caption" sx={{ display: 'block', fontWeight: 600, lineHeight: 1.4, color: !isOwnMessage ? getSenderColor(message.parent.actorId) : undefined }}>
{message.parent.actorDisplayName} {message.parent.actorDisplayName}
</Typography> </Typography>
<Typography variant="caption" noWrap sx={{ display: 'block', lineHeight: 1.4 }}> <Typography variant="caption" noWrap sx={{ display: 'block', lineHeight: 1.4 }}>

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback, useMemo } from 'react';
import { import {
Container, Container,
Box, Box,
@@ -250,6 +250,13 @@ function MitgliedDetail() {
// Edit form state — only the fields the user is allowed to change // Edit form state — only the fields the user is allowed to change
const [formData, setFormData] = useState<UpdateMemberProfileData>({}); const [formData, setFormData] = useState<UpdateMemberProfileData>({});
// 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 // Data loading
// ---------------------------------------------------------------- // ----------------------------------------------------------------
@@ -305,7 +312,7 @@ function MitgliedDetail() {
telefon_privat: member.profile.telefon_privat ?? undefined, telefon_privat: member.profile.telefon_privat ?? undefined,
notfallkontakt_name: member.profile.notfallkontakt_name ?? undefined, notfallkontakt_name: member.profile.notfallkontakt_name ?? undefined,
notfallkontakt_telefon: member.profile.notfallkontakt_telefon ?? undefined, notfallkontakt_telefon: member.profile.notfallkontakt_telefon ?? undefined,
fuehrerscheinklassen: member.profile.fuehrerscheinklassen, fuehrerscheinklassen: displayedFuehrerscheinklassen,
tshirt_groesse: member.profile.tshirt_groesse ?? undefined, tshirt_groesse: member.profile.tshirt_groesse ?? undefined,
schuhgroesse: member.profile.schuhgroesse ?? undefined, schuhgroesse: member.profile.schuhgroesse ?? undefined,
bemerkungen: member.profile.bemerkungen ?? undefined, bemerkungen: member.profile.bemerkungen ?? undefined,
@@ -374,7 +381,7 @@ function MitgliedDetail() {
telefon_privat: member.profile.telefon_privat ?? undefined, telefon_privat: member.profile.telefon_privat ?? undefined,
notfallkontakt_name: member.profile.notfallkontakt_name ?? undefined, notfallkontakt_name: member.profile.notfallkontakt_name ?? undefined,
notfallkontakt_telefon: member.profile.notfallkontakt_telefon ?? undefined, notfallkontakt_telefon: member.profile.notfallkontakt_telefon ?? undefined,
fuehrerscheinklassen: member.profile.fuehrerscheinklassen, fuehrerscheinklassen: displayedFuehrerscheinklassen,
tshirt_groesse: member.profile.tshirt_groesse ?? undefined, tshirt_groesse: member.profile.tshirt_groesse ?? undefined,
schuhgroesse: member.profile.schuhgroesse ?? undefined, schuhgroesse: member.profile.schuhgroesse ?? undefined,
bemerkungen: member.profile.bemerkungen ?? undefined, bemerkungen: member.profile.bemerkungen ?? undefined,
@@ -869,9 +876,9 @@ function MitgliedDetail() {
)} )}
size="small" size="small"
/> />
) : profile?.fuehrerscheinklassen && profile.fuehrerscheinklassen.length > 0 ? ( ) : displayedFuehrerscheinklassen.length > 0 ? (
<Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap' }}> <Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap' }}>
{profile.fuehrerscheinklassen.map((k) => ( {displayedFuehrerscheinklassen.map((k) => (
<Chip key={k} label={k} size="small" variant="outlined" /> <Chip key={k} label={k} size="small" variant="outlined" />
))} ))}
</Box> </Box>