Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import { screen, fireEvent, waitFor } from "@testing-library/react";
|
|||||||
import CalendarPopover from "../../../src/components/Calendar/CalendarModal";
|
import CalendarPopover from "../../../src/components/Calendar/CalendarModal";
|
||||||
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
||||||
import * as eventThunks from "../../../src/features/Calendars/CalendarSlice";
|
import * as eventThunks from "../../../src/features/Calendars/CalendarSlice";
|
||||||
import { Calendars } from "../../../src/features/Calendars/CalendarTypes";
|
import { Calendar } from "../../../src/features/Calendars/CalendarTypes";
|
||||||
import { getSecretLink } from "../../../src/features/Calendars/CalendarApi";
|
import { getSecretLink } from "../../../src/features/Calendars/CalendarApi";
|
||||||
|
|
||||||
jest.mock("../../../src/features/Calendars/CalendarApi", () => ({
|
jest.mock("../../../src/features/Calendars/CalendarApi", () => ({
|
||||||
@@ -103,7 +103,7 @@ describe("CalendarPopover (editing mode)", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const existingCalendar: Calendars = {
|
const existingCalendar: Calendar = {
|
||||||
id: "user1/cal1",
|
id: "user1/cal1",
|
||||||
link: "/calendars/user/cal1",
|
link: "/calendars/user/cal1",
|
||||||
name: "Work Calendar",
|
name: "Work Calendar",
|
||||||
@@ -208,7 +208,7 @@ describe("CalendarPopover - Tabs Scenarios", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const existingCalendar: Calendars = {
|
const existingCalendar: Calendar = {
|
||||||
id: "user1/cal1",
|
id: "user1/cal1",
|
||||||
link: "/calendars/user1/cal1.json",
|
link: "/calendars/user1/cal1.json",
|
||||||
name: "Work Calendar",
|
name: "Work Calendar",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import * as calAPI from "../../../src/features/Calendars/CalendarApi";
|
|||||||
import * as userAPI from "../../../src/features/User/userAPI";
|
import * as userAPI from "../../../src/features/User/userAPI";
|
||||||
|
|
||||||
import { configureStore } from "@reduxjs/toolkit";
|
import { configureStore } from "@reduxjs/toolkit";
|
||||||
import { Calendars } from "../../../src/features/Calendars/CalendarTypes";
|
import { Calendar } from "../../../src/features/Calendars/CalendarTypes";
|
||||||
import { CalendarEvent } from "../../../src/features/Events/EventsTypes";
|
import { CalendarEvent } from "../../../src/features/Events/EventsTypes";
|
||||||
|
|
||||||
jest.mock("../../../src/features/Calendars/CalendarApi");
|
jest.mock("../../../src/features/Calendars/CalendarApi");
|
||||||
@@ -79,7 +79,7 @@ describe("CalendarSlice", () => {
|
|||||||
[calId]: {
|
[calId]: {
|
||||||
id: calId,
|
id: calId,
|
||||||
events: { e1: { uid: "e1" } },
|
events: { e1: { uid: "e1" } },
|
||||||
} as unknown as Calendars,
|
} as unknown as Calendar,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const state = reducer(
|
const state = reducer(
|
||||||
@@ -97,7 +97,7 @@ describe("CalendarSlice", () => {
|
|||||||
[calId]: {
|
[calId]: {
|
||||||
id: calId,
|
id: calId,
|
||||||
events: { e1: { uid: "e1", title: "Old" } },
|
events: { e1: { uid: "e1", title: "Old" } },
|
||||||
} as unknown as Calendars,
|
} as unknown as Calendar,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const state = reducer(
|
const state = reducer(
|
||||||
@@ -276,7 +276,7 @@ describe("CalendarSlice", () => {
|
|||||||
id: "u1/cal1",
|
id: "u1/cal1",
|
||||||
name: "Existing Calendar",
|
name: "Existing Calendar",
|
||||||
events: {},
|
events: {},
|
||||||
} as Calendars,
|
} as Calendar,
|
||||||
};
|
};
|
||||||
|
|
||||||
const store = storeFactory();
|
const store = storeFactory();
|
||||||
@@ -447,7 +447,7 @@ describe("CalendarSlice", () => {
|
|||||||
[calId]: {
|
[calId]: {
|
||||||
id: calId,
|
id: calId,
|
||||||
events: { e1: { uid: "e1" } },
|
events: { e1: { uid: "e1" } },
|
||||||
} as unknown as Calendars,
|
} as unknown as Calendar,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const state = reducer(
|
const state = reducer(
|
||||||
@@ -473,7 +473,7 @@ describe("CalendarSlice", () => {
|
|||||||
ownerEmails: ["o@o.com"],
|
ownerEmails: ["o@o.com"],
|
||||||
link: "/calendars/t1.json",
|
link: "/calendars/t1.json",
|
||||||
description: "desc",
|
description: "desc",
|
||||||
} as Calendars,
|
} as Calendar,
|
||||||
};
|
};
|
||||||
const state = reducer(
|
const state = reducer(
|
||||||
initialState,
|
initialState,
|
||||||
@@ -533,7 +533,7 @@ describe("CalendarSlice", () => {
|
|||||||
["c1"]: {
|
["c1"]: {
|
||||||
id: "c1",
|
id: "c1",
|
||||||
events: {},
|
events: {},
|
||||||
} as unknown as Calendars,
|
} as unknown as Calendar,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getCalendarDetailAsync.fulfilled(payload, "req11", {
|
getCalendarDetailAsync.fulfilled(payload, "req11", {
|
||||||
|
|||||||
@@ -300,13 +300,13 @@ describe("Event Preview Display", () => {
|
|||||||
screen.getByText("eventPreview.attendingQuestion")
|
screen.getByText("eventPreview.attendingQuestion")
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
screen.getByRole("button", { name: "eventPreview.accept" })
|
screen.getByRole("button", { name: "eventPreview.ACCEPTED" })
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
screen.getByRole("button", { name: "eventPreview.maybe" })
|
screen.getByRole("button", { name: "eventPreview.TENTATIVE" })
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
screen.getByRole("button", { name: "eventPreview.decline" })
|
screen.getByRole("button", { name: "eventPreview.DECLINED" })
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
it("doesnt renders RSVP buttons when user isnt an attendee", () => {
|
it("doesnt renders RSVP buttons when user isnt an attendee", () => {
|
||||||
@@ -354,13 +354,13 @@ describe("Event Preview Display", () => {
|
|||||||
screen.queryByText("eventPreview.attendingQuestion")
|
screen.queryByText("eventPreview.attendingQuestion")
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
screen.queryByRole("button", { name: "eventPreview.accept" })
|
screen.queryByRole("button", { name: "eventPreview.ACCEPTED" })
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
screen.queryByRole("button", { name: "eventPreview.maybe" })
|
screen.queryByRole("button", { name: "eventPreview.TENTATIVE" })
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
screen.queryByRole("button", { name: "eventPreview.decline" })
|
screen.queryByRole("button", { name: "eventPreview.DECLINED" })
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -414,7 +414,7 @@ describe("Event Preview Display", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(
|
fireEvent.click(
|
||||||
screen.getByRole("button", { name: "eventPreview.accept" })
|
screen.getByRole("button", { name: "eventPreview.ACCEPTED" })
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@@ -474,7 +474,9 @@ describe("Event Preview Display", () => {
|
|||||||
rsvpState
|
rsvpState
|
||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole("button", { name: "eventPreview.maybe" }));
|
fireEvent.click(
|
||||||
|
screen.getByRole("button", { name: "eventPreview.TENTATIVE" })
|
||||||
|
);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(spy).toHaveBeenCalled();
|
||||||
@@ -534,7 +536,7 @@ describe("Event Preview Display", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(
|
fireEvent.click(
|
||||||
screen.getByRole("button", { name: "eventPreview.decline" })
|
screen.getByRole("button", { name: "eventPreview.DECLINED" })
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@@ -1020,7 +1022,7 @@ describe("Event Preview Display", () => {
|
|||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("displays only maybe count when all attendees tentative", () => {
|
it("displays only TENTATIVE count when all attendees tentative", () => {
|
||||||
const attendees = [
|
const attendees = [
|
||||||
{
|
{
|
||||||
cn: "organizer",
|
cn: "organizer",
|
||||||
@@ -1262,7 +1264,7 @@ describe("Event Preview Display", () => {
|
|||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("displays correct counts with multiple yes and maybe", () => {
|
it("displays correct counts with multiple yes and TENTATIVE", () => {
|
||||||
const attendees = [
|
const attendees = [
|
||||||
{
|
{
|
||||||
cn: "Guest 1",
|
cn: "Guest 1",
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ describe("EventPopover", () => {
|
|||||||
{
|
{
|
||||||
cn: "John Doe",
|
cn: "John Doe",
|
||||||
cal_address: "john@example.com",
|
cal_address: "john@example.com",
|
||||||
partstat: "NEED_ACTION",
|
partstat: "NEEDS-ACTION",
|
||||||
rsvp: "FALSE",
|
rsvp: "FALSE",
|
||||||
role: "REQ-PARTICIPANT",
|
role: "REQ-PARTICIPANT",
|
||||||
cutype: "INDIVIDUAL",
|
cutype: "INDIVIDUAL",
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ describe("EventPreviewModal - Recurring Event Interactions", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(
|
fireEvent.click(
|
||||||
screen.getByRole("button", { name: "eventPreview.accept" })
|
screen.getByRole("button", { name: "eventPreview.ACCEPTED" })
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@@ -505,7 +505,7 @@ describe("RSVP to Recurring Event", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(
|
fireEvent.click(
|
||||||
screen.getByRole("button", { name: "eventPreview.accept" })
|
screen.getByRole("button", { name: "eventPreview.ACCEPTED" })
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@@ -543,7 +543,7 @@ describe("RSVP to Recurring Event", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(
|
fireEvent.click(
|
||||||
screen.getByRole("button", { name: "eventPreview.accept" })
|
screen.getByRole("button", { name: "eventPreview.ACCEPTED" })
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@@ -975,8 +975,7 @@ describe("handleRSVP function", () => {
|
|||||||
basePreloadedState.calendars.list["667037022b752d0026472254/cal1"],
|
basePreloadedState.calendars.list["667037022b752d0026472254/cal1"],
|
||||||
basePreloadedState.user,
|
basePreloadedState.user,
|
||||||
nonRecurringEvent,
|
nonRecurringEvent,
|
||||||
"ACCEPTED",
|
"ACCEPTED"
|
||||||
mockOnClose
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(mockDispatch).toHaveBeenCalled();
|
expect(mockDispatch).toHaveBeenCalled();
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import EventUpdateModal from "../../../src/features/Events/EventUpdateModal";
|
|||||||
import CalendarLayout from "../../../src/components/Calendar/CalendarLayout";
|
import CalendarLayout from "../../../src/components/Calendar/CalendarLayout";
|
||||||
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
||||||
import { SpiedFunction } from "jest-mock";
|
import { SpiedFunction } from "jest-mock";
|
||||||
import { Calendars } from "../../../src/features/Calendars/CalendarTypes";
|
import { Calendar } from "../../../src/features/Calendars/CalendarTypes";
|
||||||
import { CalendarEvent } from "../../../src/features/Events/EventsTypes";
|
import { CalendarEvent } from "../../../src/features/Events/EventsTypes";
|
||||||
import { DateSelectArg } from "@fullcalendar/core";
|
import { DateSelectArg } from "@fullcalendar/core";
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ describe("Update tempcalendars called with correct params", () => {
|
|||||||
let refreshCalendarsSpy: SpiedFunction<
|
let refreshCalendarsSpy: SpiedFunction<
|
||||||
(
|
(
|
||||||
dispatch: ThunkDispatch<any, any, any>,
|
dispatch: ThunkDispatch<any, any, any>,
|
||||||
calendars: Calendars[],
|
calendars: Calendar[],
|
||||||
calendarRange: { start: Date; end: Date },
|
calendarRange: { start: Date; end: Date },
|
||||||
calType?: "temp"
|
calType?: "temp"
|
||||||
) => Promise<void>
|
) => Promise<void>
|
||||||
@@ -35,14 +35,14 @@ describe("Update tempcalendars called with correct params", () => {
|
|||||||
let refreshSingularCalendarSpy: SpiedFunction<
|
let refreshSingularCalendarSpy: SpiedFunction<
|
||||||
(
|
(
|
||||||
dispatch: ThunkDispatch<any, any, any>,
|
dispatch: ThunkDispatch<any, any, any>,
|
||||||
calendar: Calendars,
|
calendar: Calendar,
|
||||||
calendarRange: { start: Date; end: Date },
|
calendarRange: { start: Date; end: Date },
|
||||||
calType?: "temp"
|
calType?: "temp"
|
||||||
) => Promise<void>
|
) => Promise<void>
|
||||||
>;
|
>;
|
||||||
let updateTempCalendarSpy: SpiedFunction<
|
let updateTempCalendarSpy: SpiedFunction<
|
||||||
(
|
(
|
||||||
tempcalendars: Record<string, Calendars>,
|
tempcalendars: Record<string, Calendar>,
|
||||||
event: CalendarEvent,
|
event: CalendarEvent,
|
||||||
dispatch: ThunkDispatch<any, any, any>,
|
dispatch: ThunkDispatch<any, any, any>,
|
||||||
calendarRange: { start: Date; end: Date }
|
calendarRange: { start: Date; end: Date }
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ describe("parseCalendarEvent", () => {
|
|||||||
cn: "Bob",
|
cn: "Bob",
|
||||||
cal_address: "bob@example.com",
|
cal_address: "bob@example.com",
|
||||||
partstat: "ACCEPTED",
|
partstat: "ACCEPTED",
|
||||||
rsvp: "",
|
cutype: "INDIVIDUAL",
|
||||||
role: "",
|
role: "REQ-PARTICIPANT",
|
||||||
cutype: "",
|
rsvp: "FALSE",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -133,9 +133,9 @@ describe("parseCalendarEvent", () => {
|
|||||||
cn: "Bob",
|
cn: "Bob",
|
||||||
cal_address: "bob@example.com",
|
cal_address: "bob@example.com",
|
||||||
partstat: "ACCEPTED",
|
partstat: "ACCEPTED",
|
||||||
rsvp: "",
|
cutype: "INDIVIDUAL",
|
||||||
role: "",
|
role: "REQ-PARTICIPANT",
|
||||||
cutype: "",
|
rsvp: "FALSE",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -285,10 +285,10 @@ describe("parseCalendarEvent", () => {
|
|||||||
{
|
{
|
||||||
cn: "",
|
cn: "",
|
||||||
cal_address: "john@example.com",
|
cal_address: "john@example.com",
|
||||||
partstat: "",
|
partstat: "NEEDS-ACTION",
|
||||||
rsvp: "",
|
cutype: "INDIVIDUAL",
|
||||||
role: "",
|
role: "REQ-PARTICIPANT",
|
||||||
cutype: "",
|
rsvp: "FALSE",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { userAttendee } from "../../features/User/userDataTypes";
|
import { userAttendee } from "../../features/User/models/attendee";
|
||||||
|
import { createAttendee } from "../../features/User/models/attendee.mapper";
|
||||||
import {
|
import {
|
||||||
PeopleSearch,
|
PeopleSearch,
|
||||||
User,
|
User,
|
||||||
@@ -20,18 +21,18 @@ export default function UserSearch({
|
|||||||
) => React.ReactNode;
|
) => React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const [selectedUsers, setSelectedUsers] = useState(
|
const [selectedUsers, setSelectedUsers] = useState(
|
||||||
attendees.map((a) => ({
|
attendees.map((attendee) => ({
|
||||||
email: a.cal_address,
|
email: attendee.cal_address,
|
||||||
displayName: a.cn ?? "",
|
displayName: attendee.cn ?? "",
|
||||||
avatarUrl: "",
|
avatarUrl: "",
|
||||||
openpaasId: "",
|
openpaasId: "",
|
||||||
})) ?? []
|
})) ?? []
|
||||||
);
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedUsers(
|
setSelectedUsers(
|
||||||
attendees.map((a) => ({
|
attendees.map((attendee) => ({
|
||||||
email: a.cal_address,
|
email: attendee.cal_address,
|
||||||
displayName: a.cn ?? "",
|
displayName: attendee.cn ?? "",
|
||||||
avatarUrl: "",
|
avatarUrl: "",
|
||||||
openpaasId: "",
|
openpaasId: "",
|
||||||
}))
|
}))
|
||||||
@@ -45,14 +46,12 @@ export default function UserSearch({
|
|||||||
inputSlot={inputSlot}
|
inputSlot={inputSlot}
|
||||||
onChange={(event: any, value: User[]) => {
|
onChange={(event: any, value: User[]) => {
|
||||||
setAttendees(
|
setAttendees(
|
||||||
value.map((a: User) => ({
|
value.map((attendee: User) =>
|
||||||
cn: a.displayName,
|
createAttendee({
|
||||||
cal_address: a.email,
|
cal_address: attendee.email,
|
||||||
partstat: "NEED_ACTION",
|
cn: attendee.displayName,
|
||||||
rsvp: "FALSE",
|
})
|
||||||
role: "REQ-PARTICIPANT",
|
)
|
||||||
cutype: "INDIVIDUAL",
|
|
||||||
}))
|
|
||||||
);
|
);
|
||||||
setSelectedUsers(value);
|
setSelectedUsers(value);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ import {
|
|||||||
exportCalendar,
|
exportCalendar,
|
||||||
getSecretLink,
|
getSecretLink,
|
||||||
} from "../../features/Calendars/CalendarApi";
|
} from "../../features/Calendars/CalendarApi";
|
||||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../features/Calendars/CalendarTypes";
|
||||||
import { FieldWithLabel } from "../Event/components/FieldWithLabel";
|
import { FieldWithLabel } from "../Event/components/FieldWithLabel";
|
||||||
import { SnackbarAlert } from "../Loading/SnackBarAlert";
|
import { SnackbarAlert } from "../Loading/SnackBarAlert";
|
||||||
import { useI18n } from "twake-i18n";
|
import { useI18n } from "twake-i18n";
|
||||||
import { ErrorSnackbar } from "../Error/ErrorSnackbar";
|
import { ErrorSnackbar } from "../Error/ErrorSnackbar";
|
||||||
|
|
||||||
export function AccessTab({ calendar }: { calendar: Calendars }) {
|
export function AccessTab({ calendar }: { calendar: Calendar }) {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const calDAVLink = `${(window as any).CALENDAR_BASE_URL}${calendar.link.replace(".json", "")}`;
|
const calDAVLink = `${(window as any).CALENDAR_BASE_URL}${calendar.link.replace(".json", "")}`;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { MenuItem } from "@mui/material";
|
import { MenuItem } from "@mui/material";
|
||||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../features/Calendars/CalendarTypes";
|
||||||
import { CalendarName } from "./CalendarName";
|
import { CalendarName } from "./CalendarName";
|
||||||
|
|
||||||
export function CalendarItemList(
|
export function CalendarItemList(
|
||||||
userPersonalCalendars: Calendars[]
|
userPersonalCalendars: Calendar[]
|
||||||
): React.ReactNode {
|
): React.ReactNode {
|
||||||
return Object.values(userPersonalCalendars).map((calendar) => (
|
return Object.values(userPersonalCalendars).map((calendar) => (
|
||||||
<MenuItem key={calendar.id} value={calendar.id}>
|
<MenuItem key={calendar.id} value={calendar.id}>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
patchACLCalendarAsync,
|
patchACLCalendarAsync,
|
||||||
patchCalendarAsync,
|
patchCalendarAsync,
|
||||||
} from "../../features/Calendars/CalendarSlice";
|
} from "../../features/Calendars/CalendarSlice";
|
||||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../features/Calendars/CalendarTypes";
|
||||||
import { ResponsiveDialog } from "../Dialog";
|
import { ResponsiveDialog } from "../Dialog";
|
||||||
import { AccessTab } from "./AccessTab";
|
import { AccessTab } from "./AccessTab";
|
||||||
import { ImportTab } from "./ImportTab";
|
import { ImportTab } from "./ImportTab";
|
||||||
@@ -25,7 +25,7 @@ function CalendarPopover({
|
|||||||
event: object | null,
|
event: object | null,
|
||||||
reason: "backdropClick" | "escapeKeyDown"
|
reason: "backdropClick" | "escapeKeyDown"
|
||||||
) => void;
|
) => void;
|
||||||
calendar?: Calendars;
|
calendar?: Calendar;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Box, Typography } from "@mui/material";
|
import { Box, Typography } from "@mui/material";
|
||||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../features/Calendars/CalendarTypes";
|
||||||
import SquareRoundedIcon from "@mui/icons-material/SquareRounded";
|
import SquareRoundedIcon from "@mui/icons-material/SquareRounded";
|
||||||
export function CalendarName({ calendar }: { calendar: Calendars }) {
|
export function CalendarName({ calendar }: { calendar: Calendar }) {
|
||||||
return (
|
return (
|
||||||
<Box style={{ display: "flex", flexDirection: "row", gap: 8 }}>
|
<Box style={{ display: "flex", flexDirection: "row", gap: 8 }}>
|
||||||
<SquareRoundedIcon
|
<SquareRoundedIcon
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useState } from "react";
|
|||||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||||
import { getCalendars } from "../../features/Calendars/CalendarApi";
|
import { getCalendars } from "../../features/Calendars/CalendarApi";
|
||||||
import { addSharedCalendarAsync } from "../../features/Calendars/CalendarSlice";
|
import { addSharedCalendarAsync } from "../../features/Calendars/CalendarSlice";
|
||||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../features/Calendars/CalendarTypes";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import { PeopleSearch, User } from "../Attendees/PeopleSearch";
|
import { PeopleSearch, User } from "../Attendees/PeopleSearch";
|
||||||
import { ResponsiveDialog } from "../Dialog";
|
import { ResponsiveDialog } from "../Dialog";
|
||||||
@@ -93,7 +93,7 @@ function SelectedCalendarsList({
|
|||||||
onRemove,
|
onRemove,
|
||||||
onColorChange,
|
onColorChange,
|
||||||
}: {
|
}: {
|
||||||
calendars: Record<string, Calendars>;
|
calendars: Record<string, Calendar>;
|
||||||
selectedCal: CalendarWithOwner[];
|
selectedCal: CalendarWithOwner[];
|
||||||
onRemove: (cal: CalendarWithOwner) => void;
|
onRemove: (cal: CalendarWithOwner) => void;
|
||||||
onColorChange: (
|
onColorChange: (
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
|||||||
import AddIcon from "@mui/icons-material/Add";
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
import { useState, useMemo, useEffect } from "react";
|
import { useState, useMemo, useEffect } from "react";
|
||||||
import CalendarPopover from "./CalendarModal";
|
import CalendarPopover from "./CalendarModal";
|
||||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../features/Calendars/CalendarTypes";
|
||||||
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import Checkbox from "@mui/material/Checkbox";
|
import Checkbox from "@mui/material/Checkbox";
|
||||||
@@ -206,7 +206,7 @@ function CalendarSelector({
|
|||||||
handleCalendarToggle,
|
handleCalendarToggle,
|
||||||
setOpen,
|
setOpen,
|
||||||
}: {
|
}: {
|
||||||
calendars: Record<string, Calendars>;
|
calendars: Record<string, Calendar>;
|
||||||
id: string;
|
id: string;
|
||||||
isPersonal: boolean;
|
isPersonal: boolean;
|
||||||
selectedCalendars: string[];
|
selectedCalendars: string[];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Dialog from "@mui/material/Dialog";
|
import Dialog from "@mui/material/Dialog";
|
||||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../features/Calendars/CalendarTypes";
|
||||||
import DialogTitle from "@mui/material/DialogTitle";
|
import DialogTitle from "@mui/material/DialogTitle";
|
||||||
import DialogContent from "@mui/material/DialogContent";
|
import DialogContent from "@mui/material/DialogContent";
|
||||||
import DialogContentText from "@mui/material/DialogContentText";
|
import DialogContentText from "@mui/material/DialogContentText";
|
||||||
@@ -17,7 +17,7 @@ export function DeleteCalendarDialog({
|
|||||||
}: {
|
}: {
|
||||||
deletePopupOpen: boolean;
|
deletePopupOpen: boolean;
|
||||||
setDeletePopupOpen: (e: boolean) => void;
|
setDeletePopupOpen: (e: boolean) => void;
|
||||||
calendars: Record<string, Calendars>;
|
calendars: Record<string, Calendar>;
|
||||||
id: string;
|
id: string;
|
||||||
isPersonal: boolean;
|
isPersonal: boolean;
|
||||||
handleDeleteConfirm: () => void;
|
handleDeleteConfirm: () => void;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import { useI18n } from "twake-i18n";
|
import { useI18n } from "twake-i18n";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useAppSelector } from "../../app/hooks";
|
import { useAppSelector } from "../../app/hooks";
|
||||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../features/Calendars/CalendarTypes";
|
||||||
import { AddDescButton } from "../Event/AddDescButton";
|
import { AddDescButton } from "../Event/AddDescButton";
|
||||||
import { ColorPicker } from "./CalendarColorPicker";
|
import { ColorPicker } from "./CalendarColorPicker";
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ export function SettingsTab({
|
|||||||
setColor: Function;
|
setColor: Function;
|
||||||
visibility: "public" | "private";
|
visibility: "public" | "private";
|
||||||
setVisibility: Function;
|
setVisibility: Function;
|
||||||
calendar?: Calendars;
|
calendar?: Calendar;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const [toggleDesc, setToggleDesc] = useState(Boolean(description));
|
const [toggleDesc, setToggleDesc] = useState(Boolean(description));
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
getTempCalendarsListAsync,
|
getTempCalendarsListAsync,
|
||||||
removeTempCal,
|
removeTempCal,
|
||||||
} from "../../features/Calendars/CalendarSlice";
|
} from "../../features/Calendars/CalendarSlice";
|
||||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../features/Calendars/CalendarTypes";
|
||||||
import { setView } from "../../features/Settings/SettingsSlice";
|
import { setView } from "../../features/Settings/SettingsSlice";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import { User, PeopleSearch } from "../Attendees/PeopleSearch";
|
import { User, PeopleSearch } from "../Attendees/PeopleSearch";
|
||||||
@@ -134,7 +134,7 @@ function getCalendarsFromUsersDelta(
|
|||||||
return { calendarsToImport, calendarsToToggle };
|
return { calendarsToImport, calendarsToToggle };
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildEmailToCalendarMap(calRecord: Record<string, Calendars>) {
|
function buildEmailToCalendarMap(calRecord: Record<string, Calendar>) {
|
||||||
const map = new Map<string, string[]>();
|
const map = new Map<string, string[]>();
|
||||||
for (const [id, cal] of Object.entries(calRecord)) {
|
for (const [id, cal] of Object.entries(calRecord)) {
|
||||||
cal.ownerEmails?.forEach((email) => {
|
cal.ownerEmails?.forEach((email) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DateSelectArg } from "@fullcalendar/core";
|
import { DateSelectArg } from "@fullcalendar/core";
|
||||||
import { CalendarApi } from "@fullcalendar/core";
|
import { CalendarApi } from "@fullcalendar/core";
|
||||||
import { CalendarEvent } from "../../../features/Events/EventsTypes";
|
import { CalendarEvent } from "../../../features/Events/EventsTypes";
|
||||||
import { Calendars } from "../../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../../features/Calendars/CalendarTypes";
|
||||||
import { getDeltaInMilliseconds } from "../../../utils/dateUtils";
|
import { getDeltaInMilliseconds } from "../../../utils/dateUtils";
|
||||||
import {
|
import {
|
||||||
getCalendarDetailAsync,
|
getCalendarDetailAsync,
|
||||||
@@ -17,21 +17,22 @@ import { refreshCalendars } from "../../Event/utils/eventUtils";
|
|||||||
import { updateTempCalendar } from "../utils/calendarUtils";
|
import { updateTempCalendar } from "../utils/calendarUtils";
|
||||||
import { User } from "../../Attendees/PeopleSearch";
|
import { User } from "../../Attendees/PeopleSearch";
|
||||||
import { formatLocalDateTime } from "../../Event/utils/dateTimeFormatters";
|
import { formatLocalDateTime } from "../../Event/utils/dateTimeFormatters";
|
||||||
import { userAttendee } from "../../../features/User/userDataTypes";
|
import { userAttendee } from "../../../features/User/models/attendee";
|
||||||
|
import { createAttendee } from "../../../features/User/models/attendee.mapper";
|
||||||
|
|
||||||
export interface EventHandlersProps {
|
export interface EventHandlersProps {
|
||||||
setSelectedRange: (range: DateSelectArg | null) => void;
|
setSelectedRange: (range: DateSelectArg | null) => void;
|
||||||
setAnchorEl: (el: HTMLElement | null) => void;
|
setAnchorEl: (el: HTMLElement | null) => void;
|
||||||
calendarRef: React.RefObject<CalendarApi | null>;
|
calendarRef: React.RefObject<CalendarApi | null>;
|
||||||
selectedCalendars: string[];
|
selectedCalendars: string[];
|
||||||
tempcalendars: Record<string, Calendars>;
|
tempcalendars: Record<string, Calendar>;
|
||||||
calendarRange: { start: Date; end: Date };
|
calendarRange: { start: Date; end: Date };
|
||||||
dispatch: any;
|
dispatch: any;
|
||||||
setOpenEventDisplay: (open: boolean) => void;
|
setOpenEventDisplay: (open: boolean) => void;
|
||||||
setEventDisplayedId: (id: string) => void;
|
setEventDisplayedId: (id: string) => void;
|
||||||
setEventDisplayedCalId: (id: string) => void;
|
setEventDisplayedCalId: (id: string) => void;
|
||||||
setEventDisplayedTemp: (temp: boolean) => void;
|
setEventDisplayedTemp: (temp: boolean) => void;
|
||||||
calendars: Record<string, Calendars>;
|
calendars: Record<string, Calendar>;
|
||||||
setSelectedEvent: (event: CalendarEvent) => void;
|
setSelectedEvent: (event: CalendarEvent) => void;
|
||||||
setAfterChoiceFunc: (func: Function) => void;
|
setAfterChoiceFunc: (func: Function) => void;
|
||||||
setOpenEditModePopup: (open: string) => void;
|
setOpenEditModePopup: (open: string) => void;
|
||||||
@@ -72,14 +73,13 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
|||||||
end: selectInfo?.end
|
end: selectInfo?.end
|
||||||
? formatLocalDateTime(selectInfo?.end, timezone)
|
? formatLocalDateTime(selectInfo?.end, timezone)
|
||||||
: "",
|
: "",
|
||||||
attendee: tempUsers.map((u) => ({
|
attendee: tempUsers.map((user) =>
|
||||||
cn: u.displayName,
|
createAttendee({
|
||||||
cal_address: u.email,
|
cal_address: user.email,
|
||||||
partstat: "NEED-ACTION",
|
cn: user.displayName,
|
||||||
role: "REQ-PARTICIPANT",
|
rsvp: "TRUE",
|
||||||
rsvp: "TRUE",
|
})
|
||||||
cutype: "INDIVIDUAL",
|
),
|
||||||
})),
|
|
||||||
} as CalendarEvent;
|
} as CalendarEvent;
|
||||||
|
|
||||||
setTempEvent(newEvent);
|
setTempEvent(newEvent);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { CalendarApi, NowIndicatorContentArg } from "@fullcalendar/core";
|
import { CalendarApi, NowIndicatorContentArg } from "@fullcalendar/core";
|
||||||
import { createMouseHandlers } from "./mouseHandlers";
|
import { createMouseHandlers } from "./mouseHandlers";
|
||||||
import { userAttendee } from "../../../features/User/userDataTypes";
|
import { userAttendee } from "../../../features/User/models/attendee";
|
||||||
import { Calendars } from "../../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../../features/Calendars/CalendarTypes";
|
||||||
import { EventErrorHandler } from "../../Error/EventErrorHandler";
|
import { EventErrorHandler } from "../../Error/EventErrorHandler";
|
||||||
import { EventChip } from "../../Event/EventChip/EventChip";
|
import { EventChip } from "../../Event/EventChip/EventChip";
|
||||||
|
|
||||||
@@ -11,8 +11,8 @@ export interface ViewHandlersProps {
|
|||||||
setSelectedDate: (date: Date) => void;
|
setSelectedDate: (date: Date) => void;
|
||||||
setSelectedMiniDate: (date: Date) => void;
|
setSelectedMiniDate: (date: Date) => void;
|
||||||
onViewChange?: (view: string) => void;
|
onViewChange?: (view: string) => void;
|
||||||
calendars: Record<string, Calendars>;
|
calendars: Record<string, Calendar>;
|
||||||
tempcalendars: Record<string, Calendars>;
|
tempcalendars: Record<string, Calendar>;
|
||||||
errorHandler: EventErrorHandler;
|
errorHandler: EventErrorHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { darken, getContrastRatio, lighten, Theme } from "@mui/material";
|
import { darken, getContrastRatio, lighten, Theme } from "@mui/material";
|
||||||
import { ThunkDispatch } from "@reduxjs/toolkit";
|
import { ThunkDispatch } from "@reduxjs/toolkit";
|
||||||
import { updateCalColor } from "../../../features/Calendars/CalendarSlice";
|
import { updateCalColor } from "../../../features/Calendars/CalendarSlice";
|
||||||
import { Calendars } from "../../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../../features/Calendars/CalendarTypes";
|
||||||
|
|
||||||
export function updateDarkColor(
|
export function updateDarkColor(
|
||||||
calendars: Record<string, Calendars>,
|
calendars: Record<string, Calendar>,
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
dispatch: ThunkDispatch<any, any, any>
|
dispatch: ThunkDispatch<any, any, any>
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CalendarEvent } from "../../../features/Events/EventsTypes";
|
import { CalendarEvent } from "../../../features/Events/EventsTypes";
|
||||||
import { Calendars } from "../../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../../features/Calendars/CalendarTypes";
|
||||||
import { formatDateToYYYYMMDDTHHMMSS } from "../../../utils/dateUtils";
|
import { formatDateToYYYYMMDDTHHMMSS } from "../../../utils/dateUtils";
|
||||||
import { getCalendarDetailAsync } from "../../../features/Calendars/CalendarSlice";
|
import { getCalendarDetailAsync } from "../../../features/Calendars/CalendarSlice";
|
||||||
import { SlotLabelContentArg } from "@fullcalendar/core";
|
import { SlotLabelContentArg } from "@fullcalendar/core";
|
||||||
@@ -140,7 +140,7 @@ export const eventToFullCalendarFormat = (
|
|||||||
|
|
||||||
export const extractEvents = (
|
export const extractEvents = (
|
||||||
selectedCalendars: string[],
|
selectedCalendars: string[],
|
||||||
calendars: Record<string, Calendars>,
|
calendars: Record<string, Calendar>,
|
||||||
userAddress?: string,
|
userAddress?: string,
|
||||||
hideDeclinedEvents?: boolean | null
|
hideDeclinedEvents?: boolean | null
|
||||||
) => {
|
) => {
|
||||||
@@ -272,7 +272,7 @@ export function getCalendarVisibility(acl: AclEntry[]): "private" | "public" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function updateTempCalendar(
|
export async function updateTempCalendar(
|
||||||
tempcalendars: Record<string, Calendars>,
|
tempcalendars: Record<string, Calendar>,
|
||||||
event: CalendarEvent,
|
event: CalendarEvent,
|
||||||
dispatch: ThunkDispatch<any, any, any>,
|
dispatch: ThunkDispatch<any, any, any>,
|
||||||
calendarRange: { start: Date; end: Date }
|
calendarRange: { start: Date; end: Date }
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import LockOutlineIcon from "@mui/icons-material/LockOutline";
|
|||||||
import { Box, getContrastRatio } from "@mui/material";
|
import { Box, getContrastRatio } from "@mui/material";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { useLayoutEffect, useState } from "react";
|
import React, { useLayoutEffect, useState } from "react";
|
||||||
import { Calendars } from "../../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../../features/Calendars/CalendarTypes";
|
||||||
import { userAttendee } from "../../../features/User/userDataTypes";
|
import { userAttendee } from "../../../features/User/models/attendee";
|
||||||
import { EventErrorHandler } from "../../Error/EventErrorHandler";
|
import { EventErrorHandler } from "../../Error/EventErrorHandler";
|
||||||
import { EVENT_DURATION } from "./EventChip";
|
import { EVENT_DURATION } from "./EventChip";
|
||||||
|
|
||||||
@@ -13,8 +13,8 @@ const COMPACT_WIDTH_THRESHOLD = 100;
|
|||||||
|
|
||||||
export interface EventChipProps {
|
export interface EventChipProps {
|
||||||
arg: any;
|
arg: any;
|
||||||
calendars: Record<string, Calendars>;
|
calendars: Record<string, Calendar>;
|
||||||
tempcalendars: Record<string, Calendars>;
|
tempcalendars: Record<string, Calendar>;
|
||||||
errorHandler: EventErrorHandler;
|
errorHandler: EventErrorHandler;
|
||||||
}
|
}
|
||||||
export interface IconDisplayConfig {
|
export interface IconDisplayConfig {
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import {
|
|||||||
import AttendeeSelector from "../Attendees/AttendeeSearch";
|
import AttendeeSelector from "../Attendees/AttendeeSearch";
|
||||||
import RepeatEvent from "./EventRepeat";
|
import RepeatEvent from "./EventRepeat";
|
||||||
import { RepetitionObject } from "../../features/Events/EventsTypes";
|
import { RepetitionObject } from "../../features/Events/EventsTypes";
|
||||||
import { userAttendee } from "../../features/User/userDataTypes";
|
import { userAttendee } from "../../features/User/models/attendee";
|
||||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../features/Calendars/CalendarTypes";
|
||||||
import {
|
import {
|
||||||
generateMeetingLink,
|
generateMeetingLink,
|
||||||
addVideoConferenceToDescription,
|
addVideoConferenceToDescription,
|
||||||
@@ -87,7 +87,7 @@ interface EventFormFieldsProps {
|
|||||||
isOpen?: boolean;
|
isOpen?: boolean;
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
userPersonalCalendars: Calendars[];
|
userPersonalCalendars: Calendar[];
|
||||||
timezoneList: {
|
timezoneList: {
|
||||||
zones: string[];
|
zones: string[];
|
||||||
browserTz: string;
|
browserTz: string;
|
||||||
|
|||||||
@@ -1,51 +1,128 @@
|
|||||||
import { ThunkDispatch } from "@reduxjs/toolkit";
|
import { ThunkDispatch } from "@reduxjs/toolkit";
|
||||||
import { useAppSelector } from "../../../app/hooks";
|
|
||||||
import {
|
import {
|
||||||
updateEventInstanceAsync,
|
updateEventInstanceAsync,
|
||||||
updateSeriesAsync,
|
|
||||||
putEventAsync,
|
putEventAsync,
|
||||||
deleteEventInstanceAsync,
|
deleteEventInstanceAsync,
|
||||||
deleteEventAsync,
|
deleteEventAsync,
|
||||||
} from "../../../features/Calendars/CalendarSlice";
|
} from "../../../features/Calendars/CalendarSlice";
|
||||||
import { Calendars } from "../../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../../features/Calendars/CalendarTypes";
|
||||||
import {
|
import { updateSeriesPartstat } from "../../../features/Events/EventApi";
|
||||||
getEvent,
|
|
||||||
updateSeriesPartstat,
|
|
||||||
} from "../../../features/Events/EventApi";
|
|
||||||
import { CalendarEvent } from "../../../features/Events/EventsTypes";
|
import { CalendarEvent } from "../../../features/Events/EventsTypes";
|
||||||
|
import { PartStat } from "../../../features/User/models/attendee";
|
||||||
|
import { createAttendee } from "../../../features/User/models/attendee.mapper";
|
||||||
import { userData } from "../../../features/User/userDataTypes";
|
import { userData } from "../../../features/User/userDataTypes";
|
||||||
|
import { buildFamilyName } from "../../../utils/buildFamilyName";
|
||||||
import { getCalendarRange } from "../../../utils/dateUtils";
|
import { getCalendarRange } from "../../../utils/dateUtils";
|
||||||
import { refreshCalendars } from "../utils/eventUtils";
|
import { refreshCalendars } from "../utils/eventUtils";
|
||||||
|
|
||||||
|
function updateEventAttendees(
|
||||||
|
event: CalendarEvent,
|
||||||
|
user: userData | undefined,
|
||||||
|
rsvp: PartStat
|
||||||
|
) {
|
||||||
|
if (!user) {
|
||||||
|
throw new Error("Cannot update attendees without user data");
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventHasNoAttendees = !event?.attendee || event.attendee.length === 0;
|
||||||
|
const isOrganizer =
|
||||||
|
!event.organizer ||
|
||||||
|
event.organizer.cal_address?.toLowerCase() === user.email?.toLowerCase();
|
||||||
|
if (eventHasNoAttendees) {
|
||||||
|
const userdata = createAttendee({
|
||||||
|
cal_address: user.email,
|
||||||
|
cn: buildFamilyName(user.given_name, user.family_name, user.email),
|
||||||
|
role: isOrganizer ? "CHAIR" : "REQ-PARTICIPANT",
|
||||||
|
partstat: rsvp,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
organizer: isOrganizer ? userdata : event.organizer,
|
||||||
|
attendee: [userdata],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
attendee: (() => {
|
||||||
|
const userEmailLower = user.email?.toLowerCase();
|
||||||
|
const userExists = event.attendee.some(
|
||||||
|
(attendee) => attendee.cal_address?.toLowerCase() === userEmailLower
|
||||||
|
);
|
||||||
|
|
||||||
|
const updatedAttendees = event.attendee.map((attendeeData) =>
|
||||||
|
attendeeData.cal_address?.toLowerCase() === userEmailLower
|
||||||
|
? { ...attendeeData, partstat: rsvp }
|
||||||
|
: attendeeData
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!userExists) {
|
||||||
|
const newUserAttendee = createAttendee({
|
||||||
|
cal_address: user.email,
|
||||||
|
cn: buildFamilyName(user.given_name, user.family_name, user.email),
|
||||||
|
role: "REQ-PARTICIPANT",
|
||||||
|
partstat: rsvp,
|
||||||
|
});
|
||||||
|
return [...updatedAttendees, newUserAttendee];
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedAttendees;
|
||||||
|
})(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSoloRSVP(
|
||||||
|
dispatch: ThunkDispatch<any, any, any>,
|
||||||
|
calendar: Calendar,
|
||||||
|
event: CalendarEvent
|
||||||
|
) {
|
||||||
|
dispatch(updateEventInstanceAsync({ cal: calendar, event }));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleAllRSVP(
|
||||||
|
dispatch: ThunkDispatch<any, any, any>,
|
||||||
|
event: CalendarEvent,
|
||||||
|
userEmail: string,
|
||||||
|
rsvp: PartStat,
|
||||||
|
calendars: Calendar[]
|
||||||
|
) {
|
||||||
|
const calendarRange = getCalendarRange(new Date(event.start));
|
||||||
|
await updateSeriesPartstat(event, userEmail, rsvp);
|
||||||
|
await refreshCalendars(dispatch, calendars, calendarRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDefaultRSVP(
|
||||||
|
dispatch: ThunkDispatch<any, any, any>,
|
||||||
|
calendar: Calendar,
|
||||||
|
newEvent: CalendarEvent
|
||||||
|
) {
|
||||||
|
dispatch(putEventAsync({ cal: calendar, newEvent }));
|
||||||
|
}
|
||||||
|
|
||||||
export async function handleRSVP(
|
export async function handleRSVP(
|
||||||
dispatch: ThunkDispatch<any, any, any>,
|
dispatch: ThunkDispatch<any, any, any>,
|
||||||
calendar: Calendars,
|
calendar: Calendar,
|
||||||
user: { userData: userData },
|
user: userData | undefined,
|
||||||
event: CalendarEvent,
|
event: CalendarEvent,
|
||||||
rsvp: string,
|
rsvp: PartStat,
|
||||||
onClose?: (event: {}, reason: "backdropClick" | "escapeKeyDown") => void,
|
|
||||||
typeOfAction?: string,
|
typeOfAction?: string,
|
||||||
calendars?: Calendars[]
|
calendars?: Calendar[]
|
||||||
) {
|
) {
|
||||||
const newEvent = {
|
const newEvent = {
|
||||||
...event,
|
...event,
|
||||||
attendee: event.attendee?.map((a) =>
|
...updateEventAttendees(event, user, rsvp),
|
||||||
a.cal_address === user.userData?.email ? { ...a, partstat: rsvp } : a
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeOfAction === "solo") {
|
if (typeOfAction === "solo") {
|
||||||
dispatch(updateEventInstanceAsync({ cal: calendar, event: newEvent }));
|
await handleSoloRSVP(dispatch, calendar, newEvent);
|
||||||
} else if (typeOfAction === "all") {
|
} else if (typeOfAction === "all") {
|
||||||
const calendarRange = getCalendarRange(new Date(event.start));
|
if (!calendars || calendars.length === 0) {
|
||||||
|
throw new Error("Cannot update all occurrences without calendar list");
|
||||||
// Update PARTSTAT on ALL VEVENTs (master + exceptions)
|
|
||||||
await updateSeriesPartstat(event, user.userData?.email, rsvp);
|
|
||||||
|
|
||||||
if (calendars) {
|
|
||||||
await refreshCalendars(dispatch, calendars, calendarRange);
|
|
||||||
}
|
}
|
||||||
|
if (!user?.email) {
|
||||||
|
throw new Error("Cannot update all occurrences without user email");
|
||||||
|
}
|
||||||
|
await handleAllRSVP(dispatch, event, user.email, rsvp, calendars);
|
||||||
} else {
|
} else {
|
||||||
dispatch(putEventAsync({ cal: calendar, newEvent }));
|
await handleDefaultRSVP(dispatch, calendar, newEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +131,7 @@ export function handleDelete(
|
|||||||
typeOfAction: "solo" | "all" | undefined,
|
typeOfAction: "solo" | "all" | undefined,
|
||||||
onClose: (event: {}, reason: "backdropClick" | "escapeKeyDown") => void,
|
onClose: (event: {}, reason: "backdropClick" | "escapeKeyDown") => void,
|
||||||
dispatch: Function,
|
dispatch: Function,
|
||||||
calendar: Calendars,
|
calendar: Calendar,
|
||||||
event: CalendarEvent,
|
event: CalendarEvent,
|
||||||
calId: string,
|
calId: string,
|
||||||
eventId: string
|
eventId: string
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import {
|
|||||||
getCalendarDetailAsync,
|
getCalendarDetailAsync,
|
||||||
getCalendarsListAsync,
|
getCalendarsListAsync,
|
||||||
} from "../../../features/Calendars/CalendarSlice";
|
} from "../../../features/Calendars/CalendarSlice";
|
||||||
import { Calendars } from "../../../features/Calendars/CalendarTypes";
|
import { Calendar } from "../../../features/Calendars/CalendarTypes";
|
||||||
import { userAttendee } from "../../../features/User/userDataTypes";
|
import { userAttendee } from "../../../features/User/models/attendee";
|
||||||
import { formatDateToYYYYMMDDTHHMMSS } from "../../../utils/dateUtils";
|
import { formatDateToYYYYMMDDTHHMMSS } from "../../../utils/dateUtils";
|
||||||
|
|
||||||
export function renderAttendeeBadge(
|
export function renderAttendeeBadge(
|
||||||
@@ -111,7 +111,7 @@ export function stringAvatar(name: string) {
|
|||||||
|
|
||||||
export async function refreshCalendars(
|
export async function refreshCalendars(
|
||||||
dispatch: ThunkDispatch<any, any, any>,
|
dispatch: ThunkDispatch<any, any, any>,
|
||||||
calendars: Calendars[],
|
calendars: Calendar[],
|
||||||
calendarRange: { start: Date; end: Date },
|
calendarRange: { start: Date; end: Date },
|
||||||
calType?: "temp"
|
calType?: "temp"
|
||||||
) {
|
) {
|
||||||
@@ -163,7 +163,7 @@ export async function refreshCalendars(
|
|||||||
|
|
||||||
export async function refreshSingularCalendar(
|
export async function refreshSingularCalendar(
|
||||||
dispatch: ThunkDispatch<any, any, any>,
|
dispatch: ThunkDispatch<any, any, any>,
|
||||||
calendar: Calendars,
|
calendar: Calendar,
|
||||||
calendarRange: { start: Date; end: Date },
|
calendarRange: { start: Date; end: Date },
|
||||||
calType?: "temp"
|
calType?: "temp"
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -24,10 +24,11 @@ import { useI18n } from "twake-i18n";
|
|||||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||||
import { searchEventsAsync } from "../../features/Search/SearchSlice";
|
import { searchEventsAsync } from "../../features/Search/SearchSlice";
|
||||||
import { setView } from "../../features/Settings/SettingsSlice";
|
import { setView } from "../../features/Settings/SettingsSlice";
|
||||||
import { userAttendee } from "../../features/User/userDataTypes";
|
import { userAttendee } from "../../features/User/models/attendee";
|
||||||
import UserSearch from "../Attendees/AttendeeSearch";
|
import UserSearch from "../Attendees/AttendeeSearch";
|
||||||
import { CalendarItemList } from "../Calendar/CalendarItemList";
|
import { CalendarItemList } from "../Calendar/CalendarItemList";
|
||||||
import { PeopleSearch, User } from "../Attendees/PeopleSearch";
|
import { PeopleSearch, User } from "../Attendees/PeopleSearch";
|
||||||
|
import { createAttendee } from "../../features/User/models/attendee.mapper";
|
||||||
|
|
||||||
export default function SearchBar() {
|
export default function SearchBar() {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
@@ -151,14 +152,12 @@ export default function SearchBar() {
|
|||||||
if (contacts.length > 0) {
|
if (contacts.length > 0) {
|
||||||
handleSearch("", {
|
handleSearch("", {
|
||||||
...filters,
|
...filters,
|
||||||
organizers: contacts.map((c) => ({
|
organizers: contacts.map((contact) =>
|
||||||
cal_address: c.email || c.displayName || "",
|
createAttendee({
|
||||||
cutype: "INDIVIDUAL",
|
cal_address: contact.email,
|
||||||
cn: c.displayName || c.email,
|
cn: contact.displayName,
|
||||||
role: "Participant",
|
})
|
||||||
rsvp: "TRUE",
|
),
|
||||||
partstat: "",
|
|
||||||
})),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -334,14 +333,12 @@ export default function SearchBar() {
|
|||||||
handleFilterChange("keywords", query);
|
handleFilterChange("keywords", query);
|
||||||
handleFilterChange(
|
handleFilterChange(
|
||||||
"organizers",
|
"organizers",
|
||||||
selectedContacts.map((a: User) => ({
|
selectedContacts.map((attendee: User) =>
|
||||||
cn: a.displayName,
|
createAttendee({
|
||||||
cal_address: a.email || "",
|
cal_address: attendee.email,
|
||||||
partstat: "NEEDS-ACTION",
|
cn: attendee.displayName,
|
||||||
rsvp: "FALSE",
|
})
|
||||||
role: "REQ-PARTICIPANT",
|
)
|
||||||
cutype: "INDIVIDUAL",
|
|
||||||
}))
|
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
import { Calendars } from "./CalendarTypes";
|
import { Calendar } from "./CalendarTypes";
|
||||||
import { CalendarEvent } from "../Events/EventsTypes";
|
import { CalendarEvent } from "../Events/EventsTypes";
|
||||||
import {
|
import {
|
||||||
addSharedCalendar,
|
addSharedCalendar,
|
||||||
@@ -39,7 +39,7 @@ interface RejectedError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getCalendarsListAsync = createAsyncThunk<
|
export const getCalendarsListAsync = createAsyncThunk<
|
||||||
{ importedCalendars: Record<string, Calendars>; errors: string }, // Return type
|
{ importedCalendars: Record<string, Calendar>; errors: string }, // Return type
|
||||||
void, // Arg type
|
void, // Arg type
|
||||||
{ rejectValue: RejectedError; state: any } // ThunkAPI config
|
{ rejectValue: RejectedError; state: any } // ThunkAPI config
|
||||||
>("calendars/getCalendars", async (_, { rejectWithValue, getState }) => {
|
>("calendars/getCalendars", async (_, { rejectWithValue, getState }) => {
|
||||||
@@ -52,7 +52,7 @@ export const getCalendarsListAsync = createAsyncThunk<
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const importedCalendars: Record<string, Calendars> = {};
|
const importedCalendars: Record<string, Calendar> = {};
|
||||||
const user = (await getOpenPaasUser()) as Record<string, string>;
|
const user = (await getOpenPaasUser()) as Record<string, string>;
|
||||||
const calendars = (await getCalendars(user.id)) as Record<string, any>;
|
const calendars = (await getCalendars(user.id)) as Record<string, any>;
|
||||||
const rawCalendars = calendars._embedded["dav:calendar"] as Record<
|
const rawCalendars = calendars._embedded["dav:calendar"] as Record<
|
||||||
@@ -159,12 +159,12 @@ export const getCalendarsListAsync = createAsyncThunk<
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const getTempCalendarsListAsync = createAsyncThunk<
|
export const getTempCalendarsListAsync = createAsyncThunk<
|
||||||
Record<string, Calendars>,
|
Record<string, Calendar>,
|
||||||
User,
|
User,
|
||||||
{ rejectValue: RejectedError }
|
{ rejectValue: RejectedError }
|
||||||
>("calendars/getTempCalendars", async (tempUser, { rejectWithValue }) => {
|
>("calendars/getTempCalendars", async (tempUser, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const importedCalendars: Record<string, Calendars> = {};
|
const importedCalendars: Record<string, Calendar> = {};
|
||||||
|
|
||||||
const calendars = (await getCalendars(
|
const calendars = (await getCalendars(
|
||||||
tempUser.openpaasId ?? "",
|
tempUser.openpaasId ?? "",
|
||||||
@@ -267,7 +267,7 @@ export const getCalendarDetailAsync = createAsyncThunk<
|
|||||||
|
|
||||||
export const putEventAsync = createAsyncThunk<
|
export const putEventAsync = createAsyncThunk<
|
||||||
{ calId: string; events: CalendarEvent[]; calType?: "temp" },
|
{ calId: string; events: CalendarEvent[]; calType?: "temp" },
|
||||||
{ cal: Calendars; newEvent: CalendarEvent; calType?: "temp" },
|
{ cal: Calendar; newEvent: CalendarEvent; calType?: "temp" },
|
||||||
{ rejectValue: RejectedError }
|
{ rejectValue: RejectedError }
|
||||||
>(
|
>(
|
||||||
"calendars/putEvent",
|
"calendars/putEvent",
|
||||||
@@ -395,7 +395,7 @@ export const removeCalendarAsync = createAsyncThunk<
|
|||||||
|
|
||||||
export const moveEventAsync = createAsyncThunk<
|
export const moveEventAsync = createAsyncThunk<
|
||||||
{ calId: string; events: CalendarEvent[] },
|
{ calId: string; events: CalendarEvent[] },
|
||||||
{ cal: Calendars; newEvent: CalendarEvent; newURL: string },
|
{ cal: Calendar; newEvent: CalendarEvent; newURL: string },
|
||||||
{ rejectValue: RejectedError }
|
{ rejectValue: RejectedError }
|
||||||
>(
|
>(
|
||||||
"calendars/moveEvent",
|
"calendars/moveEvent",
|
||||||
@@ -490,7 +490,7 @@ export const deleteEventAsync = createAsyncThunk<
|
|||||||
|
|
||||||
export const deleteEventInstanceAsync = createAsyncThunk<
|
export const deleteEventInstanceAsync = createAsyncThunk<
|
||||||
{ calId: string; eventId: string },
|
{ calId: string; eventId: string },
|
||||||
{ cal: Calendars; event: CalendarEvent },
|
{ cal: Calendar; event: CalendarEvent },
|
||||||
{ rejectValue: RejectedError }
|
{ rejectValue: RejectedError }
|
||||||
>("calendars/delEventInstance", async ({ cal, event }, { rejectWithValue }) => {
|
>("calendars/delEventInstance", async ({ cal, event }, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
@@ -506,7 +506,7 @@ export const deleteEventInstanceAsync = createAsyncThunk<
|
|||||||
|
|
||||||
export const updateEventInstanceAsync = createAsyncThunk<
|
export const updateEventInstanceAsync = createAsyncThunk<
|
||||||
{ calId: string; event: CalendarEvent },
|
{ calId: string; event: CalendarEvent },
|
||||||
{ cal: Calendars; event: CalendarEvent },
|
{ cal: Calendar; event: CalendarEvent },
|
||||||
{ rejectValue: RejectedError }
|
{ rejectValue: RejectedError }
|
||||||
>(
|
>(
|
||||||
"calendars/updateEventInstance",
|
"calendars/updateEventInstance",
|
||||||
@@ -525,7 +525,7 @@ export const updateEventInstanceAsync = createAsyncThunk<
|
|||||||
|
|
||||||
export const updateSeriesAsync = createAsyncThunk<
|
export const updateSeriesAsync = createAsyncThunk<
|
||||||
void,
|
void,
|
||||||
{ cal: Calendars; event: CalendarEvent; removeOverrides?: boolean },
|
{ cal: Calendar; event: CalendarEvent; removeOverrides?: boolean },
|
||||||
{ rejectValue: RejectedError }
|
{ rejectValue: RejectedError }
|
||||||
>(
|
>(
|
||||||
"calendars/updateSeries",
|
"calendars/updateSeries",
|
||||||
@@ -659,13 +659,13 @@ export const importEventFromFileAsync = createAsyncThunk<
|
|||||||
const CalendarSlice = createSlice({
|
const CalendarSlice = createSlice({
|
||||||
name: "calendars",
|
name: "calendars",
|
||||||
initialState: {
|
initialState: {
|
||||||
list: {} as Record<string, Calendars>,
|
list: {} as Record<string, Calendar>,
|
||||||
templist: {} as Record<string, Calendars>,
|
templist: {} as Record<string, Calendar>,
|
||||||
pending: false,
|
pending: false,
|
||||||
error: null as string | null,
|
error: null as string | null,
|
||||||
} as {
|
} as {
|
||||||
list: Record<string, Calendars>;
|
list: Record<string, Calendar>;
|
||||||
templist: Record<string, Calendars>;
|
templist: Record<string, Calendar>;
|
||||||
pending: boolean;
|
pending: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
},
|
},
|
||||||
@@ -675,7 +675,7 @@ const CalendarSlice = createSlice({
|
|||||||
action: PayloadAction<Record<string, string | Record<string, string>>>
|
action: PayloadAction<Record<string, string | Record<string, string>>>
|
||||||
) => {
|
) => {
|
||||||
const id = Date.now().toString(36);
|
const id = Date.now().toString(36);
|
||||||
state.list[id] = {} as Calendars;
|
state.list[id] = {} as Calendar;
|
||||||
state.list[id].name = action.payload.name as string;
|
state.list[id].name = action.payload.name as string;
|
||||||
state.list[id].color = action.payload.color as Record<string, string>;
|
state.list[id].color = action.payload.color as Record<string, string>;
|
||||||
state.list[id].description = action.payload.description as string;
|
state.list[id].description = action.payload.description as string;
|
||||||
@@ -753,7 +753,7 @@ const CalendarSlice = createSlice({
|
|||||||
(
|
(
|
||||||
state,
|
state,
|
||||||
action: PayloadAction<{
|
action: PayloadAction<{
|
||||||
importedCalendars: Record<string, Calendars>;
|
importedCalendars: Record<string, Calendar>;
|
||||||
errors: string;
|
errors: string;
|
||||||
}>
|
}>
|
||||||
) => {
|
) => {
|
||||||
@@ -766,7 +766,7 @@ const CalendarSlice = createSlice({
|
|||||||
)
|
)
|
||||||
.addCase(
|
.addCase(
|
||||||
getTempCalendarsListAsync.fulfilled,
|
getTempCalendarsListAsync.fulfilled,
|
||||||
(state, action: PayloadAction<Record<string, Calendars>>) => {
|
(state, action: PayloadAction<Record<string, Calendar>>) => {
|
||||||
state.pending = false;
|
state.pending = false;
|
||||||
Object.keys(action.payload).forEach(
|
Object.keys(action.payload).forEach(
|
||||||
(id) => (state.templist[id] = action.payload[id])
|
(id) => (state.templist[id] = action.payload[id])
|
||||||
@@ -823,7 +823,7 @@ const CalendarSlice = createSlice({
|
|||||||
state[type][action.payload.calId] = {
|
state[type][action.payload.calId] = {
|
||||||
id: action.payload.calId,
|
id: action.payload.calId,
|
||||||
events: {},
|
events: {},
|
||||||
} as Calendars;
|
} as Calendar;
|
||||||
}
|
}
|
||||||
action.payload.events.forEach((event) => {
|
action.payload.events.forEach((event) => {
|
||||||
state[type][action.payload.calId].events[event.uid] = event;
|
state[type][action.payload.calId].events[event.uid] = event;
|
||||||
@@ -853,7 +853,7 @@ const CalendarSlice = createSlice({
|
|||||||
state.list[action.payload.calId] = {
|
state.list[action.payload.calId] = {
|
||||||
id: action.payload.calId,
|
id: action.payload.calId,
|
||||||
events: {},
|
events: {},
|
||||||
} as Calendars;
|
} as Calendar;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.list[action.payload.calId].events[action.payload.event.uid] =
|
state.list[action.payload.calId].events[action.payload.event.uid] =
|
||||||
@@ -871,7 +871,7 @@ const CalendarSlice = createSlice({
|
|||||||
state.list[action.payload.calId] = {
|
state.list[action.payload.calId] = {
|
||||||
id: action.payload.calId,
|
id: action.payload.calId,
|
||||||
events: {},
|
events: {},
|
||||||
} as Calendars;
|
} as Calendar;
|
||||||
}
|
}
|
||||||
action.payload.events.forEach((event) => {
|
action.payload.events.forEach((event) => {
|
||||||
state.list[action.payload.calId].events[event.uid] = event;
|
state.list[action.payload.calId].events[event.uid] = event;
|
||||||
@@ -932,7 +932,7 @@ const CalendarSlice = createSlice({
|
|||||||
owner: action.payload.owner,
|
owner: action.payload.owner,
|
||||||
ownerEmails: action.payload.ownerEmails,
|
ownerEmails: action.payload.ownerEmails,
|
||||||
events: {},
|
events: {},
|
||||||
} as Calendars;
|
} as Calendar;
|
||||||
state.error = null;
|
state.error = null;
|
||||||
})
|
})
|
||||||
.addCase(patchCalendarAsync.fulfilled, (state, action) => {
|
.addCase(patchCalendarAsync.fulfilled, (state, action) => {
|
||||||
@@ -973,7 +973,7 @@ const CalendarSlice = createSlice({
|
|||||||
events: {},
|
events: {},
|
||||||
owner: action.payload.owner,
|
owner: action.payload.owner,
|
||||||
ownerEmails: action.payload.ownerEmails,
|
ownerEmails: action.payload.ownerEmails,
|
||||||
} as Calendars;
|
} as Calendar;
|
||||||
state.error = null;
|
state.error = null;
|
||||||
})
|
})
|
||||||
.addCase(removeCalendarAsync.fulfilled, (state, action) => {
|
.addCase(removeCalendarAsync.fulfilled, (state, action) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { CalendarEvent } from "../Events/EventsTypes";
|
import { CalendarEvent } from "../Events/EventsTypes";
|
||||||
|
|
||||||
export interface Calendars {
|
export interface Calendar {
|
||||||
id: string;
|
id: string;
|
||||||
link: string;
|
link: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { Box, Typography } from "@mui/material";
|
||||||
|
import { Dispatch, SetStateAction } from "react";
|
||||||
|
import { useI18n } from "twake-i18n";
|
||||||
|
import { Calendar } from "../../Calendars/CalendarTypes";
|
||||||
|
import { userData } from "../../User/userDataTypes";
|
||||||
|
import { ContextualizedEvent } from "../EventsTypes";
|
||||||
|
import { RSVPButton } from "./RSVPButton";
|
||||||
|
|
||||||
|
interface AttendanceValidationProps {
|
||||||
|
contextualizedEvent: ContextualizedEvent;
|
||||||
|
calendarList: Calendar[];
|
||||||
|
user: userData | undefined;
|
||||||
|
setAfterChoiceFunc: Dispatch<SetStateAction<Function | undefined>>;
|
||||||
|
setOpenEditModePopup: Dispatch<SetStateAction<string | null>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AttendanceValidation({
|
||||||
|
contextualizedEvent,
|
||||||
|
calendarList,
|
||||||
|
user,
|
||||||
|
setAfterChoiceFunc,
|
||||||
|
setOpenEditModePopup,
|
||||||
|
}: AttendanceValidationProps) {
|
||||||
|
const { currentUserAttendee, isOwn } = contextualizedEvent;
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
// Check if we should show RSVP buttons
|
||||||
|
const hasNoAttendeesOrOrganizer =
|
||||||
|
!(contextualizedEvent.event?.attendee?.length > 0) &&
|
||||||
|
!contextualizedEvent.event?.organizer;
|
||||||
|
|
||||||
|
if (!((currentUserAttendee || hasNoAttendeesOrOrganizer) && isOwn)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const commonButtonProps = {
|
||||||
|
contextualizedEvent,
|
||||||
|
user,
|
||||||
|
calendarList,
|
||||||
|
setAfterChoiceFunc,
|
||||||
|
setOpenEditModePopup,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography sx={{ marginRight: 2 }}>
|
||||||
|
{t("eventPreview.attendingQuestion")}
|
||||||
|
</Typography>
|
||||||
|
<Box display="flex" gap="15px" alignItems="center">
|
||||||
|
<RSVPButton rsvpValue="ACCEPTED" {...commonButtonProps} />
|
||||||
|
<RSVPButton rsvpValue="TENTATIVE" {...commonButtonProps} />
|
||||||
|
<RSVPButton rsvpValue="DECLINED" {...commonButtonProps} />
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { Button } from "@mui/material";
|
||||||
|
import { Dispatch, SetStateAction } from "react";
|
||||||
|
import { useI18n } from "twake-i18n";
|
||||||
|
import { useAppDispatch } from "../../../app/hooks";
|
||||||
|
import { Calendar } from "../../Calendars/CalendarTypes";
|
||||||
|
import { PartStat } from "../../User/models/attendee";
|
||||||
|
import { userData } from "../../User/userDataTypes";
|
||||||
|
import { ContextualizedEvent } from "../EventsTypes";
|
||||||
|
import { handleRSVPClick } from "./handleRSVPClick";
|
||||||
|
|
||||||
|
const rsvpColor: Record<PartStat, "success" | "error" | "warning" | "primary"> =
|
||||||
|
{
|
||||||
|
ACCEPTED: "success",
|
||||||
|
DECLINED: "error",
|
||||||
|
TENTATIVE: "warning",
|
||||||
|
"NEEDS-ACTION": "primary",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
interface RSVPButtonProps {
|
||||||
|
rsvpValue: PartStat;
|
||||||
|
contextualizedEvent: ContextualizedEvent;
|
||||||
|
user: userData | undefined;
|
||||||
|
calendarList: Calendar[];
|
||||||
|
setAfterChoiceFunc: Dispatch<SetStateAction<Function | undefined>>;
|
||||||
|
setOpenEditModePopup: Dispatch<SetStateAction<string | null>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RSVPButton({
|
||||||
|
rsvpValue,
|
||||||
|
contextualizedEvent,
|
||||||
|
user,
|
||||||
|
calendarList,
|
||||||
|
setAfterChoiceFunc,
|
||||||
|
setOpenEditModePopup,
|
||||||
|
}: RSVPButtonProps) {
|
||||||
|
const { t } = useI18n();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { currentUserAttendee } = contextualizedEvent;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant={
|
||||||
|
currentUserAttendee?.partstat === rsvpValue ? "contained" : "outlined"
|
||||||
|
}
|
||||||
|
color={
|
||||||
|
currentUserAttendee?.partstat === rsvpValue
|
||||||
|
? rsvpColor[rsvpValue]
|
||||||
|
: "primary"
|
||||||
|
}
|
||||||
|
size="large"
|
||||||
|
sx={{ borderRadius: "50px" }}
|
||||||
|
onClick={() =>
|
||||||
|
handleRSVPClick(
|
||||||
|
rsvpValue,
|
||||||
|
contextualizedEvent,
|
||||||
|
user,
|
||||||
|
calendarList,
|
||||||
|
setAfterChoiceFunc,
|
||||||
|
setOpenEditModePopup,
|
||||||
|
dispatch
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t(`eventPreview.${rsvpValue}`)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { Dispatch, SetStateAction } from "react";
|
||||||
|
import { AppDispatch } from "../../../app/store";
|
||||||
|
import { handleRSVP } from "../../../components/Event/eventHandlers/eventHandlers";
|
||||||
|
import { Calendar } from "../../Calendars/CalendarTypes";
|
||||||
|
import { PartStat } from "../../User/models/attendee";
|
||||||
|
import { userData } from "../../User/userDataTypes";
|
||||||
|
import { ContextualizedEvent } from "../EventsTypes";
|
||||||
|
|
||||||
|
export async function handleRSVPClick(
|
||||||
|
rsvp: PartStat,
|
||||||
|
contextualizedEvent: ContextualizedEvent,
|
||||||
|
user: userData | undefined,
|
||||||
|
calendarList: Calendar[],
|
||||||
|
setAfterChoiceFunc: Dispatch<SetStateAction<Function | undefined>>,
|
||||||
|
setOpenEditModePopup: Dispatch<SetStateAction<string | null>>,
|
||||||
|
dispatch: AppDispatch
|
||||||
|
) {
|
||||||
|
const { isRecurring, calendar, event } = contextualizedEvent;
|
||||||
|
if (isRecurring) {
|
||||||
|
setAfterChoiceFunc(() => async (type: string) => {
|
||||||
|
try {
|
||||||
|
await handleRSVP(
|
||||||
|
dispatch,
|
||||||
|
calendar,
|
||||||
|
user,
|
||||||
|
event,
|
||||||
|
rsvp,
|
||||||
|
type,
|
||||||
|
calendarList
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error handling RSVP:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setOpenEditModePopup("attendance");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await handleRSVP(dispatch, calendar, user, event, rsvp);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error handling RSVP:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,10 +36,7 @@ import {
|
|||||||
import ResponsiveDialog from "../../components/Dialog/ResponsiveDialog";
|
import ResponsiveDialog from "../../components/Dialog/ResponsiveDialog";
|
||||||
import { EditModeDialog } from "../../components/Event/EditModeDialog";
|
import { EditModeDialog } from "../../components/Event/EditModeDialog";
|
||||||
import EventDuplication from "../../components/Event/EventDuplicate";
|
import EventDuplication from "../../components/Event/EventDuplicate";
|
||||||
import {
|
import { handleDelete } from "../../components/Event/eventHandlers/eventHandlers";
|
||||||
handleDelete,
|
|
||||||
handleRSVP,
|
|
||||||
} from "../../components/Event/eventHandlers/eventHandlers";
|
|
||||||
import { InfoRow } from "../../components/Event/InfoRow";
|
import { InfoRow } from "../../components/Event/InfoRow";
|
||||||
import { renderAttendeeBadge } from "../../components/Event/utils/eventUtils";
|
import { renderAttendeeBadge } from "../../components/Event/utils/eventUtils";
|
||||||
import { getCalendarRange } from "../../utils/dateUtils";
|
import { getCalendarRange } from "../../utils/dateUtils";
|
||||||
@@ -48,8 +45,12 @@ import { dlEvent } from "./EventApi";
|
|||||||
import { CalendarEvent } from "./EventsTypes";
|
import { CalendarEvent } from "./EventsTypes";
|
||||||
import EventUpdateModal from "./EventUpdateModal";
|
import EventUpdateModal from "./EventUpdateModal";
|
||||||
import { useI18n } from "twake-i18n";
|
import { useI18n } from "twake-i18n";
|
||||||
import { userAttendee } from "../User/userDataTypes";
|
import { userAttendee } from "../User/models/attendee";
|
||||||
import { browserDefaultTimeZone } from "../../utils/timezone";
|
import { browserDefaultTimeZone } from "../../utils/timezone";
|
||||||
|
import { AttendanceValidation } from "./AttendanceValidation/AttendanceValidation";
|
||||||
|
import { Calendar } from "../Calendars/CalendarTypes";
|
||||||
|
import { userData } from "../User/userDataTypes";
|
||||||
|
import { createEventContext } from "./createEventContext";
|
||||||
|
|
||||||
export default function EventPreviewModal({
|
export default function EventPreviewModal({
|
||||||
eventId,
|
eventId,
|
||||||
@@ -77,12 +78,13 @@ export default function EventPreviewModal({
|
|||||||
? calendars.templist[calId]
|
? calendars.templist[calId]
|
||||||
: calendars.list[calId];
|
: calendars.list[calId];
|
||||||
const event = calendar.events[eventId];
|
const event = calendar.events[eventId];
|
||||||
const user = useAppSelector((state) => state.user);
|
const user = useAppSelector((state) => state.user.userData);
|
||||||
|
if (!user) return null;
|
||||||
|
|
||||||
const isRecurring = event?.uid?.includes("/");
|
const isRecurring = event?.uid?.includes("/");
|
||||||
const isOwn = calendar.ownerEmails?.includes(user.userData?.email);
|
const isOwn = calendar.ownerEmails?.includes(user.email);
|
||||||
const isOrganizer = event.organizer
|
const isOrganizer = event.organizer
|
||||||
? user.userData?.email === event.organizer.cal_address
|
? user.email === event.organizer.cal_address
|
||||||
: isOwn;
|
: isOwn;
|
||||||
const [showAllAttendees, setShowAllAttendees] = useState(false);
|
const [showAllAttendees, setShowAllAttendees] = useState(false);
|
||||||
const [openUpdateModal, setOpenUpdateModal] = useState(false);
|
const [openUpdateModal, setOpenUpdateModal] = useState(false);
|
||||||
@@ -274,8 +276,9 @@ export default function EventPreviewModal({
|
|||||||
: attendees.slice(0, attendeeDisplayLimit);
|
: attendees.slice(0, attendeeDisplayLimit);
|
||||||
|
|
||||||
const currentUserAttendee = event.attendee?.find(
|
const currentUserAttendee = event.attendee?.find(
|
||||||
(person) => person.cal_address === user.userData?.email
|
(person) => person.cal_address === user.email
|
||||||
);
|
);
|
||||||
|
const contextualizedEvent = createEventContext(event, calendar, user);
|
||||||
|
|
||||||
const organizer = event.attendee?.find(
|
const organizer = event.attendee?.find(
|
||||||
(a) => a.cal_address === event.organizer?.cal_address
|
(a) => a.cal_address === event.organizer?.cal_address
|
||||||
@@ -365,7 +368,7 @@ export default function EventPreviewModal({
|
|||||||
window.open(
|
window.open(
|
||||||
`${mailSpaUrl}/mailto/?uri=mailto:${event.attendee
|
`${mailSpaUrl}/mailto/?uri=mailto:${event.attendee
|
||||||
.map((a) => a.cal_address)
|
.map((a) => a.cal_address)
|
||||||
.filter((mail) => mail !== user.userData?.email)
|
.filter((mail) => mail !== user.email)
|
||||||
.join(",")}?subject=${event.title}`
|
.join(",")}?subject=${event.title}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -467,147 +470,13 @@ export default function EventPreviewModal({
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
currentUserAttendee &&
|
<AttendanceValidation
|
||||||
isOwn && (
|
contextualizedEvent={contextualizedEvent}
|
||||||
<>
|
calendarList={calendarList}
|
||||||
<>
|
user={user}
|
||||||
<Typography sx={{ marginRight: 2 }}>
|
setAfterChoiceFunc={setAfterChoiceFunc}
|
||||||
{t("eventPreview.attendingQuestion")}
|
setOpenEditModePopup={setOpenEditModePopup}
|
||||||
</Typography>
|
/>
|
||||||
<Box display="flex" gap="15px" alignItems="center">
|
|
||||||
<Button
|
|
||||||
variant={
|
|
||||||
currentUserAttendee?.partstat === "ACCEPTED"
|
|
||||||
? "contained"
|
|
||||||
: "outlined"
|
|
||||||
}
|
|
||||||
color={
|
|
||||||
currentUserAttendee?.partstat === "ACCEPTED"
|
|
||||||
? "success"
|
|
||||||
: "primary"
|
|
||||||
}
|
|
||||||
size="large"
|
|
||||||
sx={{ borderRadius: "50px" }}
|
|
||||||
onClick={() => {
|
|
||||||
if (isRecurring) {
|
|
||||||
setAfterChoiceFunc(
|
|
||||||
() => (type: string) =>
|
|
||||||
handleRSVP(
|
|
||||||
dispatch,
|
|
||||||
calendar,
|
|
||||||
user,
|
|
||||||
event,
|
|
||||||
"ACCEPTED",
|
|
||||||
onClose,
|
|
||||||
type,
|
|
||||||
calendarList
|
|
||||||
)
|
|
||||||
);
|
|
||||||
setOpenEditModePopup("attendance");
|
|
||||||
} else {
|
|
||||||
handleRSVP(
|
|
||||||
dispatch,
|
|
||||||
calendar,
|
|
||||||
user,
|
|
||||||
event,
|
|
||||||
"ACCEPTED",
|
|
||||||
onClose
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("eventPreview.accept")}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant={
|
|
||||||
currentUserAttendee?.partstat === "TENTATIVE"
|
|
||||||
? "contained"
|
|
||||||
: "outlined"
|
|
||||||
}
|
|
||||||
color={
|
|
||||||
currentUserAttendee?.partstat === "TENTATIVE"
|
|
||||||
? "warning"
|
|
||||||
: "primary"
|
|
||||||
}
|
|
||||||
size="large"
|
|
||||||
sx={{ borderRadius: "50px" }}
|
|
||||||
onClick={() => {
|
|
||||||
if (isRecurring) {
|
|
||||||
setAfterChoiceFunc(
|
|
||||||
() => (type: string) =>
|
|
||||||
handleRSVP(
|
|
||||||
dispatch,
|
|
||||||
calendar,
|
|
||||||
user,
|
|
||||||
event,
|
|
||||||
"TENTATIVE",
|
|
||||||
onClose,
|
|
||||||
type,
|
|
||||||
calendarList
|
|
||||||
)
|
|
||||||
);
|
|
||||||
setOpenEditModePopup("attendance");
|
|
||||||
} else {
|
|
||||||
handleRSVP(
|
|
||||||
dispatch,
|
|
||||||
calendar,
|
|
||||||
user,
|
|
||||||
event,
|
|
||||||
"TENTATIVE",
|
|
||||||
onClose
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("eventPreview.maybe")}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant={
|
|
||||||
currentUserAttendee?.partstat === "DECLINED"
|
|
||||||
? "contained"
|
|
||||||
: "outlined"
|
|
||||||
}
|
|
||||||
color={
|
|
||||||
currentUserAttendee?.partstat === "DECLINED"
|
|
||||||
? "error"
|
|
||||||
: "primary"
|
|
||||||
}
|
|
||||||
size="large"
|
|
||||||
sx={{ borderRadius: "50px" }}
|
|
||||||
onClick={() => {
|
|
||||||
if (isRecurring) {
|
|
||||||
setAfterChoiceFunc(
|
|
||||||
() => (type: string) =>
|
|
||||||
handleRSVP(
|
|
||||||
dispatch,
|
|
||||||
calendar,
|
|
||||||
user,
|
|
||||||
event,
|
|
||||||
"DECLINED",
|
|
||||||
onClose,
|
|
||||||
type,
|
|
||||||
calendarList
|
|
||||||
)
|
|
||||||
);
|
|
||||||
setOpenEditModePopup("attendance");
|
|
||||||
} else {
|
|
||||||
handleRSVP(
|
|
||||||
dispatch,
|
|
||||||
calendar,
|
|
||||||
user,
|
|
||||||
event,
|
|
||||||
"DECLINED",
|
|
||||||
onClose
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("eventPreview.decline")}
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{((event.class !== "PRIVATE" && !isOwn) || isOwn) && (
|
{((event.class !== "PRIVATE" && !isOwn) || isOwn) && (
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import React, {
|
|||||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||||
import { ResponsiveDialog } from "../../components/Dialog";
|
import { ResponsiveDialog } from "../../components/Dialog";
|
||||||
import { putEventAsync } from "../Calendars/CalendarSlice";
|
import { putEventAsync } from "../Calendars/CalendarSlice";
|
||||||
import { Calendars } from "../Calendars/CalendarTypes";
|
import { Calendar } from "../Calendars/CalendarTypes";
|
||||||
import { userAttendee } from "../User/userDataTypes";
|
import { userAttendee } from "../User/models/attendee";
|
||||||
import { CalendarEvent, RepetitionObject } from "./EventsTypes";
|
import { CalendarEvent, RepetitionObject } from "./EventsTypes";
|
||||||
import { createSelector } from "@reduxjs/toolkit";
|
import { createSelector } from "@reduxjs/toolkit";
|
||||||
import { TIMEZONES } from "../../utils/timezone-data";
|
import { TIMEZONES } from "../../utils/timezone-data";
|
||||||
@@ -75,11 +75,11 @@ function EventPopover({
|
|||||||
if (id.split("/")[0] === userId) {
|
if (id.split("/")[0] === userId) {
|
||||||
return calendars.list?.[id];
|
return calendars.list?.[id];
|
||||||
}
|
}
|
||||||
return {} as Calendars;
|
return {} as Calendar;
|
||||||
})
|
})
|
||||||
.filter((calendar) => calendar.id)
|
.filter((calendar) => calendar.id)
|
||||||
);
|
);
|
||||||
const userPersonalCalendars: Calendars[] = useAppSelector(
|
const userPersonalCalendars: Calendar[] = useAppSelector(
|
||||||
selectPersonalCalendars
|
selectPersonalCalendars
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -722,10 +722,10 @@ function EventPopover({
|
|||||||
const newEventUID = crypto.randomUUID();
|
const newEventUID = crypto.randomUUID();
|
||||||
|
|
||||||
// Resolve target calendar safely
|
// Resolve target calendar safely
|
||||||
const targetCalendar: Calendars | undefined =
|
const targetCalendar: Calendar | undefined =
|
||||||
calList[calendarid] ||
|
calList[calendarid] ||
|
||||||
userPersonalCalendars[0] ||
|
userPersonalCalendars[0] ||
|
||||||
(Object.values(calList)[0] as Calendars | undefined);
|
(Object.values(calList)[0] as Calendar | undefined);
|
||||||
if (!targetCalendar || !targetCalendar.id) {
|
if (!targetCalendar || !targetCalendar.id) {
|
||||||
console.error("No target calendar available to save event");
|
console.error("No target calendar available to save event");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import {
|
|||||||
updateEventLocal,
|
updateEventLocal,
|
||||||
clearFetchCache,
|
clearFetchCache,
|
||||||
} from "../Calendars/CalendarSlice";
|
} from "../Calendars/CalendarSlice";
|
||||||
import { Calendars } from "../Calendars/CalendarTypes";
|
import { Calendar } from "../Calendars/CalendarTypes";
|
||||||
import { userAttendee } from "../User/userDataTypes";
|
import { userAttendee } from "../User/models/attendee";
|
||||||
import { CalendarEvent, RepetitionObject } from "./EventsTypes";
|
import { CalendarEvent, RepetitionObject } from "./EventsTypes";
|
||||||
import { TIMEZONES } from "../../utils/timezone-data";
|
import { TIMEZONES } from "../../utils/timezone-data";
|
||||||
import { addVideoConferenceToDescription } from "../../utils/videoConferenceUtils";
|
import { addVideoConferenceToDescription } from "../../utils/videoConferenceUtils";
|
||||||
@@ -95,10 +95,10 @@ function EventUpdateModal({
|
|||||||
|
|
||||||
const calendarsList = useAppSelector((state) => state.calendars.list);
|
const calendarsList = useAppSelector((state) => state.calendars.list);
|
||||||
|
|
||||||
const userPersonalCalendars: Calendars[] = useMemo(() => {
|
const userPersonalCalendars: Calendar[] = useMemo(() => {
|
||||||
const allCalendars = Object.values(calendarsList) as Calendars[];
|
const allCalendars = Object.values(calendarsList) as Calendar[];
|
||||||
return allCalendars.filter(
|
return allCalendars.filter(
|
||||||
(c: Calendars) => c.id?.split("/")[0] === user.userData?.openpaasId
|
(c: Calendar) => c.id?.split("/")[0] === user.userData?.openpaasId
|
||||||
);
|
);
|
||||||
}, [calendarsList, user.userData?.openpaasId]);
|
}, [calendarsList, user.userData?.openpaasId]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { userAttendee, userOrganiser } from "../User/userDataTypes";
|
import { Calendar } from "../Calendars/CalendarTypes";
|
||||||
|
import { userAttendee } from "../User/models/attendee";
|
||||||
|
import { userOrganiser } from "../User/userDataTypes";
|
||||||
|
|
||||||
export interface CalendarEvent {
|
export interface CalendarEvent {
|
||||||
URL: string;
|
URL: string;
|
||||||
@@ -39,3 +41,12 @@ export interface AlarmObject {
|
|||||||
trigger: string;
|
trigger: string;
|
||||||
action: string;
|
action: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ContextualizedEvent {
|
||||||
|
event: CalendarEvent;
|
||||||
|
calendar: Calendar;
|
||||||
|
currentUserAttendee: userAttendee | undefined;
|
||||||
|
isOwn: boolean;
|
||||||
|
isRecurring: boolean;
|
||||||
|
isOrganizer: boolean;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Calendar } from "../Calendars/CalendarTypes";
|
||||||
|
import { userData } from "../User/userDataTypes";
|
||||||
|
import { CalendarEvent, ContextualizedEvent } from "./EventsTypes";
|
||||||
|
|
||||||
|
export function createEventContext(
|
||||||
|
event: CalendarEvent,
|
||||||
|
calendar: Calendar,
|
||||||
|
user: userData
|
||||||
|
): ContextualizedEvent {
|
||||||
|
const isOwn = calendar.ownerEmails?.includes(user.email) ?? false;
|
||||||
|
const isRecurring = event?.uid?.includes("/") ?? false;
|
||||||
|
const isOrganizer = event.organizer
|
||||||
|
? user?.email === event.organizer.cal_address
|
||||||
|
: isOwn;
|
||||||
|
const currentUserAttendee = event.attendee?.find(
|
||||||
|
(person) => person.cal_address === user.email
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
event,
|
||||||
|
calendar,
|
||||||
|
currentUserAttendee,
|
||||||
|
isOwn,
|
||||||
|
isRecurring,
|
||||||
|
isOrganizer,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { userAttendee } from "../User/userDataTypes";
|
import { userAttendee } from "../User/models/attendee";
|
||||||
import { AlarmObject, CalendarEvent, RepetitionObject } from "./EventsTypes";
|
import { AlarmObject, CalendarEvent, RepetitionObject } from "./EventsTypes";
|
||||||
import ICAL from "ical.js";
|
import ICAL from "ical.js";
|
||||||
import { TIMEZONES } from "../../utils/timezone-data";
|
import { TIMEZONES } from "../../utils/timezone-data";
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
convertFormDateTimeToISO,
|
convertFormDateTimeToISO,
|
||||||
detectDateTimeFormat,
|
detectDateTimeFormat,
|
||||||
} from "../../components/Event/utils/dateTimeHelpers";
|
} from "../../components/Event/utils/dateTimeHelpers";
|
||||||
|
import { createAttendee } from "../User/models/attendee.mapper";
|
||||||
type RawEntry = [string, Record<string, string>, string, any];
|
type RawEntry = [string, Record<string, string>, string, any];
|
||||||
|
|
||||||
function resolveTimezoneId(tzid?: string): string | undefined {
|
function resolveTimezoneId(tzid?: string): string | undefined {
|
||||||
@@ -119,14 +120,16 @@ export function parseCalendarEvent(
|
|||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case "attendee":
|
case "attendee":
|
||||||
(event.attendee as userAttendee[]).push({
|
(event.attendee as userAttendee[]).push(
|
||||||
cn: params?.cn ?? "",
|
createAttendee({
|
||||||
cal_address: value.replace(/^mailto:/i, ""),
|
cn: params?.cn,
|
||||||
partstat: params?.partstat ?? "",
|
cal_address: value.replace(/^mailto:/i, ""),
|
||||||
rsvp: params?.rsvp ?? "",
|
partstat: params?.partstat as userAttendee["partstat"],
|
||||||
role: params?.role ?? "",
|
rsvp: params?.rsvp as userAttendee["rsvp"],
|
||||||
cutype: params?.cutype ?? "",
|
role: params?.role as userAttendee["role"],
|
||||||
});
|
cutype: params?.cutype as userAttendee["cutype"],
|
||||||
|
})
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case "dtstamp":
|
case "dtstamp":
|
||||||
event.stamp = value;
|
event.stamp = value;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { TIMEZONES } from "../../utils/timezone-data";
|
import { TIMEZONES } from "../../utils/timezone-data";
|
||||||
import { resolveTimezone } from "../../components/Calendar/TimezoneSelector";
|
import { resolveTimezone } from "../../components/Calendar/TimezoneSelector";
|
||||||
import { CalendarEvent, RepetitionObject } from "./EventsTypes";
|
import { CalendarEvent, RepetitionObject } from "./EventsTypes";
|
||||||
import { userAttendee } from "../User/userDataTypes";
|
import { userAttendee } from "../User/models/attendee";
|
||||||
import { formatDateTimeInTimezone } from "../../components/Event/utils/dateTimeFormatters";
|
import { formatDateTimeInTimezone } from "../../components/Event/utils/dateTimeFormatters";
|
||||||
import { addVideoConferenceToDescription } from "../../utils/videoConferenceUtils";
|
import { addVideoConferenceToDescription } from "../../utils/videoConferenceUtils";
|
||||||
import { browserDefaultTimeZone } from "../../utils/timezone";
|
import { browserDefaultTimeZone } from "../../utils/timezone";
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { userAttendee } from "./attendee";
|
||||||
|
|
||||||
|
export function createAttendee(options?: {
|
||||||
|
cal_address?: string;
|
||||||
|
cn?: string;
|
||||||
|
role?: userAttendee["role"];
|
||||||
|
partstat?: userAttendee["partstat"];
|
||||||
|
rsvp?: userAttendee["rsvp"];
|
||||||
|
cutype?: userAttendee["cutype"];
|
||||||
|
}): userAttendee {
|
||||||
|
return {
|
||||||
|
cal_address: options?.cal_address ?? "",
|
||||||
|
cn: options?.cn ?? "",
|
||||||
|
cutype: options?.cutype ?? "INDIVIDUAL",
|
||||||
|
role: options?.role ?? "REQ-PARTICIPANT",
|
||||||
|
partstat: options?.partstat ?? "NEEDS-ACTION",
|
||||||
|
rsvp: options?.rsvp ?? "FALSE",
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
export type AttendeeRole = "CHAIR" | "REQ-PARTICIPANT" | "OPT-PARTICIPANT";
|
||||||
|
export type CuType = "INDIVIDUAL" | "GROUP";
|
||||||
|
export type PartStat = "ACCEPTED" | "DECLINED" | "TENTATIVE" | "NEEDS-ACTION";
|
||||||
|
|
||||||
|
export interface userAttendee {
|
||||||
|
cal_address: string;
|
||||||
|
partstat: PartStat;
|
||||||
|
role: AttendeeRole;
|
||||||
|
cutype: CuType;
|
||||||
|
rsvp: "TRUE" | "FALSE";
|
||||||
|
cn: string;
|
||||||
|
}
|
||||||
@@ -33,12 +33,3 @@ export interface userOrganiser {
|
|||||||
cn: string;
|
cn: string;
|
||||||
cal_address: string;
|
cal_address: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface userAttendee {
|
|
||||||
cn?: string;
|
|
||||||
cal_address: string;
|
|
||||||
partstat: string;
|
|
||||||
rsvp: string;
|
|
||||||
role: string;
|
|
||||||
cutype: string;
|
|
||||||
}
|
|
||||||
|
|||||||
+3
-3
@@ -245,9 +245,9 @@
|
|||||||
"tooltip": "Others see you as available during the time range of this event."
|
"tooltip": "Others see you as available during the time range of this event."
|
||||||
},
|
},
|
||||||
"attendingQuestion": "Attending?",
|
"attendingQuestion": "Attending?",
|
||||||
"accept": "Accept",
|
"ACCEPTED": "Accept",
|
||||||
"maybe": "Maybe",
|
"TENTATIVE": "Maybe",
|
||||||
"decline": "Decline",
|
"DECLINED": "Decline",
|
||||||
"showMore": "Show more",
|
"showMore": "Show more",
|
||||||
"showLess": "Show less",
|
"showLess": "Show less",
|
||||||
"joinVideo": "Join the video conference",
|
"joinVideo": "Join the video conference",
|
||||||
|
|||||||
+3
-3
@@ -247,9 +247,9 @@
|
|||||||
"tooltip": "Les autres vous voient comme disponible pendant la plage horaire de cet événement."
|
"tooltip": "Les autres vous voient comme disponible pendant la plage horaire de cet événement."
|
||||||
},
|
},
|
||||||
"attendingQuestion": "Vous participez ?",
|
"attendingQuestion": "Vous participez ?",
|
||||||
"accept": "Accepter",
|
"ACCEPTED": "Accepter",
|
||||||
"maybe": "Peut-être",
|
"TENTATIVE": "Peut-être",
|
||||||
"decline": "Décliner",
|
"DECLINED": "Décliner",
|
||||||
"showMore": "Afficher plus",
|
"showMore": "Afficher plus",
|
||||||
"showLess": "Afficher moins",
|
"showLess": "Afficher moins",
|
||||||
"joinVideo": "Rejoindre la visioconférence",
|
"joinVideo": "Rejoindre la visioconférence",
|
||||||
|
|||||||
+3
-3
@@ -247,9 +247,9 @@
|
|||||||
"tooltip": "Другие видят вас свободным."
|
"tooltip": "Другие видят вас свободным."
|
||||||
},
|
},
|
||||||
"attendingQuestion": "Присоединитесь?",
|
"attendingQuestion": "Присоединитесь?",
|
||||||
"accept": "Да",
|
"ACCEPTED": "Да",
|
||||||
"maybe": "Возможно",
|
"TENTATIVE": "Возможно",
|
||||||
"decline": "Нет",
|
"DECLINED": "Нет",
|
||||||
"showMore": "Показать больше",
|
"showMore": "Показать больше",
|
||||||
"showLess": "Показать меньше",
|
"showLess": "Показать меньше",
|
||||||
"joinVideo": "Присоединиться к видеоконференции",
|
"joinVideo": "Присоединиться к видеоконференции",
|
||||||
|
|||||||
+3
-3
@@ -245,9 +245,9 @@
|
|||||||
"tooltip": "Người khác sẽ thấy bạn rảnh trong khoảng thời gian này."
|
"tooltip": "Người khác sẽ thấy bạn rảnh trong khoảng thời gian này."
|
||||||
},
|
},
|
||||||
"attendingQuestion": "Tham gia?",
|
"attendingQuestion": "Tham gia?",
|
||||||
"accept": "Chấp nhận",
|
"ACCEPTED": "Chấp nhận",
|
||||||
"maybe": "Có thể",
|
"TENTATIVE": "Có thể",
|
||||||
"decline": "Từ chối",
|
"DECLINED": "Từ chối",
|
||||||
"showMore": "Xem thêm",
|
"showMore": "Xem thêm",
|
||||||
"showLess": "Thu gọn",
|
"showLess": "Thu gọn",
|
||||||
"joinVideo": "Tham gia cuộc họp video",
|
"joinVideo": "Tham gia cuộc họp video",
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
export function buildFamilyName(
|
||||||
|
firstName: string | undefined,
|
||||||
|
lastName: string | undefined,
|
||||||
|
email: string
|
||||||
|
): string {
|
||||||
|
const trimmedFirstName = firstName?.trim() || "";
|
||||||
|
const trimmedLastName = lastName?.trim() || "";
|
||||||
|
const fullName = [trimmedFirstName, trimmedLastName]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" ");
|
||||||
|
return fullName || email;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { RepetitionObject } from "../features/Events/EventsTypes";
|
import { RepetitionObject } from "../features/Events/EventsTypes";
|
||||||
import { userAttendee } from "../features/User/userDataTypes";
|
import { userAttendee } from "../features/User/models/attendee";
|
||||||
|
|
||||||
export interface EventFormTempData {
|
export interface EventFormTempData {
|
||||||
// Form fields
|
// Form fields
|
||||||
|
|||||||
Reference in New Issue
Block a user