@@ -55,6 +55,139 @@ describe("Calendar App Component Display Tests", () => {
|
||||
expect(screen.getByText("t")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Edge cases for avatar display logic
|
||||
it("handles user with only family_name", () => {
|
||||
(window as any).appList = [{ name: "test", icon: "test", link: "test" }];
|
||||
const preloadedState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "test@test.com",
|
||||
family_name: "Smith",
|
||||
name: null,
|
||||
sid: "mockSid",
|
||||
openpaasId: "667037022b752d0026472254",
|
||||
},
|
||||
},
|
||||
};
|
||||
renderWithProviders(<Menubar />, preloadedState);
|
||||
expect(screen.getByText("t")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("handles user with only name", () => {
|
||||
(window as any).appList = [{ name: "test", icon: "test", link: "test" }];
|
||||
const preloadedState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "test@test.com",
|
||||
family_name: null,
|
||||
name: "John",
|
||||
sid: "mockSid",
|
||||
openpaasId: "667037022b752d0026472254",
|
||||
},
|
||||
},
|
||||
};
|
||||
renderWithProviders(<Menubar />, preloadedState);
|
||||
expect(screen.getByText("t")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("handles user with both name and family_name", () => {
|
||||
(window as any).appList = [{ name: "test", icon: "test", link: "test" }];
|
||||
const preloadedState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "test@test.com",
|
||||
family_name: "Doe",
|
||||
name: "John",
|
||||
sid: "mockSid",
|
||||
openpaasId: "667037022b752d0026472254",
|
||||
},
|
||||
},
|
||||
};
|
||||
renderWithProviders(<Menubar />, preloadedState);
|
||||
expect(screen.getByText("JD")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("handles user with no name and family_name", () => {
|
||||
(window as any).appList = [{ name: "test", icon: "test", link: "test" }];
|
||||
const preloadedState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "test@test.com",
|
||||
family_name: null,
|
||||
name: null,
|
||||
sid: "mockSid",
|
||||
openpaasId: "667037022b752d0026472254",
|
||||
},
|
||||
},
|
||||
};
|
||||
renderWithProviders(<Menubar />, preloadedState);
|
||||
expect(screen.getByText("t")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("handles user with empty email", () => {
|
||||
(window as any).appList = [{ name: "test", icon: "test", link: "test" }];
|
||||
const preloadedState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "",
|
||||
family_name: null,
|
||||
name: null,
|
||||
sid: "mockSid",
|
||||
openpaasId: "667037022b752d0026472254",
|
||||
},
|
||||
},
|
||||
};
|
||||
renderWithProviders(<Menubar />, preloadedState);
|
||||
// Should not crash and show empty string
|
||||
const avatar = screen.getByRole("img", { hidden: true });
|
||||
expect(avatar).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("handles user with null email", () => {
|
||||
(window as any).appList = [{ name: "test", icon: "test", link: "test" }];
|
||||
const preloadedState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: null,
|
||||
family_name: null,
|
||||
name: null,
|
||||
sid: "mockSid",
|
||||
openpaasId: "667037022b752d0026472254",
|
||||
},
|
||||
},
|
||||
};
|
||||
renderWithProviders(<Menubar />, preloadedState);
|
||||
// Should not crash and show empty string
|
||||
const avatar = screen.getByRole("img", { hidden: true });
|
||||
expect(avatar).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("handles user with undefined email", () => {
|
||||
(window as any).appList = [{ name: "test", icon: "test", link: "test" }];
|
||||
const preloadedState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: undefined,
|
||||
family_name: null,
|
||||
name: null,
|
||||
sid: "mockSid",
|
||||
openpaasId: "667037022b752d0026472254",
|
||||
},
|
||||
},
|
||||
};
|
||||
renderWithProviders(<Menubar />, preloadedState);
|
||||
// Should not crash and show empty string
|
||||
const avatar = screen.getByRole("img", { hidden: true });
|
||||
expect(avatar).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows AppsIcon when applist is not empty", () => {
|
||||
(window as any).appList = [{ name: "test", icon: "test", link: "test" }];
|
||||
renderWithProviders(<Menubar />, preloadedState);
|
||||
|
||||
@@ -465,4 +465,202 @@ describe("calendarEventToJCal", () => {
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
// Edge cases for timezone fallback
|
||||
it("handles invalid timezone with UTC fallback", () => {
|
||||
const mockEvent = {
|
||||
uid: "event-invalid-tz",
|
||||
title: "Invalid Timezone Event",
|
||||
start: new Date("2025-07-23T10:00:00"),
|
||||
end: new Date("2025-07-23T11:00:00"),
|
||||
timezone: "Invalid/Timezone",
|
||||
allday: false,
|
||||
attendee: [],
|
||||
};
|
||||
|
||||
const result = calendarEventToJCal(mockEvent as CalendarEvent);
|
||||
|
||||
expect(result[0]).toBe("vcalendar");
|
||||
const [vevent, vtimezone] = result[2];
|
||||
expect(vevent[0]).toBe("vevent");
|
||||
expect(vtimezone[0]).toBe("vtimezone");
|
||||
|
||||
// Should use UTC timezone as fallback - check for UTC standard timezone
|
||||
expect(vtimezone[2]).toEqual(
|
||||
expect.arrayContaining([
|
||||
[
|
||||
"standard",
|
||||
[
|
||||
["tzoffsetfrom", {}, "utc-offset", "+00:00"],
|
||||
["tzoffsetto", {}, "utc-offset", "+00:00"],
|
||||
["tzname", {}, "text", "UTC"],
|
||||
["dtstart", {}, "date-time", "1970-01-01T00:00:00"],
|
||||
],
|
||||
[],
|
||||
],
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it("handles null timezone with UTC fallback", () => {
|
||||
const mockEvent = {
|
||||
uid: "event-null-tz",
|
||||
title: "Null Timezone Event",
|
||||
start: new Date("2025-07-23T10:00:00"),
|
||||
end: new Date("2025-07-23T11:00:00"),
|
||||
timezone: null,
|
||||
allday: false,
|
||||
attendee: [],
|
||||
};
|
||||
|
||||
const result = calendarEventToJCal(mockEvent as CalendarEvent);
|
||||
|
||||
expect(result[0]).toBe("vcalendar");
|
||||
const [vevent, vtimezone] = result[2];
|
||||
expect(vevent[0]).toBe("vevent");
|
||||
expect(vtimezone[0]).toBe("vtimezone");
|
||||
|
||||
// Should use UTC timezone as fallback
|
||||
expect(vtimezone[2]).toEqual(
|
||||
expect.arrayContaining([
|
||||
[
|
||||
"standard",
|
||||
[
|
||||
["tzoffsetfrom", {}, "utc-offset", "+00:00"],
|
||||
["tzoffsetto", {}, "utc-offset", "+00:00"],
|
||||
["tzname", {}, "text", "UTC"],
|
||||
["dtstart", {}, "date-time", "1970-01-01T00:00:00"],
|
||||
],
|
||||
[],
|
||||
],
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it("handles undefined timezone with UTC fallback", () => {
|
||||
const mockEvent = {
|
||||
uid: "event-undefined-tz",
|
||||
title: "Undefined Timezone Event",
|
||||
start: new Date("2025-07-23T10:00:00"),
|
||||
end: new Date("2025-07-23T11:00:00"),
|
||||
timezone: undefined,
|
||||
allday: false,
|
||||
attendee: [],
|
||||
};
|
||||
|
||||
const result = calendarEventToJCal(mockEvent as CalendarEvent);
|
||||
|
||||
expect(result[0]).toBe("vcalendar");
|
||||
const [vevent, vtimezone] = result[2];
|
||||
expect(vevent[0]).toBe("vevent");
|
||||
expect(vtimezone[0]).toBe("vtimezone");
|
||||
|
||||
// Should use UTC timezone as fallback
|
||||
expect(vtimezone[2]).toEqual(
|
||||
expect.arrayContaining([
|
||||
[
|
||||
"standard",
|
||||
[
|
||||
["tzoffsetfrom", {}, "utc-offset", "+00:00"],
|
||||
["tzoffsetto", {}, "utc-offset", "+00:00"],
|
||||
["tzname", {}, "text", "UTC"],
|
||||
["dtstart", {}, "date-time", "1970-01-01T00:00:00"],
|
||||
],
|
||||
[],
|
||||
],
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it("handles empty string timezone with UTC fallback", () => {
|
||||
const mockEvent = {
|
||||
uid: "event-empty-tz",
|
||||
title: "Empty Timezone Event",
|
||||
start: new Date("2025-07-23T10:00:00"),
|
||||
end: new Date("2025-07-23T11:00:00"),
|
||||
timezone: "",
|
||||
allday: false,
|
||||
attendee: [],
|
||||
};
|
||||
|
||||
const result = calendarEventToJCal(mockEvent as CalendarEvent);
|
||||
|
||||
expect(result[0]).toBe("vcalendar");
|
||||
const [vevent, vtimezone] = result[2];
|
||||
expect(vevent[0]).toBe("vevent");
|
||||
expect(vtimezone[0]).toBe("vtimezone");
|
||||
|
||||
// Should use UTC timezone as fallback
|
||||
expect(vtimezone[2]).toEqual(
|
||||
expect.arrayContaining([
|
||||
[
|
||||
"standard",
|
||||
[
|
||||
["tzoffsetfrom", {}, "utc-offset", "+00:00"],
|
||||
["tzoffsetto", {}, "utc-offset", "+00:00"],
|
||||
["tzname", {}, "text", "UTC"],
|
||||
["dtstart", {}, "date-time", "1970-01-01T00:00:00"],
|
||||
],
|
||||
[],
|
||||
],
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it("handles valid timezone correctly", () => {
|
||||
const mockEvent = {
|
||||
uid: "event-valid-tz",
|
||||
title: "Valid Timezone Event",
|
||||
start: new Date("2025-07-23T10:00:00"),
|
||||
end: new Date("2025-07-23T11:00:00"),
|
||||
timezone: "Europe/Paris",
|
||||
allday: false,
|
||||
attendee: [],
|
||||
};
|
||||
|
||||
const result = calendarEventToJCal(mockEvent as CalendarEvent);
|
||||
|
||||
expect(result[0]).toBe("vcalendar");
|
||||
const [vevent, vtimezone] = result[2];
|
||||
expect(vevent[0]).toBe("vevent");
|
||||
expect(vtimezone[0]).toBe("vtimezone");
|
||||
|
||||
// Should use the specified timezone - check for Europe/Paris timezone components
|
||||
expect(vtimezone[2]).toEqual(
|
||||
expect.arrayContaining([
|
||||
[
|
||||
"daylight",
|
||||
[
|
||||
["tzoffsetfrom", {}, "utc-offset", "+01:00"],
|
||||
["tzoffsetto", {}, "utc-offset", "+02:00"],
|
||||
["tzname", {}, "text", "CEST"],
|
||||
["dtstart", {}, "date-time", "1970-03-29T02:00:00"],
|
||||
[
|
||||
"rrule",
|
||||
{},
|
||||
"recur",
|
||||
{ byday: "-1SU", bymonth: 3, freq: "YEARLY" },
|
||||
],
|
||||
],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"standard",
|
||||
[
|
||||
["tzoffsetfrom", {}, "utc-offset", "+02:00"],
|
||||
["tzoffsetto", {}, "utc-offset", "+01:00"],
|
||||
["tzname", {}, "text", "CET"],
|
||||
["dtstart", {}, "date-time", "1970-10-25T03:00:00"],
|
||||
[
|
||||
"rrule",
|
||||
{},
|
||||
"recur",
|
||||
{ byday: "-1SU", bymonth: 10, freq: "YEARLY" },
|
||||
],
|
||||
],
|
||||
[],
|
||||
],
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
main {
|
||||
.App {
|
||||
background-color: #f3f6f9;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.main-layout {
|
||||
background-color: #fff;
|
||||
flex-direction: row;
|
||||
height: calc(100vh - 90px);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
flex-grow: 1;
|
||||
overflow: "hidden";
|
||||
height: 90vh;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding: 0 25px 0 10px;
|
||||
}
|
||||
|
||||
.calendarListHeader {
|
||||
@@ -16,38 +25,22 @@ main {
|
||||
margin-left: 25%;
|
||||
}
|
||||
.sidebar {
|
||||
border-right: 2px gray;
|
||||
border-right: 2px solid #f3f4f6;
|
||||
width: 20vw;
|
||||
padding: 16px 25px;
|
||||
}
|
||||
|
||||
.fc .fc-daygrid-day {
|
||||
min-height: 10px !important; /* Default is ~40px */
|
||||
height: 10px !important;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.fc .fc-timegrid-slot {
|
||||
height: 60px !important; /* Default is ~40px */
|
||||
}
|
||||
|
||||
.fc-timegrid-slot {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.declined-event {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.tentative-event {
|
||||
}
|
||||
|
||||
.needs-action-event {
|
||||
opacity: 0.7;
|
||||
border: dashed 1px #ffffff;
|
||||
}
|
||||
|
||||
.fc .fc-timegrid-slot {
|
||||
height: 30px !important;
|
||||
min-height: 30px !important;
|
||||
height: auto;
|
||||
min-height: 54px !important;
|
||||
}
|
||||
|
||||
.fc .fc-timegrid-slot-label {
|
||||
@@ -74,14 +67,6 @@ main {
|
||||
overflow: hidden; /* Important: let FullCalendar handle internal scroll */
|
||||
}
|
||||
|
||||
.fc-scroller-liquid-absolute {
|
||||
overflow-y: auto !important; /* This targets the scrollable part */
|
||||
max-height: calc(100vh - 100px); /* Adjust based on your header size */
|
||||
}
|
||||
.scrollgrid-section-header {
|
||||
border: white;
|
||||
}
|
||||
|
||||
/* Base container */
|
||||
.fc-daygrid-day-top {
|
||||
display: flex;
|
||||
@@ -91,36 +76,6 @@ main {
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
|
||||
/* Day number */
|
||||
.fc-daygrid-day-top .fc-daygrid-day-number {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
/* Day name (e.g., MON) */
|
||||
.fc-daygrid-day-top small {
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.05em;
|
||||
color: #94a3b8; /* slate gray */
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Today’s date highlight */
|
||||
.current-date {
|
||||
background-color: orange;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
.fc .fc-col-header-cell,
|
||||
.fc .fc-scrollgrid table,
|
||||
.fc-theme-standard .fc-scrollgrid {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* sidebar*/
|
||||
.sidebar-calendar {
|
||||
width: 280px;
|
||||
@@ -207,3 +162,112 @@ main {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
/* Custom main calendar view */
|
||||
.fc-header-toolbar.fc-toolbar.fc-toolbar-ltr {
|
||||
/* display: none; */
|
||||
}
|
||||
.fc-theme-standard td,
|
||||
.fc-theme-standard th {
|
||||
border: 1px solid #b8c1cc;
|
||||
border-left: 0;
|
||||
}
|
||||
a.fc-timegrid-axis-cushion {
|
||||
color: #243b55;
|
||||
text-align: center;
|
||||
font-family: Roboto;
|
||||
font-size: 12.507px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18.76px;
|
||||
letter-spacing: 0.391px;
|
||||
}
|
||||
.fc-daygrid-day-top small {
|
||||
color: #8c9caf;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.25px;
|
||||
}
|
||||
.fc-daygrid-day-top .fc-daygrid-day-number,
|
||||
span.fc-daygrid-day-number {
|
||||
color: #243b55;
|
||||
margin: 0 8px 0 0;
|
||||
font-family: Roboto;
|
||||
font-size: 28px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 36px;
|
||||
position: relative;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
line-height: 39px;
|
||||
}
|
||||
.fc-daygrid-day-top .fc-daygrid-day-number:after,
|
||||
span.fc-daygrid-day-number:after {
|
||||
content: "";
|
||||
z-index: -1;
|
||||
border-radius: 50%;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -1px;
|
||||
}
|
||||
span.fc-daygrid-day-number.current-date {
|
||||
color: #fff;
|
||||
}
|
||||
span.fc-daygrid-day-number.current-date:after {
|
||||
background-color: #f67e35;
|
||||
}
|
||||
td.fc-timegrid-slot.fc-timegrid-slot-label.fc-scrollgrid-shrink,
|
||||
td.fc-timegrid-slot.fc-timegrid-slot-label.fc-timegrid-slot-minor {
|
||||
border: 0;
|
||||
}
|
||||
th.fc-timegrid-axis.fc-scrollgrid-shrink {
|
||||
border: 0;
|
||||
border-right: 1px solid #b8c1cc;
|
||||
}
|
||||
.fc .fc-timegrid-axis-cushion {
|
||||
min-width: 70px;
|
||||
max-width: 70px;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
}
|
||||
.fc .fc-timegrid-divider {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
.fc-timegrid-slot-label-cushion {
|
||||
color: #aea9b1;
|
||||
font-family: Roboto;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.25px;
|
||||
}
|
||||
th.fc-col-header-cell.fc-day {
|
||||
border-right: 0;
|
||||
}
|
||||
.fc .fc-scrollgrid {
|
||||
border: 0;
|
||||
}
|
||||
.fc .fc-timegrid-slot-label {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.fc .fc-timegrid-slot-label .fc-timegrid-slot-label-frame {
|
||||
position: relative;
|
||||
transform: translate(-15px, -12px);
|
||||
}
|
||||
.fc .fc-timegrid-slot-label .fc-timegrid-slot-label-frame::after {
|
||||
content: "";
|
||||
z-index: -1;
|
||||
background-color: #b8c1cc;
|
||||
width: 10px;
|
||||
height: 1px;
|
||||
position: absolute;
|
||||
top: 9.5px;
|
||||
right: -15px;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,8 @@ export default function CalendarApp() {
|
||||
|
||||
const calendars = useAppSelector((state) => state.calendars.list);
|
||||
const pending = useAppSelector((state) => state.calendars.pending);
|
||||
const userId = useAppSelector((state) => state.user.userData.openpaasId);
|
||||
const userId =
|
||||
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
|
||||
const selectPersonnalCalendars = createSelector(
|
||||
(state) => state.calendars,
|
||||
(calendars) =>
|
||||
@@ -80,9 +81,21 @@ export default function CalendarApp() {
|
||||
);
|
||||
}
|
||||
});
|
||||
const [selectedCalendars, setSelectedCalendars] = useState<string[]>(
|
||||
Object.keys(calendars).filter((id) => id.split("/")[0] === userId)
|
||||
);
|
||||
const [selectedCalendars, setSelectedCalendars] = useState<string[]>([]);
|
||||
|
||||
// Auto-select personal calendars when first loaded
|
||||
useEffect(() => {
|
||||
if (
|
||||
Object.keys(calendars).length > 0 &&
|
||||
userId &&
|
||||
selectedCalendars.length === 0
|
||||
) {
|
||||
const personalCalendarIds = Object.keys(calendars).filter(
|
||||
(id) => id.split("/")[0] === userId
|
||||
);
|
||||
setSelectedCalendars(personalCalendarIds);
|
||||
}
|
||||
}, [calendars, userId, selectedCalendars.length]);
|
||||
|
||||
const calendarRange = getCalendarRange(selectedDate);
|
||||
|
||||
@@ -163,7 +176,7 @@ export default function CalendarApp() {
|
||||
}
|
||||
|
||||
return (
|
||||
<main>
|
||||
<main className="main-layout">
|
||||
<div className="sidebar">
|
||||
<div className="calendar-label">
|
||||
<div className="calendar-label">
|
||||
@@ -304,8 +317,10 @@ export default function CalendarApp() {
|
||||
weekNumbers
|
||||
weekNumberFormat={{ week: "long" }}
|
||||
slotDuration={"00:30:00"}
|
||||
slotLabelInterval={"01:00:00"}
|
||||
scrollTime={"08:00:00"}
|
||||
slotLabelInterval={"00:30:00"}
|
||||
scrollTime={new Date(Date.now() - 2 * 60 * 60 * 1000)
|
||||
.toTimeString()
|
||||
.slice(0, 5)}
|
||||
unselectAuto={false}
|
||||
allDayText=""
|
||||
slotLabelFormat={{
|
||||
|
||||
@@ -10,7 +10,6 @@ import { Calendars } from "../../features/Calendars/CalendarTypes";
|
||||
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
import Button from "@mui/material/Button";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
|
||||
function CalendarAccordion({
|
||||
@@ -34,8 +33,8 @@ function CalendarAccordion({
|
||||
}) {
|
||||
const allCalendars = useAppSelector((state) => state.calendars.list);
|
||||
|
||||
if (calendars.length === 0) return null;
|
||||
const [expended, setExpended] = useState(defaultExpanded);
|
||||
if (calendars.length === 0) return null;
|
||||
|
||||
return (
|
||||
<Accordion defaultExpanded={defaultExpanded} expanded={expended}>
|
||||
@@ -81,7 +80,8 @@ export default function CalendarSelection({
|
||||
selectedCalendars: string[];
|
||||
setSelectedCalendars: Function;
|
||||
}) {
|
||||
const userId = useAppSelector((state) => state.user.userData.openpaasId);
|
||||
const userId =
|
||||
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
|
||||
const calendars = useAppSelector((state) => state.calendars.list);
|
||||
|
||||
const personnalCalendars = Object.keys(calendars).filter(
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
padding: 0.5rem;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
height: 7vh;
|
||||
height: 80px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.menubar-item {
|
||||
|
||||
@@ -48,14 +48,18 @@ export function Menubar() {
|
||||
<Avatar
|
||||
sx={{
|
||||
bgcolor: stringToColor(
|
||||
user.family_name ? user.family_name : user.email
|
||||
user && user.family_name
|
||||
? user.family_name
|
||||
: user && user.email
|
||||
? user.email
|
||||
: ""
|
||||
),
|
||||
}}
|
||||
sizes="large"
|
||||
>
|
||||
{user.name && user.family_name
|
||||
{user?.name && user?.family_name
|
||||
? `${user.name[0]}${user.family_name[0]}`
|
||||
: user.email[0]}
|
||||
: (user?.email?.[0] ?? "")}
|
||||
</Avatar>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -30,7 +30,7 @@ function CalendarPopover({
|
||||
}) {
|
||||
const dispatch = useAppDispatch();
|
||||
const userId =
|
||||
useAppSelector((state) => state.user.userData.openpaasId) ?? "";
|
||||
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
|
||||
const [name, setName] = useState(calendar?.name ?? "");
|
||||
const [description, setDescription] = useState(calendar?.description ?? "");
|
||||
const [color, setColor] = useState(calendar?.color ?? "");
|
||||
|
||||
@@ -74,7 +74,7 @@ export default function EventDisplayModal({
|
||||
);
|
||||
|
||||
const userPersonnalCalendars: Calendars[] = calendars.filter(
|
||||
(c) => c.id?.split("/")[0] === user.userData.openpaasId
|
||||
(c) => c.id?.split("/")[0] === user.userData?.openpaasId
|
||||
);
|
||||
|
||||
// Form state
|
||||
@@ -97,7 +97,7 @@ export default function EventDisplayModal({
|
||||
const [timezone, setTimezone] = useState(event?.timezone ?? "UTC");
|
||||
const [newCalId, setNewCalId] = useState(event?.calId);
|
||||
const [calendarid, setCalendarid] = useState(
|
||||
calId.split("/")[0] === user.userData.openpaasId
|
||||
calId.split("/")[0] === user.userData?.openpaasId
|
||||
? userPersonnalCalendars.findIndex((cal) => cal.id === calId)
|
||||
: calendars.findIndex((cal) => cal.id === calId)
|
||||
);
|
||||
@@ -198,7 +198,7 @@ export default function EventDisplayModal({
|
||||
};
|
||||
|
||||
const calList =
|
||||
calId.split("/")[0] === user.userData.openpaasId
|
||||
calId.split("/")[0] === user.userData?.openpaasId
|
||||
? Object.keys(userPersonnalCalendars).map((calendar, index) => (
|
||||
<MenuItem key={index} value={index}>
|
||||
<Typography variant="body2">
|
||||
|
||||
@@ -46,7 +46,8 @@ function EventPopover({
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const organizer = useAppSelector((state) => state.user.organiserData);
|
||||
const userId = useAppSelector((state) => state.user.userData.openpaasId);
|
||||
const userId =
|
||||
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
|
||||
const selectPersonnalCalendars = createSelector(
|
||||
(state) => state.calendars,
|
||||
(calendars) =>
|
||||
|
||||
@@ -234,8 +234,17 @@ export function calendarEventToJCal(
|
||||
]);
|
||||
});
|
||||
|
||||
const timezoneData = TIMEZONES.zones[event.timezone];
|
||||
if (!timezoneData) {
|
||||
const vtimezone = new ICAL.Timezone({
|
||||
component: TIMEZONES.zones["Etc/UTC"].ics,
|
||||
tzid: "Etc/UTC",
|
||||
});
|
||||
return ["vcalendar", [], [vevent, vtimezone.component.jCal]];
|
||||
}
|
||||
|
||||
const vtimezone = new ICAL.Timezone({
|
||||
component: TIMEZONES.zones[event.timezone].ics,
|
||||
component: timezoneData.ics,
|
||||
tzid: event.timezone,
|
||||
});
|
||||
return ["vcalendar", [], [vevent, vtimezone.component.jCal]];
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useEffect } from "react";
|
||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||
import { Auth } from "./oidcAuth";
|
||||
import { Loading } from "../../components/Loading/Loading";
|
||||
import { Error } from "../../components/Error/Error";
|
||||
import { push } from "redux-first-history";
|
||||
import { redirectTo } from "../../utils/apiUtils";
|
||||
import { getOpenPaasUserDataAsync, setTokens, setUserData } from "./userSlice";
|
||||
@@ -27,7 +26,7 @@ export function HandleLogin() {
|
||||
dispatch(setUserData(savedUser));
|
||||
dispatch(getOpenPaasUserDataAsync());
|
||||
dispatch(getCalendarsListAsync());
|
||||
dispatch(push("/"));
|
||||
dispatch(push("/calendar"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user