update icon event preview modal
This commit is contained in:
@@ -116,6 +116,7 @@ function ResponsiveDialog({
|
|||||||
}: ResponsiveDialogProps) {
|
}: ResponsiveDialogProps) {
|
||||||
const baseSx: SxProps<Theme> = {
|
const baseSx: SxProps<Theme> = {
|
||||||
"& .MuiBackdrop-root": {
|
"& .MuiBackdrop-root": {
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.1)",
|
||||||
opacity: isExpanded ? "0 !important" : undefined,
|
opacity: isExpanded ? "0 !important" : undefined,
|
||||||
transition: isExpanded ? "none !important" : undefined,
|
transition: isExpanded ? "none !important" : undefined,
|
||||||
pointerEvents: isExpanded ? "none" : undefined,
|
pointerEvents: isExpanded ? "none" : undefined,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ type InfoRowProps = {
|
|||||||
data?: string; // optional link target
|
data?: string; // optional link target
|
||||||
content?: React.ReactNode; // if provided, overrides text rendering
|
content?: React.ReactNode; // if provided, overrides text rendering
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
|
alignItems?: React.CSSProperties["alignItems"];
|
||||||
};
|
};
|
||||||
|
|
||||||
function detectUrls(text: string) {
|
function detectUrls(text: string) {
|
||||||
@@ -62,12 +63,13 @@ export function InfoRow({
|
|||||||
data,
|
data,
|
||||||
content,
|
content,
|
||||||
style,
|
style,
|
||||||
|
alignItems = "center",
|
||||||
}: InfoRowProps) {
|
}: InfoRowProps) {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems,
|
||||||
gap: 1,
|
gap: 1,
|
||||||
marginBottom: 1,
|
marginBottom: 1,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -13,6 +13,13 @@ import { Avatar, Badge, Box, Typography } from "@linagora/twake-mui";
|
|||||||
import CancelIcon from "@mui/icons-material/Cancel";
|
import CancelIcon from "@mui/icons-material/Cancel";
|
||||||
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
|
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
|
||||||
|
|
||||||
|
const EMAIL_DISPLAY_MAX_LENGTH = 50;
|
||||||
|
|
||||||
|
function truncateDisplayText(text: string, maxLength: number): string {
|
||||||
|
if (text.length <= maxLength) return text;
|
||||||
|
return `${text.slice(0, maxLength)}...`;
|
||||||
|
}
|
||||||
|
|
||||||
export function renderAttendeeBadge(
|
export function renderAttendeeBadge(
|
||||||
a: userAttendee,
|
a: userAttendee,
|
||||||
key: string,
|
key: string,
|
||||||
@@ -65,15 +72,11 @@ export function renderAttendeeBadge(
|
|||||||
<Avatar {...stringAvatar(a.cn || a.cal_address)} />
|
<Avatar {...stringAvatar(a.cn || a.cal_address)} />
|
||||||
</Badge>
|
</Badge>
|
||||||
<Box style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
|
<Box style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
|
||||||
<Typography
|
<Typography noWrap>
|
||||||
noWrap
|
{truncateDisplayText(
|
||||||
style={{
|
a.cn || a.cal_address,
|
||||||
maxWidth: "180px",
|
EMAIL_DISPLAY_MAX_LENGTH
|
||||||
overflow: "hidden",
|
)}
|
||||||
textOverflow: "ellipsis",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{a.cn || a.cal_address}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
{isOrganizer && (
|
{isOrganizer && (
|
||||||
<Typography variant="caption" color="text.secondary">
|
<Typography variant="caption" color="text.secondary">
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
|
useTheme,
|
||||||
} from "@linagora/twake-mui";
|
} from "@linagora/twake-mui";
|
||||||
|
import { alpha } from "@mui/material/styles";
|
||||||
import CalendarTodayIcon from "@mui/icons-material/CalendarToday";
|
import CalendarTodayIcon from "@mui/icons-material/CalendarToday";
|
||||||
import CircleIcon from "@mui/icons-material/Circle";
|
import CircleIcon from "@mui/icons-material/Circle";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
@@ -73,6 +75,9 @@ export default function EventPreviewModal({
|
|||||||
: calendars.list[calId];
|
: calendars.list[calId];
|
||||||
const event = calendar.events[eventId];
|
const event = calendar.events[eventId];
|
||||||
const user = useAppSelector((state) => state.user.userData);
|
const user = useAppSelector((state) => state.user.userData);
|
||||||
|
const theme = useTheme();
|
||||||
|
const infoIconColor = alpha(theme.palette.grey[900], 0.9);
|
||||||
|
const infoIconSx = { minWidth: "25px", marginRight: 2, color: infoIconColor };
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
|
|
||||||
const isRecurring = event?.uid?.includes("/");
|
const isRecurring = event?.uid?.includes("/");
|
||||||
@@ -451,10 +456,10 @@ export default function EventPreviewModal({
|
|||||||
title={t("eventPreview.privateEvent.tooltipOwn")}
|
title={t("eventPreview.privateEvent.tooltipOwn")}
|
||||||
placement="top"
|
placement="top"
|
||||||
>
|
>
|
||||||
<LockOutlineIcon />
|
<LockOutlineIcon sx={{ color: infoIconColor }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<LockOutlineIcon />
|
<LockOutlineIcon sx={{ color: infoIconColor }} />
|
||||||
))}
|
))}
|
||||||
<Typography
|
<Typography
|
||||||
variant="h5"
|
variant="h5"
|
||||||
@@ -488,8 +493,9 @@ export default function EventPreviewModal({
|
|||||||
{/* Video */}
|
{/* Video */}
|
||||||
{event.x_openpass_videoconference && (
|
{event.x_openpass_videoconference && (
|
||||||
<InfoRow
|
<InfoRow
|
||||||
|
alignItems="flex-start"
|
||||||
icon={
|
icon={
|
||||||
<Box sx={{ minWidth: "25px", marginRight: 2 }}>
|
<Box sx={{ ...infoIconSx, mt: 1 }}>
|
||||||
<VideocamOutlinedIcon />
|
<VideocamOutlinedIcon />
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
@@ -511,8 +517,9 @@ export default function EventPreviewModal({
|
|||||||
{attendees?.length > 0 && (
|
{attendees?.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<InfoRow
|
<InfoRow
|
||||||
|
alignItems="flex-start"
|
||||||
icon={
|
icon={
|
||||||
<Box sx={{ minWidth: "25px", marginRight: 2 }}>
|
<Box sx={{ ...infoIconSx, mt: 1 }}>
|
||||||
<PeopleAltOutlinedIcon />
|
<PeopleAltOutlinedIcon />
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
@@ -585,8 +592,9 @@ export default function EventPreviewModal({
|
|||||||
{/* Location */}
|
{/* Location */}
|
||||||
{event.location && (
|
{event.location && (
|
||||||
<InfoRow
|
<InfoRow
|
||||||
|
alignItems="flex-start"
|
||||||
icon={
|
icon={
|
||||||
<Box sx={{ minWidth: "25px", marginRight: 2 }}>
|
<Box sx={infoIconSx}>
|
||||||
<LocationOnOutlinedIcon />
|
<LocationOnOutlinedIcon />
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
@@ -600,8 +608,9 @@ export default function EventPreviewModal({
|
|||||||
{/* Description */}
|
{/* Description */}
|
||||||
{event.description && (
|
{event.description && (
|
||||||
<InfoRow
|
<InfoRow
|
||||||
|
alignItems="flex-start"
|
||||||
icon={
|
icon={
|
||||||
<Box sx={{ minWidth: "25px", marginRight: 2 }}>
|
<Box sx={infoIconSx}>
|
||||||
<SubjectIcon />
|
<SubjectIcon />
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
@@ -615,8 +624,9 @@ export default function EventPreviewModal({
|
|||||||
{/* ALARM */}
|
{/* ALARM */}
|
||||||
{event.alarm && (
|
{event.alarm && (
|
||||||
<InfoRow
|
<InfoRow
|
||||||
|
alignItems="flex-start"
|
||||||
icon={
|
icon={
|
||||||
<Box sx={{ minWidth: "25px", marginRight: 2 }}>
|
<Box sx={infoIconSx}>
|
||||||
<NotificationsNoneIcon />
|
<NotificationsNoneIcon />
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
@@ -641,8 +651,9 @@ export default function EventPreviewModal({
|
|||||||
{/* Repetition */}
|
{/* Repetition */}
|
||||||
{event.repetition && (
|
{event.repetition && (
|
||||||
<InfoRow
|
<InfoRow
|
||||||
|
alignItems="flex-start"
|
||||||
icon={
|
icon={
|
||||||
<Box sx={{ minWidth: "25px", marginRight: 2 }}>
|
<Box sx={infoIconSx}>
|
||||||
<RepeatIcon />
|
<RepeatIcon />
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
@@ -681,8 +692,9 @@ export default function EventPreviewModal({
|
|||||||
{/* Error */}
|
{/* Error */}
|
||||||
{event.error && (
|
{event.error && (
|
||||||
<InfoRow
|
<InfoRow
|
||||||
|
alignItems="flex-start"
|
||||||
icon={
|
icon={
|
||||||
<Box sx={{ minWidth: "25px", marginRight: 2 }}>
|
<Box sx={infoIconSx}>
|
||||||
<ErrorOutlineIcon color="error" />
|
<ErrorOutlineIcon color="error" />
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
@@ -698,12 +710,12 @@ export default function EventPreviewModal({
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "flex-start",
|
||||||
gap: 1,
|
gap: 1,
|
||||||
mb: 2,
|
mb: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ minWidth: "25px", marginRight: 2 }}>
|
<Box sx={infoIconSx}>
|
||||||
<CalendarTodayIcon />
|
<CalendarTodayIcon />
|
||||||
</Box>
|
</Box>
|
||||||
<CalendarName calendar={calendar} />
|
<CalendarName calendar={calendar} />
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ describe("videoConferenceUtils", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("addVideoConferenceToDescription", () => {
|
describe("addVideoConferenceToDescription", () => {
|
||||||
it("should add video conference footer to empty description", () => {
|
it("should add video conference on first line when description is empty", () => {
|
||||||
const description = "";
|
const description = "";
|
||||||
const meetingLink = "https://meet.linagora.com/abc-defg-hij";
|
const meetingLink = "https://meet.linagora.com/abc-defg-hij";
|
||||||
const result = addVideoConferenceToDescription(description, meetingLink);
|
const result = addVideoConferenceToDescription(description, meetingLink);
|
||||||
expect(result).toBe("\nVisio: https://meet.linagora.com/abc-defg-hij");
|
expect(result).toBe("Visio: https://meet.linagora.com/abc-defg-hij");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should add video conference footer to existing description", () => {
|
it("should add video conference footer to existing description", () => {
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ export function generateMeetingLink(baseUrl?: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add video conference footer to event description
|
* Add video conference footer to event description.
|
||||||
|
* If description is empty, adds on first line; otherwise adds on the line below existing content.
|
||||||
* @param {string} description - Original description
|
* @param {string} description - Original description
|
||||||
* @param {string} meetingLink - Generated meeting link
|
* @param {string} meetingLink - Generated meeting link
|
||||||
* @returns {string} Description with video conference footer
|
* @returns {string} Description with video conference footer
|
||||||
@@ -42,8 +43,9 @@ export function addVideoConferenceToDescription(
|
|||||||
description: string,
|
description: string,
|
||||||
meetingLink: string
|
meetingLink: string
|
||||||
): string {
|
): string {
|
||||||
const footer = `\nVisio: ${meetingLink}`;
|
const line = `Visio: ${meetingLink}`;
|
||||||
return description + footer;
|
const trimmed = description.trimEnd();
|
||||||
|
return trimmed ? `${trimmed}\n${line}` : line;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user