update normal mode event createa update modal

This commit is contained in:
lenhanphung
2026-01-26 17:49:48 +07:00
parent 1d6a597c46
commit da0dba423e
8 changed files with 579 additions and 208 deletions
+325 -185
View File
@@ -20,12 +20,16 @@ import {
ToggleButton,
ToggleButtonGroup,
Typography,
useTheme,
} from "@linagora/twake-mui";
import { alpha } from "@mui/material/styles";
import {
Close as DeleteIcon,
ContentCopy as CopyIcon,
Public as PublicIcon,
LocationOn as LocationIcon,
} from "@mui/icons-material";
import SquareRoundedIcon from "@mui/icons-material/SquareRounded";
import LockOutlineIcon from "@mui/icons-material/LockOutline";
import React from "react";
import { useI18n } from "twake-i18n";
@@ -35,6 +39,7 @@ import { SnackbarAlert } from "../Loading/SnackBarAlert";
import { TimezoneAutocomplete } from "../Timezone/TimezoneAutocomplete";
import { AddDescButton } from "./AddDescButton";
import { DateTimeFields } from "./components/DateTimeFields";
import { DateTimeSummary } from "./components/DateTimeSummary";
import { FieldWithLabel } from "./components/FieldWithLabel";
import RepeatEvent from "./EventRepeat";
import { useAllDayToggle } from "./hooks/useAllDayToggle";
@@ -156,6 +161,7 @@ export default function EventFormFields({
onHasEndDateChangedChange,
}: EventFormFieldsProps) {
const { t } = useI18n();
const theme = useTheme();
// Internal state for 4 separate fields
const [startDate, setStartDate] = React.useState("");
@@ -165,11 +171,28 @@ export default function EventFormFields({
// Track if user has manually changed end date in extended mode
const [hasEndDateChanged, setHasEndDateChanged] = React.useState(false);
// Track if user has clicked on datetime clickable section in normal mode
// Once clicked, normal mode will always show full fields until modal closes
const [hasClickedDateTimeSection, setHasClickedDateTimeSection] = React.useState(false);
// Track if user has clicked on location section in normal mode
const [hasClickedLocationSection, setHasClickedLocationSection] = React.useState(false);
// Track if user has clicked on calendar section in normal mode
const [hasClickedCalendarSection, setHasClickedCalendarSection] = React.useState(false);
// Track if user has clicked on video meeting section in normal mode
const [hasClickedVideoMeetingSection, setHasClickedVideoMeetingSection] = React.useState(false);
// Reset hasEndDateChanged when modal closes
// Reset hasEndDateChanged and hasClickedDateTimeSection when modal closes
React.useEffect(() => {
if (!isOpen) {
setHasEndDateChanged(false);
setHasClickedDateTimeSection(false);
setHasClickedLocationSection(false);
setHasClickedCalendarSection(false);
setHasClickedVideoMeetingSection(false);
}
}, [isOpen]);
@@ -255,6 +278,7 @@ export default function EventFormFields({
prevShowMoreRef.current = showMore;
}, [showMore, isOpen]);
// Sync start prop to startDate/startTime
React.useEffect(() => {
if (start) {
@@ -389,6 +413,12 @@ export default function EventFormFields({
setDescription(updatedDescription);
setHasVideoConference(true);
setMeetingLink(newMeetingLink);
// Open description field when adding video conference
setShowDescription(true);
// Track click on video meeting section in normal mode
if (!showMore) {
setHasClickedVideoMeetingSection(true);
}
};
const [openToast, setOpenToast] = React.useState(false);
@@ -421,7 +451,10 @@ export default function EventFormFields({
return (
<>
<FieldWithLabel label={t("event.form.title")} isExpanded={showMore}>
<FieldWithLabel
label={showMore ? t("event.form.title") : ""}
isExpanded={showMore}
>
<TextField
fullWidth
label=""
@@ -437,6 +470,224 @@ export default function EventFormFields({
/>
</FieldWithLabel>
<FieldWithLabel
label={
!showMore && !hasClickedDateTimeSection ? "" : t("event.form.dateTime")
}
isExpanded={showMore}
>
{!showMore && !hasClickedDateTimeSection ? (
<DateTimeSummary
startDate={startDate}
startTime={startTime}
endDate={endDate}
endTime={endTime}
allday={allday}
timezone={timezone}
repetition={repetition}
showEndDate={
allday ||
(hasEndDateChanged && startDate !== endDate) ||
(!allday && startDate !== endDate)
}
onClick={() => setHasClickedDateTimeSection(true)}
/>
) : (
<DateTimeFields
startDate={startDate}
startTime={startTime}
endDate={endDate}
endTime={endTime}
allday={allday}
showMore={showMore}
hasEndDateChanged={hasEndDateChanged}
validation={validation}
onStartDateChange={handleStartDateChange}
onStartTimeChange={handleStartTimeChange}
onEndDateChange={handleEndDateChange}
onEndTimeChange={handleEndTimeChange}
showEndDate={
showMore ||
allday ||
(hasEndDateChanged && startDate !== endDate) ||
(!showMore && !allday && startDate !== endDate)
}
onToggleEndDate={() => {}}
/>
)}
</FieldWithLabel>
{!(!showMore && !hasClickedDateTimeSection) && (
<FieldWithLabel label=" " isExpanded={showMore}>
<Box display="flex" gap={2} alignItems="center">
<FormControlLabel
control={
<Checkbox checked={allday} onChange={handleAllDayToggle} />
}
label={
<Typography variant="h6">{t("event.form.allDay")}</Typography>
}
/>
<FormControlLabel
control={
<Checkbox
checked={
showRepeat || (typeOfAction === "solo" && !!repetition?.freq)
}
disabled={typeOfAction === "solo"}
onChange={() => {
const newShowRepeat = !showRepeat;
setShowRepeat(newShowRepeat);
if (newShowRepeat) {
setRepetition({
freq: "daily",
interval: 1,
occurrences: 0,
endDate: "",
byday: null,
} as RepetitionObject);
} else {
setRepetition({
freq: "",
interval: 1,
occurrences: 0,
endDate: "",
byday: null,
} as RepetitionObject);
}
}}
/>
}
label={
<Typography variant="h6">{t("event.form.repeat")}</Typography>
}
/>
<TimezoneAutocomplete
value={timezone}
onChange={setTimezone}
zones={timezoneList.zones}
getTimezoneOffset={(tzName: string) =>
timezoneList.getTimezoneOffset(tzName, new Date(start))
}
showIcon={false}
width={240}
size="small"
placeholder={t("event.form.timezonePlaceholder")}
/>
</Box>
</FieldWithLabel>
)}
{!(!showMore && !hasClickedDateTimeSection) &&
(showRepeat || (typeOfAction === "solo" && repetition?.freq)) && (
<FieldWithLabel label=" " isExpanded={showMore}>
<RepeatEvent
repetition={repetition}
eventStart={new Date(start)}
setRepetition={setRepetition}
isOwn={typeOfAction !== "solo"}
/>
</FieldWithLabel>
)}
<FieldWithLabel label="" isExpanded={showMore}>
<AttendeeSelector
attendees={attendees}
setAttendees={setAttendees}
placeholder="Add guests"
inputSlot={(params) => <TextField {...params} size="small" />}
/>
</FieldWithLabel>
<FieldWithLabel
label={showMore ? t("event.form.videoMeeting") : ""}
isExpanded={showMore}
sx={
hasClickedVideoMeetingSection && !showMore
? {
marginTop: "0 !important",
"& > .MuiBox-root": {
marginTop: "0 !important",
},
}
: undefined
}
>
{!showMore ? (
<Button
startIcon={
<img src={iconCamera} alt="camera" width={24} height={24} />
}
onClick={handleAddVideoConference}
fullWidth
sx={{
justifyContent: "flex-start",
display: hasVideoConference ? "none" : "flex",
borderRadius: "4px",
color: alpha(theme.palette.grey[900], 0.9),
}}
>
{t("event.form.addVisioConference")}
</Button>
) : (
<Box display="flex" gap={1} alignItems="center">
<Button
startIcon={
<img src={iconCamera} alt="camera" width={24} height={24} />
}
onClick={handleAddVideoConference}
size="medium"
variant="contained"
color="secondary"
sx={{
borderRadius: "4px",
display: hasVideoConference ? "none" : "flex",
}}
>
{t("event.form.addVisioConference")}
</Button>
{hasVideoConference && meetingLink && (
<>
<Button
startIcon={
<img src={iconCamera} alt="camera" width={24} height={24} />
}
onClick={() => window.open(meetingLink, "_blank")}
size="medium"
variant="contained"
color="primary"
sx={{
borderRadius: "4px",
mr: 1,
}}
>
{t("event.form.joinVisioConference")}
</Button>
<IconButton
onClick={handleCopyMeetingLink}
size="small"
sx={{ color: "primary.main" }}
aria-label={t("event.form.copyMeetingLink")}
title={t("event.form.copyMeetingLink")}
>
<CopyIcon />
</IconButton>
<IconButton
onClick={handleDeleteVideoConference}
size="small"
sx={{ color: "error.main" }}
aria-label={t("event.form.removeVideoConference")}
title={t("event.form.removeVideoConference")}
>
<DeleteIcon />
</IconButton>
</>
)}
</Box>
)}
</FieldWithLabel>
<AddDescButton
showDescription={showDescription}
setShowDescription={setShowDescription}
@@ -447,199 +698,88 @@ export default function EventFormFields({
buttonColor="secondary"
/>
<FieldWithLabel label={t("event.form.dateTime")} isExpanded={showMore}>
<DateTimeFields
startDate={startDate}
startTime={startTime}
endDate={endDate}
endTime={endTime}
allday={allday}
showMore={showMore}
hasEndDateChanged={hasEndDateChanged}
validation={validation}
onStartDateChange={handleStartDateChange}
onStartTimeChange={handleStartTimeChange}
onEndDateChange={handleEndDateChange}
onEndTimeChange={handleEndTimeChange}
showEndDate={
showMore ||
allday ||
(hasEndDateChanged && startDate !== endDate) ||
(!showMore && !allday && startDate !== endDate)
}
onToggleEndDate={() => {}}
/>
<FieldWithLabel
label={
showMore || hasClickedLocationSection
? t("event.form.location")
: ""
}
isExpanded={showMore}
>
{!showMore && !hasClickedLocationSection ? (
<Button
startIcon={<LocationIcon />}
onClick={() => setHasClickedLocationSection(true)}
fullWidth
sx={{
justifyContent: "flex-start",
borderRadius: "4px",
color: alpha(theme.palette.grey[900], 0.9),
}}
>
{location || t("event.form.locationPlaceholder")}
</Button>
) : (
<TextField
fullWidth
label=""
inputProps={{ "aria-label": t("event.form.location") }}
placeholder={t("event.form.locationPlaceholder")}
value={location}
onChange={(e) => setLocation(e.target.value)}
size="small"
margin="dense"
/>
)}
</FieldWithLabel>
<FieldWithLabel label=" " isExpanded={showMore}>
<Box display="flex" gap={2} alignItems="center">
<FormControlLabel
control={
<Checkbox checked={allday} onChange={handleAllDayToggle} />
}
label={
<Typography variant="h6">{t("event.form.allDay")}</Typography>
}
/>
<FormControlLabel
control={
<Checkbox
checked={
showRepeat || (typeOfAction === "solo" && !!repetition?.freq)
}
disabled={typeOfAction === "solo"}
onChange={() => {
const newShowRepeat = !showRepeat;
setShowRepeat(newShowRepeat);
if (newShowRepeat) {
setRepetition({
freq: "daily",
interval: 1,
occurrences: 0,
endDate: "",
byday: null,
} as RepetitionObject);
} else {
setRepetition({
freq: "",
interval: 1,
occurrences: 0,
endDate: "",
byday: null,
} as RepetitionObject);
}
<FieldWithLabel
label={
showMore || hasClickedCalendarSection
? t("event.form.calendar")
: ""
}
isExpanded={showMore}
>
{!showMore && !hasClickedCalendarSection ? (
<Button
startIcon={
<SquareRoundedIcon
sx={{
color:
userPersonalCalendars.find((cal) => cal.id === calendarid)
?.color?.light ?? "#3788D8",
width: 24,
height: 24,
}}
/>
}
label={
<Typography variant="h6">{t("event.form.repeat")}</Typography>
}
/>
<TimezoneAutocomplete
value={timezone}
onChange={setTimezone}
zones={timezoneList.zones}
getTimezoneOffset={(tzName: string) =>
timezoneList.getTimezoneOffset(tzName, new Date(start))
}
showIcon={false}
width={240}
size="small"
placeholder={t("event.form.timezonePlaceholder")}
/>
</Box>
</FieldWithLabel>
{(showRepeat || (typeOfAction === "solo" && repetition?.freq)) && (
<FieldWithLabel label=" " isExpanded={showMore}>
<RepeatEvent
repetition={repetition}
eventStart={new Date(start)}
setRepetition={setRepetition}
isOwn={typeOfAction !== "solo"}
/>
</FieldWithLabel>
)}
<FieldWithLabel
label={t("event.form.participants")}
isExpanded={showMore}
>
<AttendeeSelector
attendees={attendees}
setAttendees={setAttendees}
inputSlot={(params) => <TextField {...params} size="small" />}
/>
</FieldWithLabel>
<FieldWithLabel
label={t("event.form.videoMeeting")}
isExpanded={showMore}
>
<Box display="flex" gap={1} alignItems="center">
<Button
startIcon={
<img src={iconCamera} alt="camera" width={24} height={24} />
}
onClick={handleAddVideoConference}
size="medium"
variant="contained"
color="secondary"
onClick={() => setHasClickedCalendarSection(true)}
fullWidth
sx={{
justifyContent: "flex-start",
borderRadius: "4px",
display: hasVideoConference ? "none" : "flex",
color: alpha(theme.palette.grey[900], 0.9),
}}
>
{t("event.form.addVisioConference")}
{userPersonalCalendars.find((cal) => cal.id === calendarid)?.name ||
t("event.form.calendar")}
</Button>
{hasVideoConference && meetingLink && (
<>
<Button
startIcon={
<img src={iconCamera} alt="camera" width={24} height={24} />
}
onClick={() => window.open(meetingLink, "_blank")}
size="medium"
variant="contained"
color="primary"
sx={{
borderRadius: "4px",
mr: 1,
}}
>
{t("event.form.joinVisioConference")}
</Button>
<IconButton
onClick={handleCopyMeetingLink}
size="small"
sx={{ color: "primary.main" }}
aria-label={t("event.form.copyMeetingLink")}
title={t("event.form.copyMeetingLink")}
>
<CopyIcon />
</IconButton>
<IconButton
onClick={handleDeleteVideoConference}
size="small"
sx={{ color: "error.main" }}
aria-label={t("event.form.removeVideoConference")}
title={t("event.form.removeVideoConference")}
>
<DeleteIcon />
</IconButton>
</>
)}
</Box>
</FieldWithLabel>
<FieldWithLabel label={t("event.form.location")} isExpanded={showMore}>
<TextField
fullWidth
label=""
inputProps={{ "aria-label": t("event.form.location") }}
placeholder={t("event.form.locationPlaceholder")}
value={location}
onChange={(e) => setLocation(e.target.value)}
size="small"
margin="dense"
/>
</FieldWithLabel>
<FieldWithLabel label={t("event.form.calendar")} isExpanded={showMore}>
<FormControl fullWidth margin="dense" size="small">
<Select
value={calendarid ?? ""}
label=""
SelectDisplayProps={{ "aria-label": t("event.form.calendar") }}
displayEmpty
onChange={(e: SelectChangeEvent) =>
handleCalendarChange(e.target.value)
}
>
{CalendarItemList(userPersonalCalendars)}
</Select>
</FormControl>
) : (
<FormControl fullWidth margin="dense" size="small">
<Select
value={calendarid ?? ""}
label=""
SelectDisplayProps={{ "aria-label": t("event.form.calendar") }}
displayEmpty
onChange={(e: SelectChangeEvent) =>
handleCalendarChange(e.target.value)
}
>
{CalendarItemList(userPersonalCalendars)}
</Select>
</FormControl>
)}
</FieldWithLabel>
{showMore && (