587 option hide show non working days (#613)
Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
@@ -78,9 +78,21 @@ export default function CalendarApp({
|
||||
const theme = useTheme();
|
||||
const view = useAppSelector((state) => state.settings.view);
|
||||
const userData = useAppSelector((state) => state.user.userData);
|
||||
const workingDays = useAppSelector(
|
||||
(state) => state.settings.businessHours?.daysOfWeek
|
||||
);
|
||||
const hideWorkingDays = useAppSelector((state) => state.settings.workingDays);
|
||||
|
||||
const hideDeclinedEvents = useAppSelector(
|
||||
(state) => state.settings.hideDeclinedEvents
|
||||
);
|
||||
const hiddenDays = useMemo(() => {
|
||||
if (!hideWorkingDays || !workingDays || workingDays.length === 0) return [];
|
||||
const validWorkingDays = workingDays.filter((d) => d >= 0 && d <= 6);
|
||||
if (validWorkingDays.length === 0) return [];
|
||||
return [0, 1, 2, 3, 4, 5, 6].filter((d) => !validWorkingDays.includes(d));
|
||||
}, [hideWorkingDays, workingDays]);
|
||||
|
||||
const calendars = useAppSelector((state) => state.calendars.list);
|
||||
const isPending = useAppSelector((state) => state.calendars.pending);
|
||||
const displayWeekNumbers = useAppSelector(
|
||||
@@ -413,6 +425,7 @@ export default function CalendarApp({
|
||||
{menubarProps?.isIframe && <Menubar {...menubarProps} />}
|
||||
{view === "calendar" && (
|
||||
<FullCalendar
|
||||
key={hiddenDays.join(",")}
|
||||
ref={(ref) => {
|
||||
if (ref) {
|
||||
calendarRef.current = ref.getApi();
|
||||
@@ -428,6 +441,7 @@ export default function CalendarApp({
|
||||
firstDay={1}
|
||||
editable={true}
|
||||
locale={localeMap[lang]}
|
||||
hiddenDays={hiddenDays}
|
||||
selectable={true}
|
||||
timeZone={timezone}
|
||||
height={"100%"}
|
||||
|
||||
@@ -23,6 +23,7 @@ import "dayjs/locale/vi";
|
||||
import { useI18n } from "twake-i18n";
|
||||
import { ReadOnlyDateField } from "./components/ReadOnlyPickerField";
|
||||
import { LONG_DATE_FORMAT } from "./utils/dateTimeFormatters";
|
||||
import { FC_DAYS, WeekDaySelector } from "./WeekDaySelector";
|
||||
|
||||
export default function RepeatEvent({
|
||||
repetition,
|
||||
@@ -57,31 +58,6 @@ export default function RepeatEvent({
|
||||
|
||||
const defaultEndDate = dayjs(eventStart).add(1, "day").format("YYYY-MM-DD");
|
||||
|
||||
const handleDayChange = (dayCode: string) => {
|
||||
const currentDays = repetition.byday || [];
|
||||
const updatedDays = currentDays.includes(dayCode)
|
||||
? currentDays.filter((d) => d !== dayCode)
|
||||
: [...currentDays, dayCode];
|
||||
|
||||
setRepetition({
|
||||
...repetition,
|
||||
byday: updatedDays.length > 0 ? updatedDays : null,
|
||||
});
|
||||
};
|
||||
|
||||
const getDayLabel = (day: string) => {
|
||||
const dayMap: { [key: string]: string } = {
|
||||
MO: t("event.repeat.days.monday"),
|
||||
TU: t("event.repeat.days.tuesday"),
|
||||
WE: t("event.repeat.days.wednesday"),
|
||||
TH: t("event.repeat.days.thursday"),
|
||||
FR: t("event.repeat.days.friday"),
|
||||
SA: t("event.repeat.days.saturday"),
|
||||
SU: t("event.repeat.days.sunday"),
|
||||
};
|
||||
return dayMap[day] || day;
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Stack>
|
||||
@@ -150,51 +126,21 @@ export default function RepeatEvent({
|
||||
{/* Weekly selection */}
|
||||
{repetition.freq === "weekly" && (
|
||||
<Box mb={2}>
|
||||
<Box display="flex" gap={1}>
|
||||
{days.map((dayCode) => {
|
||||
const isSelected = repetition.byday?.includes(dayCode) ?? false;
|
||||
const fullLabel = getDayLabel(dayCode);
|
||||
const label = fullLabel.charAt(0);
|
||||
|
||||
return (
|
||||
<Box
|
||||
key={dayCode}
|
||||
role="button"
|
||||
aria-label={fullLabel}
|
||||
onClick={() => {
|
||||
if (!isOwn) return;
|
||||
handleDayChange(dayCode);
|
||||
}}
|
||||
sx={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: "4px",
|
||||
border: "1px solid",
|
||||
borderColor: isSelected ? "primary.main" : "#AEAEC0",
|
||||
color: isSelected ? "#fff" : "#8C9CAF",
|
||||
fontSize: 16,
|
||||
fontWeight: 400,
|
||||
lineHeight: "24px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
textAlign: "center",
|
||||
bgcolor: isSelected ? "primary.main" : "transparent",
|
||||
cursor: isOwn ? "pointer" : "default",
|
||||
"&:hover": isOwn
|
||||
? {
|
||||
borderColor: "primary.main",
|
||||
bgcolor: "primary.main",
|
||||
color: "#fff",
|
||||
}
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
<WeekDaySelector
|
||||
selectedDays={(repetition.byday ?? [])
|
||||
.map((ics) => FC_DAYS.find((d) => d.ics === ics)?.fc ?? -1)
|
||||
.filter((d) => d !== -1)}
|
||||
onChange={(fcDays) => {
|
||||
const icsDays = fcDays
|
||||
.map((fc) => FC_DAYS.find((d) => d.fc === fc)?.ics ?? "")
|
||||
.filter(Boolean);
|
||||
setRepetition({
|
||||
...repetition,
|
||||
byday: icsDays.length > 0 ? icsDays : null,
|
||||
});
|
||||
}}
|
||||
disabled={!isOwn}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { Box } from "@linagora/twake-mui";
|
||||
import { useI18n } from "twake-i18n";
|
||||
|
||||
interface WeekDaySelectorProps {
|
||||
selectedDays: number[]; // FullCalendar format: 0=Sun, 1=Mon...
|
||||
onChange: (days: number[]) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const FC_DAYS = [
|
||||
{ fc: 1, ics: "MO" },
|
||||
{ fc: 2, ics: "TU" },
|
||||
{ fc: 3, ics: "WE" },
|
||||
{ fc: 4, ics: "TH" },
|
||||
{ fc: 5, ics: "FR" },
|
||||
{ fc: 6, ics: "SA" },
|
||||
{ fc: 0, ics: "SU" },
|
||||
];
|
||||
|
||||
export function WeekDaySelector({
|
||||
selectedDays,
|
||||
onChange,
|
||||
disabled,
|
||||
}: WeekDaySelectorProps) {
|
||||
const { t } = useI18n();
|
||||
|
||||
const getDayLabel = (ics: string) => {
|
||||
const dayMap: Record<string, string> = {
|
||||
MO: t("event.repeat.days.monday"),
|
||||
TU: t("event.repeat.days.tuesday"),
|
||||
WE: t("event.repeat.days.wednesday"),
|
||||
TH: t("event.repeat.days.thursday"),
|
||||
FR: t("event.repeat.days.friday"),
|
||||
SA: t("event.repeat.days.saturday"),
|
||||
SU: t("event.repeat.days.sunday"),
|
||||
};
|
||||
return dayMap[ics] || ics;
|
||||
};
|
||||
|
||||
const handleToggle = (fcDay: number) => {
|
||||
if (disabled) return;
|
||||
const updated = selectedDays.includes(fcDay)
|
||||
? selectedDays.filter((d) => d !== fcDay)
|
||||
: [...selectedDays, fcDay];
|
||||
onChange(updated);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box display="flex" gap={1}>
|
||||
{FC_DAYS.map(({ fc, ics }) => {
|
||||
const isSelected = selectedDays.includes(fc);
|
||||
const fullLabel = getDayLabel(ics);
|
||||
|
||||
return (
|
||||
<Box
|
||||
component="button"
|
||||
type="button"
|
||||
key={ics}
|
||||
aria-label={fullLabel}
|
||||
aria-pressed={isSelected}
|
||||
onClick={() => handleToggle(fc)}
|
||||
disabled={disabled}
|
||||
sx={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: "4px",
|
||||
border: "1px solid",
|
||||
borderColor: isSelected ? "primary.main" : "#AEAEC0",
|
||||
color: isSelected ? "#fff" : "#8C9CAF",
|
||||
fontSize: 16,
|
||||
fontWeight: 400,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
bgcolor: isSelected ? "primary.main" : "transparent",
|
||||
cursor: disabled ? "default" : "pointer",
|
||||
"&:hover": !disabled
|
||||
? {
|
||||
borderColor: "primary.main",
|
||||
bgcolor: "primary.main",
|
||||
color: "#fff",
|
||||
}
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
{fullLabel.charAt(0)}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user