@@ -396,6 +396,22 @@ describe("CalendarSlice", () => {
|
|||||||
|
|
||||||
it("createCalendarAsync.fulfilled adds a new calendar", () => {
|
it("createCalendarAsync.fulfilled adds a new calendar", () => {
|
||||||
const payload = {
|
const payload = {
|
||||||
|
userData: {
|
||||||
|
openpaasId: "u1",
|
||||||
|
family_name: "Owner",
|
||||||
|
email: "o@example.com",
|
||||||
|
sid: "",
|
||||||
|
sub: "",
|
||||||
|
given_name: "Test",
|
||||||
|
name: "Test Owner",
|
||||||
|
},
|
||||||
|
calId: "cal1",
|
||||||
|
color: { "apple:color": "#f00" },
|
||||||
|
name: "Test",
|
||||||
|
desc: "Desc",
|
||||||
|
};
|
||||||
|
|
||||||
|
const payloadResponse = {
|
||||||
userId: "u1",
|
userId: "u1",
|
||||||
calId: "cal1",
|
calId: "cal1",
|
||||||
color: { "apple:color": "#f00" },
|
color: { "apple:color": "#f00" },
|
||||||
@@ -406,7 +422,7 @@ describe("CalendarSlice", () => {
|
|||||||
};
|
};
|
||||||
const state = reducer(
|
const state = reducer(
|
||||||
initialState,
|
initialState,
|
||||||
createCalendarAsync.fulfilled(payload, "req4", payload)
|
createCalendarAsync.fulfilled(payloadResponse, "req4", payload)
|
||||||
);
|
);
|
||||||
expect(state.list["u1/cal1"].name).toBe("Test");
|
expect(state.list["u1/cal1"].name).toBe("Test");
|
||||||
expect(state.list["u1/cal1"].color?.["apple:color"]).toBe("#f00");
|
expect(state.list["u1/cal1"].color?.["apple:color"]).toBe("#f00");
|
||||||
@@ -441,28 +457,6 @@ describe("CalendarSlice", () => {
|
|||||||
expect(state.list["c1"].name).toBe("Shared");
|
expect(state.list["c1"].name).toBe("Shared");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("deleteEventAsync.fulfilled removes event", () => {
|
|
||||||
const calId = "c1";
|
|
||||||
const prev = {
|
|
||||||
...initialState,
|
|
||||||
list: {
|
|
||||||
[calId]: {
|
|
||||||
id: calId,
|
|
||||||
events: { e1: { uid: "e1" } },
|
|
||||||
} as unknown as Calendar,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const state = reducer(
|
|
||||||
prev,
|
|
||||||
deleteEventAsync.fulfilled({ calId, eventId: "e1" }, "req6", {
|
|
||||||
calId,
|
|
||||||
eventId: "e1",
|
|
||||||
eventURL: "e1.ics",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
expect(state.list[calId].events).toEqual({});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("getTempCalendarsListAsync.fulfilled updates templist", () => {
|
it("getTempCalendarsListAsync.fulfilled updates templist", () => {
|
||||||
const payload = {
|
const payload = {
|
||||||
t1: {
|
t1: {
|
||||||
@@ -490,19 +484,6 @@ describe("CalendarSlice", () => {
|
|||||||
expect(state.templist.t1.name).toBe("Temp");
|
expect(state.templist.t1.name).toBe("Temp");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("putEventAsync.fulfilled updates calendar events", () => {
|
|
||||||
const cal = { id: "c1", events: {} } as any;
|
|
||||||
const payload = { calId: "c1", events: [{ uid: "e1" }] as any[] };
|
|
||||||
const state = reducer(
|
|
||||||
initialState,
|
|
||||||
putEventAsync.fulfilled(payload, "req8", {
|
|
||||||
cal,
|
|
||||||
newEvent: { uid: "e1" } as CalendarEvent,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
expect(state.list.c1.events.e1.uid).toBe("e1");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("getEventAsync.fulfilled adds single event", () => {
|
it("getEventAsync.fulfilled adds single event", () => {
|
||||||
const payload = { calId: "c1", event: { uid: "e1" } as any };
|
const payload = { calId: "c1", event: { uid: "e1" } as any };
|
||||||
const state = reducer(
|
const state = reducer(
|
||||||
@@ -512,20 +493,6 @@ describe("CalendarSlice", () => {
|
|||||||
expect(state.list.c1.events.e1.uid).toBe("e1");
|
expect(state.list.c1.events.e1.uid).toBe("e1");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("moveEventAsync.fulfilled updates events after move", () => {
|
|
||||||
const cal = { id: "c1", events: {} } as any;
|
|
||||||
const payload = { calId: "c1", events: [{ uid: "e1" }] as any[] };
|
|
||||||
const state = reducer(
|
|
||||||
initialState,
|
|
||||||
moveEventAsync.fulfilled(payload, "req10", {
|
|
||||||
cal,
|
|
||||||
newEvent: { uid: "e1" } as CalendarEvent,
|
|
||||||
newURL: "e1.ics",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
expect(state.list.c1.events.e1.uid).toBe("e1");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("getCalendarDetailAsync.fulfilled adds calendar events", () => {
|
it("getCalendarDetailAsync.fulfilled adds calendar events", () => {
|
||||||
const payload = { calId: "c1", events: [{ uid: "e1" }] as any[] };
|
const payload = { calId: "c1", events: [{ uid: "e1" }] as any[] };
|
||||||
const state = reducer(
|
const state = reducer(
|
||||||
|
|||||||
@@ -1832,7 +1832,6 @@ describe("Event Full Display", () => {
|
|||||||
(promise as any).unwrap = () => promise;
|
(promise as any).unwrap = () => promise;
|
||||||
return () => promise as any;
|
return () => promise as any;
|
||||||
});
|
});
|
||||||
const spyRemove = jest.spyOn(calendarSlice, "removeEvent");
|
|
||||||
|
|
||||||
const testDate = new Date("2025-01-15T10:00:00.000Z");
|
const testDate = new Date("2025-01-15T10:00:00.000Z");
|
||||||
const testEndDate = new Date("2025-01-15T11:00:00.000Z");
|
const testEndDate = new Date("2025-01-15T11:00:00.000Z");
|
||||||
@@ -1902,13 +1901,6 @@ describe("Event Full Display", () => {
|
|||||||
},
|
},
|
||||||
{ timeout: 3000 }
|
{ timeout: 3000 }
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor(
|
|
||||||
() => {
|
|
||||||
expect(spyRemove).toHaveBeenCalled();
|
|
||||||
},
|
|
||||||
{ timeout: 3000 }
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("edit modal displays event time in original event timezone", () => {
|
it("edit modal displays event time in original event timezone", () => {
|
||||||
|
|||||||
@@ -784,61 +784,6 @@ describe("Event Drag and Drop - Recurring Events", () => {
|
|||||||
expect(mockSetOpenEditModePopup).toHaveBeenCalledWith("edit");
|
expect(mockSetOpenEditModePopup).toHaveBeenCalledWith("edit");
|
||||||
expect(mockSetAfterChoiceFunc).toHaveBeenCalled();
|
expect(mockSetAfterChoiceFunc).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("directly updates non-recurring event on drag", () => {
|
|
||||||
const mockDispatch = jest.fn();
|
|
||||||
const nonRecurringState = {
|
|
||||||
"667037022b752d0026472254/cal1": {
|
|
||||||
...basePreloadedState.calendars.list["667037022b752d0026472254/cal1"],
|
|
||||||
events: {
|
|
||||||
"single-event": {
|
|
||||||
uid: "single-event",
|
|
||||||
title: "Single Event",
|
|
||||||
calId: "667037022b752d0026472254/cal1",
|
|
||||||
start: day.toISOString(),
|
|
||||||
end: new Date("2025-03-15T11:00:00Z").toISOString(),
|
|
||||||
timezone: "UTC",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const eventHandlers = createEventHandlers({
|
|
||||||
setSelectedRange: jest.fn(),
|
|
||||||
setOpenEventModal: jest.fn(),
|
|
||||||
setTempEvent: jest.fn(),
|
|
||||||
setOpenEventDisplay: jest.fn(),
|
|
||||||
dispatch: mockDispatch,
|
|
||||||
calendarRange: { start: new Date(), end: new Date() },
|
|
||||||
setEventDisplayedId: jest.fn(),
|
|
||||||
setEventDisplayedCalId: jest.fn(),
|
|
||||||
setEventDisplayedTemp: jest.fn(),
|
|
||||||
calendars: nonRecurringState,
|
|
||||||
setSelectedEvent: jest.fn(),
|
|
||||||
setAfterChoiceFunc: jest.fn(),
|
|
||||||
setOpenEditModePopup: jest.fn(),
|
|
||||||
} as unknown as EventHandlersProps);
|
|
||||||
|
|
||||||
const mockArg = {
|
|
||||||
event: {
|
|
||||||
_def: {
|
|
||||||
extendedProps: {
|
|
||||||
uid: "single-event",
|
|
||||||
calId: "667037022b752d0026472254/cal1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
delta: { years: 0, months: 0, days: 1, milliseconds: 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
eventHandlers.handleEventDrop(mockArg);
|
|
||||||
|
|
||||||
expect(mockDispatch).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
type: expect.stringContaining("updateEventLocal"),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Event Resize - Recurring Events", () => {
|
describe("Event Resize - Recurring Events", () => {
|
||||||
|
|||||||
@@ -1,859 +0,0 @@
|
|||||||
import * as appHooks from "@/app/hooks";
|
|
||||||
import CalendarLayout from "@/components/Calendar/CalendarLayout";
|
|
||||||
import * as calendarUtils from "@/components/Calendar/utils/calendarUtils";
|
|
||||||
import * as eventUtils from "@/components/Event/utils/eventUtils";
|
|
||||||
import * as CalendarSlice from "@/features/Calendars/CalendarSlice";
|
|
||||||
import { Calendar } from "@/features/Calendars/CalendarTypes";
|
|
||||||
import * as eventThunks from "@/features/Calendars/services";
|
|
||||||
import EventPreviewModal from "@/features/Events/EventDisplayPreview";
|
|
||||||
import EventPopover from "@/features/Events/EventModal";
|
|
||||||
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
|
||||||
import EventUpdateModal from "@/features/Events/EventUpdateModal";
|
|
||||||
import * as userApi from "@/features/User/userAPI";
|
|
||||||
import { DateSelectArg } from "@fullcalendar/core";
|
|
||||||
import { jest } from "@jest/globals";
|
|
||||||
import { ThunkDispatch } from "@reduxjs/toolkit";
|
|
||||||
import "@testing-library/jest-dom";
|
|
||||||
import { act, fireEvent, screen, waitFor } from "@testing-library/react";
|
|
||||||
import { SpiedFunction } from "jest-mock";
|
|
||||||
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
|
||||||
|
|
||||||
describe("Update tempcalendars called with correct params", () => {
|
|
||||||
const today = new Date();
|
|
||||||
const start = new Date(today);
|
|
||||||
start.setHours(10, 0, 0, 0);
|
|
||||||
const end = new Date(today);
|
|
||||||
end.setHours(11, 0, 0, 0);
|
|
||||||
|
|
||||||
let refreshCalendarsSpy: SpiedFunction<
|
|
||||||
(
|
|
||||||
dispatch: ThunkDispatch<any, any, any>,
|
|
||||||
calendars: Calendar[],
|
|
||||||
calendarRange: { start: Date; end: Date },
|
|
||||||
calType?: "temp"
|
|
||||||
) => Promise<void>
|
|
||||||
>;
|
|
||||||
let refreshSingularCalendarSpy: SpiedFunction<
|
|
||||||
(
|
|
||||||
dispatch: ThunkDispatch<any, any, any>,
|
|
||||||
calendar: Calendar,
|
|
||||||
calendarRange: { start: Date; end: Date },
|
|
||||||
calType?: "temp"
|
|
||||||
) => Promise<void>
|
|
||||||
>;
|
|
||||||
let updateTempCalendarSpy: SpiedFunction<
|
|
||||||
(
|
|
||||||
tempcalendars: Record<string, Calendar>,
|
|
||||||
event: CalendarEvent,
|
|
||||||
dispatch: ThunkDispatch<any, any, any>,
|
|
||||||
calendarRange: { start: Date; end: Date }
|
|
||||||
) => Promise<void>
|
|
||||||
>;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
jest.clearAllMocks();
|
|
||||||
const dispatch = jest.fn((thunk) => {
|
|
||||||
if (typeof thunk === "function") {
|
|
||||||
return thunk(dispatch, () => ({}), undefined);
|
|
||||||
}
|
|
||||||
return thunk;
|
|
||||||
}) as ThunkDispatch<any, any, any>;
|
|
||||||
jest.spyOn(appHooks, "useAppDispatch").mockReturnValue(dispatch);
|
|
||||||
refreshCalendarsSpy = jest
|
|
||||||
.spyOn(eventUtils, "refreshCalendars")
|
|
||||||
.mockResolvedValue();
|
|
||||||
updateTempCalendarSpy = jest.spyOn(calendarUtils, "updateTempCalendar");
|
|
||||||
refreshSingularCalendarSpy = jest.spyOn(
|
|
||||||
eventUtils,
|
|
||||||
"refreshSingularCalendar"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
refreshCalendarsSpy.mockRestore();
|
|
||||||
updateTempCalendarSpy.mockRestore();
|
|
||||||
refreshSingularCalendarSpy.mockRestore();
|
|
||||||
});
|
|
||||||
|
|
||||||
const createPreloadedState = (withTempList = true) => ({
|
|
||||||
user: {
|
|
||||||
userData: {
|
|
||||||
sub: "test",
|
|
||||||
email: "owner@test.com",
|
|
||||||
sid: "mockSid",
|
|
||||||
openpaasId: "667037022b752d0026472254",
|
|
||||||
},
|
|
||||||
organiserData: {
|
|
||||||
cn: "Owner",
|
|
||||||
cal_address: "owner@test.com",
|
|
||||||
},
|
|
||||||
tokens: {
|
|
||||||
accessToken: "token",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
calendars: {
|
|
||||||
list: {
|
|
||||||
"667037022b752d0026472254/cal1": {
|
|
||||||
name: "Calendar 1",
|
|
||||||
id: "667037022b752d0026472254/cal1",
|
|
||||||
color: "#FF0000",
|
|
||||||
ownerEmails: ["owner@test.com"],
|
|
||||||
events: {
|
|
||||||
event1: {
|
|
||||||
id: "event1",
|
|
||||||
calId: "667037022b752d0026472254/cal1",
|
|
||||||
uid: "event1",
|
|
||||||
title: "Test Event",
|
|
||||||
start: start.toISOString(),
|
|
||||||
end: end.toISOString(),
|
|
||||||
attendee: [{ cal_address: "attendee@test.com" }],
|
|
||||||
organizer: {
|
|
||||||
cn: "Owner",
|
|
||||||
cal_address: "owner@test.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
templist: withTempList
|
|
||||||
? {
|
|
||||||
temp1: {
|
|
||||||
id: "temp1",
|
|
||||||
name: "Temp Calendar",
|
|
||||||
color: "#00FF00",
|
|
||||||
events: {},
|
|
||||||
ownerEmails: ["attendee@test.com"],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
pending: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call updateTempCalendar with correct params after event deletion", async () => {
|
|
||||||
const preloadedState = createPreloadedState(true);
|
|
||||||
|
|
||||||
const mockOnClose = jest.fn();
|
|
||||||
renderWithProviders(
|
|
||||||
<EventPreviewModal
|
|
||||||
eventId="event1"
|
|
||||||
calId="667037022b752d0026472254/cal1"
|
|
||||||
open={true}
|
|
||||||
onClose={mockOnClose}
|
|
||||||
/>,
|
|
||||||
preloadedState
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
fireEvent.click(screen.getByTestId("MoreVertIcon"));
|
|
||||||
expect(screen.getByText("eventPreview.deleteEvent")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleteMenuItem = screen.getByText("eventPreview.deleteEvent");
|
|
||||||
fireEvent.click(deleteMenuItem);
|
|
||||||
|
|
||||||
await waitFor(() =>
|
|
||||||
expect(updateTempCalendarSpy).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
temp1: expect.objectContaining({ id: "temp1" }),
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
attendee: [
|
|
||||||
expect.objectContaining({ cal_address: "attendee@test.com" }),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
expect.any(Function),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() =>
|
|
||||||
expect(refreshSingularCalendarSpy).toHaveBeenCalledWith(
|
|
||||||
expect.any(Function),
|
|
||||||
expect.objectContaining({ id: "temp1" }),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
}),
|
|
||||||
"temp"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should NOT call updateTempCalendar when templist is undefined", async () => {
|
|
||||||
const preloadedState = createPreloadedState(false);
|
|
||||||
|
|
||||||
const mockOnClose = jest.fn();
|
|
||||||
renderWithProviders(
|
|
||||||
<EventPreviewModal
|
|
||||||
eventId="event1"
|
|
||||||
calId="667037022b752d0026472254/cal1"
|
|
||||||
open={true}
|
|
||||||
onClose={mockOnClose}
|
|
||||||
/>,
|
|
||||||
preloadedState
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
fireEvent.click(screen.getByTestId("MoreVertIcon"));
|
|
||||||
expect(screen.getByText("eventPreview.deleteEvent")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleteMenuItem = screen.getByText("eventPreview.deleteEvent");
|
|
||||||
fireEvent.click(deleteMenuItem);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(updateTempCalendarSpy).not.toHaveBeenCalled();
|
|
||||||
expect(refreshSingularCalendarSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call updateTempCalendar with correct params after event creation", async () => {
|
|
||||||
const preloadedState = createPreloadedState(true);
|
|
||||||
const defaultSelectedRange = {
|
|
||||||
startStr: "2025-07-18T09:00Z",
|
|
||||||
endStr: "2025-07-18T10:00Z",
|
|
||||||
start: new Date("2025-07-18T09:00Z"),
|
|
||||||
end: new Date("2025-07-18T10:00Z"),
|
|
||||||
allDay: false,
|
|
||||||
resource: undefined,
|
|
||||||
} as unknown as DateSelectArg;
|
|
||||||
jest.spyOn(userApi, "searchUsers");
|
|
||||||
|
|
||||||
// Mock putEventAsync to return success with unwrap
|
|
||||||
const putEventAsyncMock = jest
|
|
||||||
.spyOn(eventThunks, "putEventAsync")
|
|
||||||
.mockImplementation((payload) => {
|
|
||||||
const action = {
|
|
||||||
type: "calendars/putEvent/fulfilled",
|
|
||||||
payload: {
|
|
||||||
calId: payload.cal.id,
|
|
||||||
events: [],
|
|
||||||
calType: payload.calType,
|
|
||||||
},
|
|
||||||
unwrap: () =>
|
|
||||||
Promise.resolve({
|
|
||||||
calId: payload.cal.id,
|
|
||||||
events: [],
|
|
||||||
calType: payload.calType,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
const promise = Promise.resolve(action) as any;
|
|
||||||
return () => promise;
|
|
||||||
});
|
|
||||||
|
|
||||||
renderWithProviders(
|
|
||||||
<EventPopover
|
|
||||||
anchorEl={null}
|
|
||||||
open={true}
|
|
||||||
onClose={jest.fn()}
|
|
||||||
selectedRange={defaultSelectedRange}
|
|
||||||
setSelectedRange={jest.fn()}
|
|
||||||
calendarRef={{ current: null }}
|
|
||||||
/>,
|
|
||||||
preloadedState
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByRole("dialog")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
const titleInput = screen.getByLabelText(/title/i);
|
|
||||||
fireEvent.change(titleInput, { target: { value: "New Event" } });
|
|
||||||
|
|
||||||
const attendeeInput = screen.getByLabelText(/peopleSearch.label/i);
|
|
||||||
fireEvent.change(attendeeInput, { target: { value: "attendee@test.com" } });
|
|
||||||
fireEvent.keyDown(attendeeInput, { key: "Enter", code: "Enter" });
|
|
||||||
|
|
||||||
const saveButton = screen.getByText(/save/i);
|
|
||||||
await act(async () => {
|
|
||||||
fireEvent.click(saveButton);
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitFor(
|
|
||||||
() =>
|
|
||||||
expect(updateTempCalendarSpy).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
temp1: expect.objectContaining({ id: "temp1" }),
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
title: "New Event",
|
|
||||||
}),
|
|
||||||
expect.any(Function),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
{ timeout: 3000 }
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() =>
|
|
||||||
expect(refreshSingularCalendarSpy).toHaveBeenCalledWith(
|
|
||||||
expect.any(Function),
|
|
||||||
expect.objectContaining({ id: "temp1" }),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
}),
|
|
||||||
"temp"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call updateTempCalendar with correct params after event update", async () => {
|
|
||||||
const preloadedState = createPreloadedState(true);
|
|
||||||
|
|
||||||
const mockOnClose = jest.fn();
|
|
||||||
renderWithProviders(
|
|
||||||
<EventUpdateModal
|
|
||||||
open={true}
|
|
||||||
onClose={mockOnClose}
|
|
||||||
eventId="event1"
|
|
||||||
calId="667037022b752d0026472254/cal1"
|
|
||||||
/>,
|
|
||||||
preloadedState
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByRole("dialog")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
const saveButton = screen.getByText(/save/i);
|
|
||||||
fireEvent.click(saveButton);
|
|
||||||
|
|
||||||
await waitFor(() =>
|
|
||||||
expect(updateTempCalendarSpy).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
temp1: expect.objectContaining({ id: "temp1" }),
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
attendee: [
|
|
||||||
expect.objectContaining({ cal_address: "attendee@test.com" }),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
expect.any(Function),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() =>
|
|
||||||
expect(refreshSingularCalendarSpy).toHaveBeenCalledWith(
|
|
||||||
expect.any(Function),
|
|
||||||
expect.objectContaining({ id: "temp1" }),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
}),
|
|
||||||
"temp"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call updateTempCalendar with correct params after recurring event instance update", async () => {
|
|
||||||
const preloadedStateWithRecurring = {
|
|
||||||
...createPreloadedState(true),
|
|
||||||
calendars: {
|
|
||||||
...createPreloadedState(true).calendars,
|
|
||||||
list: {
|
|
||||||
"667037022b752d0026472254/cal1": {
|
|
||||||
name: "Calendar 1",
|
|
||||||
id: "667037022b752d0026472254/cal1",
|
|
||||||
color: "#FF0000",
|
|
||||||
ownerEmails: ["owner@test.com"],
|
|
||||||
events: {
|
|
||||||
event1: {
|
|
||||||
id: "event1",
|
|
||||||
calId: "667037022b752d0026472254/cal1",
|
|
||||||
uid: "event1",
|
|
||||||
title: "Recurring Event",
|
|
||||||
start: start.toISOString(),
|
|
||||||
end: end.toISOString(),
|
|
||||||
attendee: [{ cal_address: "attendee@test.com" }],
|
|
||||||
rrule: {
|
|
||||||
freq: "WEEKLY",
|
|
||||||
},
|
|
||||||
organizer: {
|
|
||||||
cn: "Owner",
|
|
||||||
cal_address: "owner@test.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockOnClose = jest.fn();
|
|
||||||
renderWithProviders(
|
|
||||||
<EventUpdateModal
|
|
||||||
open={true}
|
|
||||||
onClose={mockOnClose}
|
|
||||||
eventId="event1"
|
|
||||||
calId="667037022b752d0026472254/cal1"
|
|
||||||
typeOfAction="solo"
|
|
||||||
/>,
|
|
||||||
preloadedStateWithRecurring
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByRole("dialog")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
const saveButton = screen.getByText(/save/i);
|
|
||||||
fireEvent.click(saveButton);
|
|
||||||
|
|
||||||
await waitFor(() =>
|
|
||||||
expect(updateTempCalendarSpy).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
temp1: expect.objectContaining({ id: "temp1" }),
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
attendee: [
|
|
||||||
expect.objectContaining({ cal_address: "attendee@test.com" }),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
expect.any(Function),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() =>
|
|
||||||
expect(refreshSingularCalendarSpy).toHaveBeenCalledWith(
|
|
||||||
expect.any(Function),
|
|
||||||
expect.objectContaining({ id: "temp1" }),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
}),
|
|
||||||
"temp"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call updateTempCalendar with correct params after recurring series update", async () => {
|
|
||||||
const preloadedStateWithRecurring = {
|
|
||||||
...createPreloadedState(true),
|
|
||||||
calendars: {
|
|
||||||
...createPreloadedState(true).calendars,
|
|
||||||
list: {
|
|
||||||
"667037022b752d0026472254/cal1": {
|
|
||||||
name: "Calendar 1",
|
|
||||||
id: "667037022b752d0026472254/cal1",
|
|
||||||
color: "#FF0000",
|
|
||||||
ownerEmails: ["owner@test.com"],
|
|
||||||
events: {
|
|
||||||
event1: {
|
|
||||||
id: "event1",
|
|
||||||
calId: "667037022b752d0026472254/cal1",
|
|
||||||
uid: "event1",
|
|
||||||
title: "Recurring Event",
|
|
||||||
start: start.toISOString(),
|
|
||||||
end: end.toISOString(),
|
|
||||||
attendee: [{ cal_address: "attendee@test.com" }],
|
|
||||||
rrule: {
|
|
||||||
freq: "DAILY",
|
|
||||||
},
|
|
||||||
organizer: {
|
|
||||||
cn: "Owner",
|
|
||||||
cal_address: "owner@test.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockOnClose = jest.fn();
|
|
||||||
renderWithProviders(
|
|
||||||
<EventUpdateModal
|
|
||||||
open={true}
|
|
||||||
onClose={mockOnClose}
|
|
||||||
eventId="event1"
|
|
||||||
calId="667037022b752d0026472254/cal1"
|
|
||||||
typeOfAction="all"
|
|
||||||
/>,
|
|
||||||
preloadedStateWithRecurring
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByRole("dialog")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
const saveButton = screen.getByText(/save/i);
|
|
||||||
fireEvent.click(saveButton);
|
|
||||||
|
|
||||||
await waitFor(() =>
|
|
||||||
expect(updateTempCalendarSpy).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
temp1: expect.objectContaining({ id: "temp1" }),
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
attendee: [
|
|
||||||
expect.objectContaining({ cal_address: "attendee@test.com" }),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
expect.any(Function),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() =>
|
|
||||||
expect(refreshSingularCalendarSpy).toHaveBeenCalledWith(
|
|
||||||
expect.any(Function),
|
|
||||||
expect.objectContaining({ id: "temp1" }),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
}),
|
|
||||||
"temp"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call refreshCalendars with 'temp' param in CalendarLayout handleRefresh", async () => {
|
|
||||||
const preloadedState = createPreloadedState(true);
|
|
||||||
|
|
||||||
renderWithProviders(<CalendarLayout />, preloadedState);
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByTestId("RefreshIcon"));
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
// Should call refreshCalendars for regular calendars
|
|
||||||
expect(refreshCalendarsSpy).toHaveBeenCalledWith(
|
|
||||||
expect.any(Function),
|
|
||||||
expect.any(Array),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Should also call refreshCalendars with 'temp' for templist
|
|
||||||
expect(refreshCalendarsSpy).toHaveBeenCalledWith(
|
|
||||||
expect.any(Function),
|
|
||||||
expect.arrayContaining([expect.objectContaining({ id: "temp1" })]),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
}),
|
|
||||||
"temp"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should only refresh temp calendars where attendees match ownerEmails", async () => {
|
|
||||||
const preloadedStateMultipleTempCals = {
|
|
||||||
...createPreloadedState(true),
|
|
||||||
calendars: {
|
|
||||||
...createPreloadedState(true).calendars,
|
|
||||||
templist: {
|
|
||||||
temp1: {
|
|
||||||
id: "temp1",
|
|
||||||
name: "Temp Calendar 1",
|
|
||||||
color: "#00FF00",
|
|
||||||
events: {},
|
|
||||||
ownerEmails: ["attendee@test.com"], // matches event attendee
|
|
||||||
},
|
|
||||||
temp2: {
|
|
||||||
id: "temp2",
|
|
||||||
name: "Temp Calendar 2",
|
|
||||||
color: "#0000FF",
|
|
||||||
events: {},
|
|
||||||
ownerEmails: ["other@test.com"], // does NOT match event attendee
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockOnClose = jest.fn();
|
|
||||||
renderWithProviders(
|
|
||||||
<EventPreviewModal
|
|
||||||
eventId="event1"
|
|
||||||
calId="667037022b752d0026472254/cal1"
|
|
||||||
open={true}
|
|
||||||
onClose={mockOnClose}
|
|
||||||
/>,
|
|
||||||
preloadedStateMultipleTempCals
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
fireEvent.click(screen.getByTestId("MoreVertIcon"));
|
|
||||||
expect(screen.getByText("eventPreview.deleteEvent")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleteMenuItem = screen.getByText("eventPreview.deleteEvent");
|
|
||||||
fireEvent.click(deleteMenuItem);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
// Should only refresh temp1, not temp2
|
|
||||||
const temp1Calls = refreshSingularCalendarSpy.mock.calls.filter(
|
|
||||||
(call) => call[1]?.id === "temp1"
|
|
||||||
);
|
|
||||||
const temp2Calls = refreshSingularCalendarSpy.mock.calls.filter(
|
|
||||||
(call) => call[1]?.id === "temp2"
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(temp1Calls.length).toBeGreaterThan(0);
|
|
||||||
expect(temp2Calls.length).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call emptyEventsCal with correct params before refreshing", async () => {
|
|
||||||
const emptyEventsCalSpy = jest.spyOn(CalendarSlice, "emptyEventsCal");
|
|
||||||
|
|
||||||
const preloadedState = createPreloadedState(true);
|
|
||||||
|
|
||||||
const mockOnClose = jest.fn();
|
|
||||||
renderWithProviders(
|
|
||||||
<EventPreviewModal
|
|
||||||
eventId="event1"
|
|
||||||
calId="667037022b752d0026472254/cal1"
|
|
||||||
open={true}
|
|
||||||
onClose={mockOnClose}
|
|
||||||
/>,
|
|
||||||
preloadedState
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
fireEvent.click(screen.getByTestId("MoreVertIcon"));
|
|
||||||
expect(screen.getByText("eventPreview.deleteEvent")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleteMenuItem = screen.getByText("eventPreview.deleteEvent");
|
|
||||||
fireEvent.click(deleteMenuItem);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
// emptyEventsCal should be called with calType: "temp" for singular calendar refresh
|
|
||||||
expect(emptyEventsCalSpy).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
calId: "temp1",
|
|
||||||
calType: "temp",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
emptyEventsCalSpy.mockRestore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle temp calendar with multiple ownerEmails where one matches", async () => {
|
|
||||||
const preloadedStateMultipleOwners = {
|
|
||||||
...createPreloadedState(true),
|
|
||||||
calendars: {
|
|
||||||
...createPreloadedState(true).calendars,
|
|
||||||
list: {
|
|
||||||
"667037022b752d0026472254/cal1": {
|
|
||||||
name: "Calendar 1",
|
|
||||||
id: "667037022b752d0026472254/cal1",
|
|
||||||
color: "#FF0000",
|
|
||||||
ownerEmails: ["owner@test.com"],
|
|
||||||
events: {
|
|
||||||
event1: {
|
|
||||||
id: "event1",
|
|
||||||
calId: "667037022b752d0026472254/cal1",
|
|
||||||
uid: "event1",
|
|
||||||
title: "Test Event",
|
|
||||||
start: start.toISOString(),
|
|
||||||
end: end.toISOString(),
|
|
||||||
attendee: [{ cal_address: "attendee@test.com" }],
|
|
||||||
organizer: {
|
|
||||||
cn: "Owner",
|
|
||||||
cal_address: "owner@test.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
templist: {
|
|
||||||
temp1: {
|
|
||||||
id: "temp1",
|
|
||||||
name: "Shared Calendar",
|
|
||||||
color: "#00FF00",
|
|
||||||
events: {},
|
|
||||||
ownerEmails: [
|
|
||||||
"someoneelse@test.com",
|
|
||||||
"attendee@test.com", // matches
|
|
||||||
"anotherperson@test.com",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockOnClose = jest.fn();
|
|
||||||
renderWithProviders(
|
|
||||||
<EventPreviewModal
|
|
||||||
eventId="event1"
|
|
||||||
calId="667037022b752d0026472254/cal1"
|
|
||||||
open={true}
|
|
||||||
onClose={mockOnClose}
|
|
||||||
/>,
|
|
||||||
preloadedStateMultipleOwners
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
fireEvent.click(screen.getByTestId("MoreVertIcon"));
|
|
||||||
expect(screen.getByText("eventPreview.deleteEvent")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleteMenuItem = screen.getByText("eventPreview.deleteEvent");
|
|
||||||
fireEvent.click(deleteMenuItem);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
// Should still refresh temp1 because one ownerEmail matches
|
|
||||||
const temp1Calls = refreshSingularCalendarSpy.mock.calls.filter(
|
|
||||||
(call) => call[1]?.id === "temp1"
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(temp1Calls.length).toBeGreaterThan(0);
|
|
||||||
expect(temp1Calls[0][3]).toBe("temp");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it("should update multiple temp calendars when event has multiple attendees matching different ownerEmails", async () => {
|
|
||||||
const preloadedStateMultipleAttendees = {
|
|
||||||
...createPreloadedState(true),
|
|
||||||
calendars: {
|
|
||||||
...createPreloadedState(true).calendars,
|
|
||||||
list: {
|
|
||||||
"667037022b752d0026472254/cal1": {
|
|
||||||
name: "Calendar 1",
|
|
||||||
id: "667037022b752d0026472254/cal1",
|
|
||||||
color: "#FF0000",
|
|
||||||
ownerEmails: ["owner@test.com"],
|
|
||||||
events: {
|
|
||||||
event1: {
|
|
||||||
id: "event1",
|
|
||||||
calId: "667037022b752d0026472254/cal1",
|
|
||||||
uid: "event1",
|
|
||||||
title: "Test Event",
|
|
||||||
start: start.toISOString(),
|
|
||||||
end: end.toISOString(),
|
|
||||||
attendee: [
|
|
||||||
{ cal_address: "attendee1@test.com" },
|
|
||||||
{ cal_address: "attendee2@test.com" },
|
|
||||||
{ cal_address: "attendee3@test.com" },
|
|
||||||
],
|
|
||||||
organizer: {
|
|
||||||
cn: "Owner",
|
|
||||||
cal_address: "owner@test.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
templist: {
|
|
||||||
temp1: {
|
|
||||||
id: "temp1",
|
|
||||||
name: "Temp Calendar 1",
|
|
||||||
color: "#00FF00",
|
|
||||||
events: {},
|
|
||||||
ownerEmails: ["attendee1@test.com"],
|
|
||||||
},
|
|
||||||
temp2: {
|
|
||||||
id: "temp2",
|
|
||||||
name: "Temp Calendar 2",
|
|
||||||
color: "#0000FF",
|
|
||||||
events: {},
|
|
||||||
ownerEmails: ["attendee2@test.com"],
|
|
||||||
},
|
|
||||||
temp3: {
|
|
||||||
id: "temp3",
|
|
||||||
name: "Temp Calendar 3",
|
|
||||||
color: "#FF00FF",
|
|
||||||
events: {},
|
|
||||||
ownerEmails: ["attendee3@test.com"],
|
|
||||||
},
|
|
||||||
temp4: {
|
|
||||||
id: "temp4",
|
|
||||||
name: "Temp Calendar 4",
|
|
||||||
color: "#FFFF00",
|
|
||||||
events: {},
|
|
||||||
ownerEmails: ["nonmatching@test.com"], // should NOT be refreshed
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockOnClose = jest.fn();
|
|
||||||
renderWithProviders(
|
|
||||||
<EventPreviewModal
|
|
||||||
eventId="event1"
|
|
||||||
calId="667037022b752d0026472254/cal1"
|
|
||||||
open={true}
|
|
||||||
onClose={mockOnClose}
|
|
||||||
/>,
|
|
||||||
preloadedStateMultipleAttendees
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
fireEvent.click(screen.getByTestId("MoreVertIcon"));
|
|
||||||
expect(screen.getByText("eventPreview.deleteEvent")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleteMenuItem = screen.getByText("eventPreview.deleteEvent");
|
|
||||||
fireEvent.click(deleteMenuItem);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
// Should be called once with all templist
|
|
||||||
expect(updateTempCalendarSpy).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
temp1: expect.objectContaining({ id: "temp1" }),
|
|
||||||
temp2: expect.objectContaining({ id: "temp2" }),
|
|
||||||
temp3: expect.objectContaining({ id: "temp3" }),
|
|
||||||
temp4: expect.objectContaining({ id: "temp4" }),
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
attendee: expect.arrayContaining([
|
|
||||||
expect.objectContaining({ cal_address: "attendee1@test.com" }),
|
|
||||||
expect.objectContaining({ cal_address: "attendee2@test.com" }),
|
|
||||||
expect.objectContaining({ cal_address: "attendee3@test.com" }),
|
|
||||||
]),
|
|
||||||
}),
|
|
||||||
expect.any(Function),
|
|
||||||
expect.objectContaining({
|
|
||||||
start: expect.any(Date),
|
|
||||||
end: expect.any(Date),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
// Should refresh temp1, temp2, and temp3 (matching attendees)
|
|
||||||
const temp1Calls = refreshSingularCalendarSpy.mock.calls.filter(
|
|
||||||
(call) => call[1]?.id === "temp1"
|
|
||||||
);
|
|
||||||
const temp2Calls = refreshSingularCalendarSpy.mock.calls.filter(
|
|
||||||
(call) => call[1]?.id === "temp2"
|
|
||||||
);
|
|
||||||
const temp3Calls = refreshSingularCalendarSpy.mock.calls.filter(
|
|
||||||
(call) => call[1]?.id === "temp3"
|
|
||||||
);
|
|
||||||
const temp4Calls = refreshSingularCalendarSpy.mock.calls.filter(
|
|
||||||
(call) => call[1]?.id === "temp4"
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(temp1Calls.length).toBeGreaterThan(0);
|
|
||||||
expect(temp2Calls.length).toBeGreaterThan(0);
|
|
||||||
expect(temp3Calls.length).toBeGreaterThan(0);
|
|
||||||
expect(temp4Calls.length).toBe(0); // Should NOT be refreshed
|
|
||||||
|
|
||||||
// Verify all matching calendars were refreshed with 'temp' param
|
|
||||||
expect(temp1Calls[0][3]).toBe("temp");
|
|
||||||
expect(temp2Calls[0][3]).toBe("temp");
|
|
||||||
expect(temp3Calls[0][3]).toBe("temp");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -59,7 +59,7 @@ describe("findCalendarById", () => {
|
|||||||
expect(result).toBeUndefined();
|
expect(result).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should prioritize main list over temp list", () => {
|
it("should prioritize temp list over main list", () => {
|
||||||
const stateWithDuplicate = {
|
const stateWithDuplicate = {
|
||||||
calendars: {
|
calendars: {
|
||||||
list: {
|
list: {
|
||||||
@@ -73,8 +73,8 @@ describe("findCalendarById", () => {
|
|||||||
|
|
||||||
const result = findCalendarById(stateWithDuplicate, "dup1");
|
const result = findCalendarById(stateWithDuplicate, "dup1");
|
||||||
|
|
||||||
expect(result?.calendar).toEqual(mockCalendar1);
|
expect(result?.calendar).toEqual(mockTempCalendar);
|
||||||
expect(result?.type).toBeUndefined();
|
expect(result?.type).toBe("temp");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle undefined list or templist", () => {
|
it("should handle undefined list or templist", () => {
|
||||||
|
|||||||
@@ -406,10 +406,6 @@ export default function CalendarApp({
|
|||||||
});
|
});
|
||||||
}, [calendarsWithClearedCache, dispatch, rangeKey, rangeStart, rangeEnd]);
|
}, [calendarsWithClearedCache, dispatch, rangeKey, rangeStart, rangeEnd]);
|
||||||
|
|
||||||
const tempCalendarControllersRef = useRef<Map<string, AbortController>>(
|
|
||||||
new Map()
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentIds = new Set(tempCalendarIds);
|
const currentIds = new Set(tempCalendarIds);
|
||||||
Object.keys(tempFetchedRangesRef.current).forEach((id) => {
|
Object.keys(tempFetchedRangesRef.current).forEach((id) => {
|
||||||
@@ -558,9 +554,6 @@ export default function CalendarApp({
|
|||||||
setSelectedRange,
|
setSelectedRange,
|
||||||
setAnchorEl,
|
setAnchorEl,
|
||||||
calendarRef,
|
calendarRef,
|
||||||
selectedCalendars,
|
|
||||||
tempcalendars,
|
|
||||||
calendarRange,
|
|
||||||
dispatch,
|
dispatch,
|
||||||
setOpenEventDisplay,
|
setOpenEventDisplay,
|
||||||
setEventDisplayedId,
|
setEventDisplayedId,
|
||||||
|
|||||||
@@ -30,11 +30,10 @@ function CalendarPopover({
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const userId =
|
const userData = useAppSelector((state) => state.user.userData) ?? {};
|
||||||
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
|
|
||||||
const calendars = useAppSelector((state) => state.calendars.list);
|
const calendars = useAppSelector((state) => state.calendars.list);
|
||||||
const isOwn = calendar?.id
|
const isOwn = calendar?.id
|
||||||
? extractEventBaseUuid(calendar.id) === userId
|
? extractEventBaseUuid(calendar.id) === userData.openpaasId
|
||||||
: true;
|
: true;
|
||||||
|
|
||||||
// existing calendar params
|
// existing calendar params
|
||||||
@@ -104,14 +103,14 @@ function CalendarPopover({
|
|||||||
name: name.trim(),
|
name: name.trim(),
|
||||||
desc: desc.trim(),
|
desc: desc.trim(),
|
||||||
color: color,
|
color: color,
|
||||||
userId,
|
userData,
|
||||||
calId,
|
calId,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
dispatch(
|
dispatch(
|
||||||
patchACLCalendarAsync({
|
patchACLCalendarAsync({
|
||||||
calId: `${userId}/${calId}`,
|
calId: `${userData.openpaasId}/${calId}`,
|
||||||
calLink: `/calendars/${userId}/${calId}.json`,
|
calLink: `/calendars/${userData.openpaasId}/${calId}.json`,
|
||||||
request: visibility === "public" ? "{DAV:}read" : "",
|
request: visibility === "public" ? "{DAV:}read" : "",
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -142,7 +141,7 @@ function CalendarPopover({
|
|||||||
importedContent &&
|
importedContent &&
|
||||||
dispatch(
|
dispatch(
|
||||||
importEventFromFileAsync({
|
importEventFromFileAsync({
|
||||||
calLink: `/calendar/${userId}/${calId}.json`,
|
calLink: `/calendar/${userData.openpaasId}/${calId}.json`,
|
||||||
file: importedContent,
|
file: importedContent,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -227,7 +226,7 @@ function CalendarPopover({
|
|||||||
importTarget={importTarget}
|
importTarget={importTarget}
|
||||||
setImportTarget={setImportTarget}
|
setImportTarget={setImportTarget}
|
||||||
setImportedContent={setImportedContent}
|
setImportedContent={setImportedContent}
|
||||||
userId={userId}
|
userId={userData.openpaasId ?? ""}
|
||||||
newCalParams={{
|
newCalParams={{
|
||||||
name: newCalName,
|
name: newCalName,
|
||||||
setName: setNewCalName,
|
setName: setNewCalName,
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
|
import { AppDispatch } from "@/app/store";
|
||||||
import { User } from "@/components/Attendees/PeopleSearch";
|
import { User } from "@/components/Attendees/PeopleSearch";
|
||||||
import { formatLocalDateTime } from "@/components/Event/utils/dateTimeFormatters";
|
import { formatLocalDateTime } from "@/components/Event/utils/dateTimeFormatters";
|
||||||
import { refreshCalendars } from "@/components/Event/utils/eventUtils";
|
|
||||||
import { updateEventLocal } from "@/features/Calendars/CalendarSlice";
|
|
||||||
import { Calendar } from "@/features/Calendars/CalendarTypes";
|
import { Calendar } from "@/features/Calendars/CalendarTypes";
|
||||||
import {
|
import {
|
||||||
getCalendarDetailAsync,
|
|
||||||
getEventAsync,
|
getEventAsync,
|
||||||
putEventAsync,
|
putEventAsync,
|
||||||
updateEventInstanceAsync,
|
updateEventInstanceAsync,
|
||||||
@@ -14,21 +12,15 @@ import { getEvent } from "@/features/Events/EventApi";
|
|||||||
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
||||||
import { userAttendee } from "@/features/User/models/attendee";
|
import { userAttendee } from "@/features/User/models/attendee";
|
||||||
import { createAttendee } from "@/features/User/models/attendee.mapper";
|
import { createAttendee } from "@/features/User/models/attendee.mapper";
|
||||||
import {
|
import { getDeltaInMilliseconds } from "@/utils/dateUtils";
|
||||||
formatDateToYYYYMMDDTHHMMSS,
|
import { CalendarApi, DateSelectArg, EventDropArg } from "@fullcalendar/core";
|
||||||
getDeltaInMilliseconds,
|
import { EventResizeDoneArg } from "@fullcalendar/interaction";
|
||||||
} from "@/utils/dateUtils";
|
|
||||||
import { CalendarApi, DateSelectArg } from "@fullcalendar/core";
|
|
||||||
import { updateTempCalendar } from "../utils/calendarUtils";
|
|
||||||
|
|
||||||
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[];
|
dispatch: AppDispatch;
|
||||||
tempcalendars: Record<string, Calendar>;
|
|
||||||
calendarRange: { start: Date; end: Date };
|
|
||||||
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;
|
||||||
@@ -47,9 +39,6 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
|||||||
setSelectedRange,
|
setSelectedRange,
|
||||||
setAnchorEl,
|
setAnchorEl,
|
||||||
calendarRef,
|
calendarRef,
|
||||||
selectedCalendars,
|
|
||||||
tempcalendars,
|
|
||||||
calendarRange,
|
|
||||||
dispatch,
|
dispatch,
|
||||||
setOpenEventDisplay,
|
setOpenEventDisplay,
|
||||||
setEventDisplayedId,
|
setEventDisplayedId,
|
||||||
@@ -88,35 +77,10 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
|||||||
setAnchorEl(document.body);
|
setAnchorEl(document.body);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClosePopover = (refresh?: boolean) => {
|
const handleClosePopover = () => {
|
||||||
calendarRef.current?.unselect();
|
calendarRef.current?.unselect();
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
setSelectedRange(null);
|
setSelectedRange(null);
|
||||||
if (refresh) {
|
|
||||||
selectedCalendars.forEach((calId) =>
|
|
||||||
dispatch(
|
|
||||||
getCalendarDetailAsync({
|
|
||||||
calId,
|
|
||||||
match: {
|
|
||||||
start: formatDateToYYYYMMDDTHHMMSS(calendarRange.start),
|
|
||||||
end: formatDateToYYYYMMDDTHHMMSS(calendarRange.end),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
Object.keys(tempcalendars).forEach((calId) =>
|
|
||||||
dispatch(
|
|
||||||
getCalendarDetailAsync({
|
|
||||||
calId,
|
|
||||||
match: {
|
|
||||||
start: formatDateToYYYYMMDDTHHMMSS(calendarRange.start),
|
|
||||||
end: formatDateToYYYYMMDDTHHMMSS(calendarRange.end),
|
|
||||||
},
|
|
||||||
calType: "temp",
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseEventDisplay = () => {
|
const handleCloseEventDisplay = () => {
|
||||||
@@ -151,11 +115,11 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEventAllow = (dropInfo: any, draggedEvent: any) => {
|
const handleEventAllow = () => {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEventDrop = async (arg: any) => {
|
const handleEventDrop = async (arg: EventDropArg) => {
|
||||||
if (!arg.event || !arg.event._def || !arg.event._def.extendedProps) {
|
if (!arg.event || !arg.event._def || !arg.event._def.extendedProps) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -182,7 +146,6 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
|||||||
} as CalendarEvent,
|
} as CalendarEvent,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isRecurring) {
|
if (isRecurring) {
|
||||||
setSelectedEvent(event);
|
setSelectedEvent(event);
|
||||||
setOpenEditModePopup("edit");
|
setOpenEditModePopup("edit");
|
||||||
@@ -192,9 +155,6 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
|||||||
await dispatch(
|
await dispatch(
|
||||||
updateEventInstanceAsync({ cal: calendar, event: newEvent })
|
updateEventInstanceAsync({ cal: calendar, event: newEvent })
|
||||||
);
|
);
|
||||||
dispatch(
|
|
||||||
updateEventLocal({ calId: newEvent.calId, event: newEvent })
|
|
||||||
);
|
|
||||||
} else if (typeOfAction === "all") {
|
} else if (typeOfAction === "all") {
|
||||||
const master = await getEvent(newEvent, true);
|
const master = await getEvent(newEvent, true);
|
||||||
await dispatch(
|
await dispatch(
|
||||||
@@ -208,30 +168,17 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await refreshCalendars(
|
|
||||||
dispatch,
|
|
||||||
Object.values(calendars),
|
|
||||||
calendarRange
|
|
||||||
);
|
|
||||||
await updateTempCalendar(
|
|
||||||
tempcalendars,
|
|
||||||
event,
|
|
||||||
dispatch,
|
|
||||||
calendarRange
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
dispatch(updateEventLocal({ calId: newEvent.calId, event: newEvent }));
|
|
||||||
await dispatch(
|
await dispatch(
|
||||||
putEventAsync({ cal: calendars[newEvent.calId], newEvent })
|
putEventAsync({ cal: calendars[newEvent.calId], newEvent })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await updateTempCalendar(tempcalendars, event, dispatch, calendarRange);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEventResize = async (arg: any) => {
|
const handleEventResize = async (arg: EventResizeDoneArg) => {
|
||||||
if (!arg.event || !arg.event._def || !arg.event._def.extendedProps) {
|
if (!arg.event || !arg.event._def || !arg.event._def.extendedProps) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -261,7 +208,6 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
|||||||
} as CalendarEvent,
|
} as CalendarEvent,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isRecurring) {
|
if (isRecurring) {
|
||||||
setSelectedEvent(event);
|
setSelectedEvent(event);
|
||||||
setOpenEditModePopup("edit");
|
setOpenEditModePopup("edit");
|
||||||
@@ -271,9 +217,6 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
|||||||
await dispatch(
|
await dispatch(
|
||||||
updateEventInstanceAsync({ cal: calendar, event: newEvent })
|
updateEventInstanceAsync({ cal: calendar, event: newEvent })
|
||||||
);
|
);
|
||||||
dispatch(
|
|
||||||
updateEventLocal({ calId: newEvent.calId, event: newEvent })
|
|
||||||
);
|
|
||||||
} else if (typeOfAction === "all") {
|
} else if (typeOfAction === "all") {
|
||||||
const master = await getEvent(newEvent, true);
|
const master = await getEvent(newEvent, true);
|
||||||
|
|
||||||
@@ -288,17 +231,6 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await refreshCalendars(
|
|
||||||
dispatch,
|
|
||||||
Object.values(calendars),
|
|
||||||
calendarRange
|
|
||||||
);
|
|
||||||
await updateTempCalendar(
|
|
||||||
tempcalendars,
|
|
||||||
event,
|
|
||||||
dispatch,
|
|
||||||
calendarRange
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -307,7 +239,6 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
|||||||
putEventAsync({ cal: calendars[newEvent.calId], newEvent })
|
putEventAsync({ cal: calendars[newEvent.calId], newEvent })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await updateTempCalendar(tempcalendars, event, dispatch, calendarRange);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -19,9 +19,6 @@ export const useCalendarEventHandlers = (props: EventHandlersProps) => {
|
|||||||
props.calendarRef,
|
props.calendarRef,
|
||||||
props.setAnchorEl,
|
props.setAnchorEl,
|
||||||
props.setSelectedRange,
|
props.setSelectedRange,
|
||||||
props.selectedCalendars,
|
|
||||||
props.tempcalendars,
|
|
||||||
props.calendarRange,
|
|
||||||
props.dispatch,
|
props.dispatch,
|
||||||
]),
|
]),
|
||||||
handleCloseEventDisplay: useCallback(
|
handleCloseEventDisplay: useCallback(
|
||||||
@@ -43,14 +40,10 @@ export const useCalendarEventHandlers = (props: EventHandlersProps) => {
|
|||||||
props.setSelectedEvent,
|
props.setSelectedEvent,
|
||||||
props.setOpenEditModePopup,
|
props.setOpenEditModePopup,
|
||||||
props.setAfterChoiceFunc,
|
props.setAfterChoiceFunc,
|
||||||
props.tempcalendars,
|
|
||||||
props.calendarRange,
|
|
||||||
]),
|
]),
|
||||||
handleEventResize: useCallback(eventHandlers.handleEventResize, [
|
handleEventResize: useCallback(eventHandlers.handleEventResize, [
|
||||||
props.calendars,
|
props.calendars,
|
||||||
props.dispatch,
|
props.dispatch,
|
||||||
props.tempcalendars,
|
|
||||||
props.calendarRange,
|
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -271,35 +271,3 @@ export function getCalendarVisibility(acl: AclEntry[]): "private" | "public" {
|
|||||||
if (hasRead) return "public";
|
if (hasRead) return "public";
|
||||||
return "private";
|
return "private";
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateTempCalendar(
|
|
||||||
tempcalendars: Record<string, Calendar>,
|
|
||||||
event: CalendarEvent,
|
|
||||||
dispatch: ThunkDispatch<any, any, any>,
|
|
||||||
calendarRange: { start: Date; end: Date }
|
|
||||||
) {
|
|
||||||
if (tempcalendars && event?.attendee) {
|
|
||||||
const attendeesEmails = event.attendee
|
|
||||||
.map((a) => a.cal_address)
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
const tempCalendarValues = Object.values(tempcalendars);
|
|
||||||
|
|
||||||
for (const tempCalendar of tempCalendarValues) {
|
|
||||||
const ownerEmails = tempCalendar.ownerEmails || [];
|
|
||||||
|
|
||||||
// Check if any of the attendees are owners of this temp calendar
|
|
||||||
const isOwnerAttendee = ownerEmails.some((ownerEmail) =>
|
|
||||||
attendeesEmails.includes(ownerEmail)
|
|
||||||
);
|
|
||||||
if (isOwnerAttendee) {
|
|
||||||
await refreshSingularCalendar(
|
|
||||||
dispatch,
|
|
||||||
tempCalendar,
|
|
||||||
calendarRange,
|
|
||||||
"temp"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import { PartStat } from "@/features/User/models/attendee";
|
|||||||
import { createAttendee } from "@/features/User/models/attendee.mapper";
|
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 { buildFamilyName } from "@/utils/buildFamilyName";
|
||||||
import { getCalendarRange } from "@/utils/dateUtils";
|
|
||||||
import { refreshCalendars } from "../utils/eventUtils";
|
|
||||||
|
|
||||||
function updateEventAttendees(
|
function updateEventAttendees(
|
||||||
event: CalendarEvent,
|
event: CalendarEvent,
|
||||||
@@ -78,15 +76,11 @@ async function handleSoloRSVP(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleAllRSVP(
|
async function handleAllRSVP(
|
||||||
dispatch: AppDispatch,
|
|
||||||
event: CalendarEvent,
|
event: CalendarEvent,
|
||||||
userEmail: string,
|
userEmail: string,
|
||||||
rsvp: PartStat,
|
rsvp: PartStat
|
||||||
calendars: Calendar[]
|
|
||||||
) {
|
) {
|
||||||
const calendarRange = getCalendarRange(new Date(event.start));
|
|
||||||
await updateSeriesPartstat(event, userEmail, rsvp);
|
await updateSeriesPartstat(event, userEmail, rsvp);
|
||||||
await refreshCalendars(dispatch, calendars, calendarRange);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleDefaultRSVP(
|
async function handleDefaultRSVP(
|
||||||
@@ -103,8 +97,7 @@ export async function handleRSVP(
|
|||||||
user: userData | undefined,
|
user: userData | undefined,
|
||||||
event: CalendarEvent,
|
event: CalendarEvent,
|
||||||
rsvp: PartStat,
|
rsvp: PartStat,
|
||||||
typeOfAction?: string,
|
typeOfAction?: string
|
||||||
calendars?: Calendar[]
|
|
||||||
) {
|
) {
|
||||||
const newEvent = {
|
const newEvent = {
|
||||||
...event,
|
...event,
|
||||||
@@ -114,13 +107,10 @@ export async function handleRSVP(
|
|||||||
if (typeOfAction === "solo") {
|
if (typeOfAction === "solo") {
|
||||||
await handleSoloRSVP(dispatch, calendar, newEvent);
|
await handleSoloRSVP(dispatch, calendar, newEvent);
|
||||||
} else if (typeOfAction === "all") {
|
} else if (typeOfAction === "all") {
|
||||||
if (!calendars || calendars.length === 0) {
|
|
||||||
throw new Error("Cannot update all occurrences without calendar list");
|
|
||||||
}
|
|
||||||
if (!user?.email) {
|
if (!user?.email) {
|
||||||
throw new Error("Cannot update all occurrences without user email");
|
throw new Error("Cannot update all occurrences without user email");
|
||||||
}
|
}
|
||||||
await handleAllRSVP(dispatch, event, user.email, rsvp, calendars);
|
await handleAllRSVP(event, user.email, rsvp);
|
||||||
} else {
|
} else {
|
||||||
await handleDefaultRSVP(dispatch, calendar, newEvent);
|
await handleDefaultRSVP(dispatch, calendar, newEvent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,6 @@ import {
|
|||||||
updateSeriesAsync,
|
updateSeriesAsync,
|
||||||
} from "./services";
|
} from "./services";
|
||||||
|
|
||||||
// Define error type for rejected actions
|
|
||||||
export interface RejectedError {
|
|
||||||
message: string;
|
|
||||||
status?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CalendarSlice = createSlice({
|
const CalendarSlice = createSlice({
|
||||||
name: "calendars",
|
name: "calendars",
|
||||||
initialState: {
|
initialState: {
|
||||||
@@ -182,42 +176,10 @@ const CalendarSlice = createSlice({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.addCase(
|
.addCase(putEventAsync.fulfilled, (state) => {
|
||||||
putEventAsync.fulfilled,
|
state.pending = false;
|
||||||
(
|
state.error = null;
|
||||||
state,
|
})
|
||||||
action: PayloadAction<{
|
|
||||||
calId: string;
|
|
||||||
events: CalendarEvent[];
|
|
||||||
calType?: "temp";
|
|
||||||
}>
|
|
||||||
) => {
|
|
||||||
state.pending = false;
|
|
||||||
const type = action.payload.calType === "temp" ? "templist" : "list";
|
|
||||||
|
|
||||||
if (!state[type][action.payload.calId]) {
|
|
||||||
state[type][action.payload.calId] = {
|
|
||||||
id: action.payload.calId,
|
|
||||||
events: {},
|
|
||||||
} as Calendar;
|
|
||||||
}
|
|
||||||
action.payload.events.forEach((event) => {
|
|
||||||
state[type][action.payload.calId].events[event.uid] = event;
|
|
||||||
});
|
|
||||||
Object.keys(state[type][action.payload.calId].events).forEach(
|
|
||||||
(id) => {
|
|
||||||
state[type][action.payload.calId].events[id].color =
|
|
||||||
state[type][action.payload.calId].color;
|
|
||||||
state[type][action.payload.calId].events[id].calId =
|
|
||||||
action.payload.calId;
|
|
||||||
if (!state[type][action.payload.calId].events[id].timezone) {
|
|
||||||
state[type][action.payload.calId].events[id].timezone =
|
|
||||||
browserDefaultTimeZone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.addCase(
|
.addCase(
|
||||||
getEventAsync.fulfilled,
|
getEventAsync.fulfilled,
|
||||||
(
|
(
|
||||||
@@ -236,50 +198,12 @@ const CalendarSlice = createSlice({
|
|||||||
action.payload.event;
|
action.payload.event;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.addCase(
|
.addCase(moveEventAsync.fulfilled, (state) => {
|
||||||
moveEventAsync.fulfilled,
|
state.pending = false;
|
||||||
(
|
state.error = null;
|
||||||
state,
|
})
|
||||||
action: PayloadAction<{ calId: string; events: CalendarEvent[] }>
|
.addCase(deleteEventAsync.fulfilled, (state) => {
|
||||||
) => {
|
|
||||||
state.pending = false;
|
|
||||||
if (!state.list[action.payload.calId]) {
|
|
||||||
state.list[action.payload.calId] = {
|
|
||||||
id: action.payload.calId,
|
|
||||||
events: {},
|
|
||||||
} as Calendar;
|
|
||||||
}
|
|
||||||
action.payload.events.forEach((event) => {
|
|
||||||
state.list[action.payload.calId].events[event.uid] = event;
|
|
||||||
});
|
|
||||||
Object.keys(state.list[action.payload.calId].events).forEach((id) => {
|
|
||||||
state.list[action.payload.calId].events[id].color =
|
|
||||||
state.list[action.payload.calId].color;
|
|
||||||
state.list[action.payload.calId].events[id].calId =
|
|
||||||
action.payload.calId;
|
|
||||||
if (!state.list[action.payload.calId].events[id].timezone) {
|
|
||||||
state.list[action.payload.calId].events[id].timezone =
|
|
||||||
browserDefaultTimeZone;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.addCase(deleteEventAsync.fulfilled, (state, action) => {
|
|
||||||
state.pending = false;
|
state.pending = false;
|
||||||
const [baseId, recurrenceId] = action.payload.eventId.split("/");
|
|
||||||
if (recurrenceId) {
|
|
||||||
Object.keys(state.list[action.payload.calId].events).forEach(
|
|
||||||
(element) => {
|
|
||||||
if (extractEventBaseUuid(element) === baseId) {
|
|
||||||
delete state.list[action.payload.calId].events[element];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
delete state.list[action.payload.calId].events[
|
|
||||||
action.payload.eventId
|
|
||||||
];
|
|
||||||
}
|
|
||||||
state.error = null;
|
state.error = null;
|
||||||
})
|
})
|
||||||
.addCase(deleteEventInstanceAsync.fulfilled, (state, action) => {
|
.addCase(deleteEventInstanceAsync.fulfilled, (state, action) => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { formatReduxError } from "@/utils/errorUtils";
|
|||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { getUserDetails } from "@/features/User/userAPI";
|
import { getUserDetails } from "@/features/User/userAPI";
|
||||||
import { addSharedCalendar } from "../CalendarApi";
|
import { addSharedCalendar } from "../CalendarApi";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
|
|
||||||
export const addSharedCalendarAsync = createAsyncThunk<
|
export const addSharedCalendarAsync = createAsyncThunk<
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { getUserDetails } from "@/features/User/userAPI";
|
import { getUserDetails } from "@/features/User/userAPI";
|
||||||
|
import { userData } from "@/features/User/userDataTypes";
|
||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { postCalendar } from "../CalendarApi";
|
import { postCalendar } from "../CalendarApi";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
|
|
||||||
export const createCalendarAsync = createAsyncThunk<
|
export const createCalendarAsync = createAsyncThunk<
|
||||||
{
|
{
|
||||||
@@ -15,7 +16,7 @@ export const createCalendarAsync = createAsyncThunk<
|
|||||||
ownerEmails: string[];
|
ownerEmails: string[];
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
userId: string;
|
userData: userData;
|
||||||
calId: string;
|
calId: string;
|
||||||
color: Record<string, string>;
|
color: Record<string, string>;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -24,21 +25,25 @@ export const createCalendarAsync = createAsyncThunk<
|
|||||||
{ rejectValue: RejectedError }
|
{ rejectValue: RejectedError }
|
||||||
>(
|
>(
|
||||||
"calendars/createCalendar",
|
"calendars/createCalendar",
|
||||||
async ({ userId, calId, color, name, desc }, { rejectWithValue }) => {
|
async ({ userData, calId, color, name, desc }, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
await postCalendar(userId, calId, color, name, desc);
|
if (!userData.openpaasId) {
|
||||||
const ownerData: any = await getUserDetails(userId.split("/")[0]);
|
throw new Error("No openpaasId");
|
||||||
|
}
|
||||||
|
|
||||||
|
await postCalendar(userData.openpaasId, calId, color, name, desc);
|
||||||
|
const owner = [userData.given_name, userData.family_name]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userId,
|
userId: userData.openpaasId,
|
||||||
calId,
|
calId,
|
||||||
color,
|
color,
|
||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
owner: [ownerData.firstname, ownerData.lastname]
|
owner,
|
||||||
.filter(Boolean)
|
ownerEmails: userData.email ? [userData.email] : [],
|
||||||
.join(" "),
|
|
||||||
ownerEmails: ownerData.emails ?? [],
|
|
||||||
};
|
};
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return rejectWithValue({
|
return rejectWithValue({
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { deleteEvent } from "@/features/Events/EventApi";
|
import { deleteEvent } from "@/features/Events/EventApi";
|
||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
|
|
||||||
export const deleteEventAsync = createAsyncThunk<
|
export const deleteEventAsync = createAsyncThunk<
|
||||||
{ calId: string; eventId: string },
|
{ calId: string; eventId: string },
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { deleteEventInstance } from "@/features/Events/EventApi";
|
|||||||
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
import { Calendar } from "../CalendarTypes";
|
import { Calendar } from "../CalendarTypes";
|
||||||
|
|
||||||
export const deleteEventInstanceAsync = createAsyncThunk<
|
export const deleteEventInstanceAsync = createAsyncThunk<
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { CalendarEvent } from "@/features/Events/EventsTypes";
|
|||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { getCalendar } from "../CalendarApi";
|
import { getCalendar } from "../CalendarApi";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
import { extractCalendarEvents } from "../utils/extractCalendarEvents";
|
import { extractCalendarEvents } from "../utils/extractCalendarEvents";
|
||||||
|
|
||||||
export const getCalendarDetailAsync = createAsyncThunk<
|
export const getCalendarDetailAsync = createAsyncThunk<
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { getOpenPaasUser, getUserDetails } from "@/features/User/userAPI";
|
|||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { getCalendars } from "../CalendarApi";
|
import { getCalendars } from "../CalendarApi";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
import { Calendar } from "../CalendarTypes";
|
import { Calendar } from "../CalendarTypes";
|
||||||
import { normalizeCalendar } from "../utils/normalizeCalendar";
|
import { normalizeCalendar } from "../utils/normalizeCalendar";
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { getEvent } from "@/features/Events/EventApi";
|
|||||||
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
|
|
||||||
export const getEventAsync = createAsyncThunk<
|
export const getEventAsync = createAsyncThunk<
|
||||||
{ calId: string; event: CalendarEvent },
|
{ calId: string; event: CalendarEvent },
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { getUserDetails } from "@/features/User/userAPI";
|
|||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { getCalendars } from "../CalendarApi";
|
import { getCalendars } from "../CalendarApi";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
import { Calendar } from "../CalendarTypes";
|
import { Calendar } from "../CalendarTypes";
|
||||||
|
|
||||||
export const getTempCalendarsListAsync = createAsyncThunk<
|
export const getTempCalendarsListAsync = createAsyncThunk<
|
||||||
@@ -14,9 +14,14 @@ export const getTempCalendarsListAsync = createAsyncThunk<
|
|||||||
>("calendars/getTempCalendars", async (tempUser, { rejectWithValue }) => {
|
>("calendars/getTempCalendars", async (tempUser, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const importedCalendars: Record<string, Calendar> = {};
|
const importedCalendars: Record<string, Calendar> = {};
|
||||||
|
if (!tempUser.openpaasId) {
|
||||||
|
const username = tempUser.displayName || tempUser.email || "User";
|
||||||
|
throw new Error(
|
||||||
|
`TRANSLATION:calendar.userDoesNotHaveValidId|name=${encodeURIComponent(username)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
const calendars = (await getCalendars(
|
const calendars = (await getCalendars(
|
||||||
tempUser.openpaasId ?? "",
|
tempUser.openpaasId,
|
||||||
"sharedPublic=true&"
|
"sharedPublic=true&"
|
||||||
)) as Record<string, any>;
|
)) as Record<string, any>;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { importEventFromFile } from "@/features/Events/EventApi";
|
|||||||
import { importFile } from "@/utils/apiUtils";
|
import { importFile } from "@/utils/apiUtils";
|
||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
|
|
||||||
export const importEventFromFileAsync = createAsyncThunk<
|
export const importEventFromFileAsync = createAsyncThunk<
|
||||||
void,
|
void,
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import {
|
|||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { getCalendar } from "../CalendarApi";
|
import { getCalendar } from "../CalendarApi";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
import { Calendar } from "../CalendarTypes";
|
import { Calendar } from "../CalendarTypes";
|
||||||
|
|
||||||
export const moveEventAsync = createAsyncThunk<
|
export const moveEventAsync = createAsyncThunk<
|
||||||
{ calId: string; events: CalendarEvent[] },
|
{ calId: string },
|
||||||
{ cal: Calendar; newEvent: CalendarEvent; newURL: string },
|
{ cal: Calendar; newEvent: CalendarEvent; newURL: string },
|
||||||
{ rejectValue: RejectedError }
|
{ rejectValue: RejectedError }
|
||||||
>(
|
>(
|
||||||
@@ -21,31 +21,8 @@ export const moveEventAsync = createAsyncThunk<
|
|||||||
try {
|
try {
|
||||||
await moveEvent(newEvent, newURL);
|
await moveEvent(newEvent, newURL);
|
||||||
|
|
||||||
const eventDate = new Date(newEvent.start);
|
|
||||||
const { start: weekStart, end: weekEnd } = computeWeekRange(eventDate);
|
|
||||||
|
|
||||||
const calEvents = (await getCalendar(cal.id, {
|
|
||||||
start: formatDateToYYYYMMDDTHHMMSS(weekStart),
|
|
||||||
end: formatDateToYYYYMMDDTHHMMSS(weekEnd),
|
|
||||||
})) as Record<string, any>;
|
|
||||||
const events: CalendarEvent[] = calEvents._embedded["dav:item"].flatMap(
|
|
||||||
(eventdata: any) => {
|
|
||||||
const vevents = eventdata.data[2] as any[][];
|
|
||||||
const eventURL = eventdata._links.self.href;
|
|
||||||
return vevents.map((vevent: any[]) => {
|
|
||||||
return parseCalendarEvent(
|
|
||||||
vevent[1],
|
|
||||||
cal.color ?? {},
|
|
||||||
cal.id,
|
|
||||||
eventURL
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
calId: cal.id,
|
calId: cal.id,
|
||||||
events,
|
|
||||||
};
|
};
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return rejectWithValue({
|
return rejectWithValue({
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { updateAclCalendar } from "../CalendarApi";
|
import { updateAclCalendar } from "../CalendarApi";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
|
|
||||||
export const patchACLCalendarAsync = createAsyncThunk<
|
export const patchACLCalendarAsync = createAsyncThunk<
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { proppatchCalendar } from "../CalendarApi";
|
import { proppatchCalendar } from "../CalendarApi";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
|
|
||||||
export const patchCalendarAsync = createAsyncThunk<
|
export const patchCalendarAsync = createAsyncThunk<
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,18 +1,12 @@
|
|||||||
import { putEvent } from "@/features/Events/EventApi";
|
import { putEvent } from "@/features/Events/EventApi";
|
||||||
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
||||||
import { parseCalendarEvent } from "@/features/Events/eventUtils";
|
|
||||||
import {
|
|
||||||
computeWeekRange,
|
|
||||||
formatDateToYYYYMMDDTHHMMSS,
|
|
||||||
} from "@/utils/dateUtils";
|
|
||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { getCalendar } from "../CalendarApi";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
|
||||||
import { Calendar } from "../CalendarTypes";
|
import { Calendar } from "../CalendarTypes";
|
||||||
|
|
||||||
export const putEventAsync = createAsyncThunk<
|
export const putEventAsync = createAsyncThunk<
|
||||||
{ calId: string; events: CalendarEvent[]; calType?: "temp" },
|
{ calId: string; calType?: "temp" },
|
||||||
{ cal: Calendar; newEvent: CalendarEvent; calType?: "temp" },
|
{ cal: Calendar; newEvent: CalendarEvent; calType?: "temp" },
|
||||||
{ rejectValue: RejectedError }
|
{ rejectValue: RejectedError }
|
||||||
>(
|
>(
|
||||||
@@ -23,34 +17,9 @@ export const putEventAsync = createAsyncThunk<
|
|||||||
newEvent,
|
newEvent,
|
||||||
cal.ownerEmails ? cal.ownerEmails[0] : undefined
|
cal.ownerEmails ? cal.ownerEmails[0] : undefined
|
||||||
);
|
);
|
||||||
const eventDate = new Date(newEvent.start);
|
|
||||||
|
|
||||||
const { start: weekStart, end: weekEnd } = computeWeekRange(eventDate);
|
|
||||||
|
|
||||||
const calEvents = (await getCalendar(cal.id, {
|
|
||||||
start: formatDateToYYYYMMDDTHHMMSS(weekStart),
|
|
||||||
end: formatDateToYYYYMMDDTHHMMSS(weekEnd),
|
|
||||||
})) as Record<string, any>;
|
|
||||||
const events: CalendarEvent[] = calEvents._embedded["dav:item"].flatMap(
|
|
||||||
(eventdata: any) => {
|
|
||||||
const vevents = eventdata.data[2] as any[][];
|
|
||||||
const eventURL = eventdata._links.self.href;
|
|
||||||
const valarm = eventdata.data[2][0][2][0];
|
|
||||||
return vevents.map((vevent: any[]) => {
|
|
||||||
return parseCalendarEvent(
|
|
||||||
vevent[1],
|
|
||||||
cal.color ?? {},
|
|
||||||
cal.id,
|
|
||||||
eventURL,
|
|
||||||
valarm
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
calId: cal.id,
|
calId: cal.id,
|
||||||
events,
|
|
||||||
calType,
|
calType,
|
||||||
};
|
};
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { formatReduxError } from "@/utils/errorUtils";
|
|||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import pMap from "p-map";
|
import pMap from "p-map";
|
||||||
import { fetchSyncTokenChanges } from "../api/fetchSyncTokenChanges";
|
import { fetchSyncTokenChanges } from "../api/fetchSyncTokenChanges";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
import { Calendar } from "../CalendarTypes";
|
import { Calendar } from "../CalendarTypes";
|
||||||
import { expandEventFunction } from "../utils/expandEventFunction";
|
import { expandEventFunction } from "../utils/expandEventFunction";
|
||||||
import { processSyncUpdates } from "../utils/processSyncTokenUpdates";
|
import { processSyncUpdates } from "../utils/processSyncTokenUpdates";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { removeCalendar } from "../CalendarApi";
|
import { removeCalendar } from "../CalendarApi";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
|
|
||||||
export const removeCalendarAsync = createAsyncThunk<
|
export const removeCalendarAsync = createAsyncThunk<
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { putEventWithOverrides } from "@/features/Events/EventApi";
|
|||||||
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
import { Calendar } from "../CalendarTypes";
|
import { Calendar } from "../CalendarTypes";
|
||||||
|
|
||||||
export const updateEventInstanceAsync = createAsyncThunk<
|
export const updateEventInstanceAsync = createAsyncThunk<
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { updateSeries } from "@/features/Events/EventApi";
|
|||||||
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
||||||
import { formatReduxError } from "@/utils/errorUtils";
|
import { formatReduxError } from "@/utils/errorUtils";
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
import { RejectedError } from "../CalendarSlice";
|
import { RejectedError } from "../types/RejectedError";
|
||||||
import { Calendar } from "../CalendarTypes";
|
import { Calendar } from "../CalendarTypes";
|
||||||
|
|
||||||
export const updateSeriesAsync = createAsyncThunk<
|
export const updateSeriesAsync = createAsyncThunk<
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Define error type for rejected actions
|
||||||
|
|
||||||
|
export interface RejectedError {
|
||||||
|
message: string;
|
||||||
|
status?: number;
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Calendar } from "@/features/Calendars/CalendarTypes";
|
|
||||||
import { userData } from "@/features/User/userDataTypes";
|
import { userData } from "@/features/User/userDataTypes";
|
||||||
import { Box, Typography } from "@linagora/twake-mui";
|
import { Box, Typography } from "@linagora/twake-mui";
|
||||||
import { Dispatch, SetStateAction } from "react";
|
import { Dispatch, SetStateAction } from "react";
|
||||||
@@ -8,7 +7,6 @@ import { RSVPButton } from "./RSVPButton";
|
|||||||
|
|
||||||
interface AttendanceValidationProps {
|
interface AttendanceValidationProps {
|
||||||
contextualizedEvent: ContextualizedEvent;
|
contextualizedEvent: ContextualizedEvent;
|
||||||
calendarList: Calendar[];
|
|
||||||
user: userData | undefined;
|
user: userData | undefined;
|
||||||
setAfterChoiceFunc: Dispatch<SetStateAction<Function | undefined>>;
|
setAfterChoiceFunc: Dispatch<SetStateAction<Function | undefined>>;
|
||||||
setOpenEditModePopup: Dispatch<SetStateAction<string | null>>;
|
setOpenEditModePopup: Dispatch<SetStateAction<string | null>>;
|
||||||
@@ -16,7 +14,6 @@ interface AttendanceValidationProps {
|
|||||||
|
|
||||||
export function AttendanceValidation({
|
export function AttendanceValidation({
|
||||||
contextualizedEvent,
|
contextualizedEvent,
|
||||||
calendarList,
|
|
||||||
user,
|
user,
|
||||||
setAfterChoiceFunc,
|
setAfterChoiceFunc,
|
||||||
setOpenEditModePopup,
|
setOpenEditModePopup,
|
||||||
@@ -36,7 +33,6 @@ export function AttendanceValidation({
|
|||||||
const commonButtonProps = {
|
const commonButtonProps = {
|
||||||
contextualizedEvent,
|
contextualizedEvent,
|
||||||
user,
|
user,
|
||||||
calendarList,
|
|
||||||
setAfterChoiceFunc,
|
setAfterChoiceFunc,
|
||||||
setOpenEditModePopup,
|
setOpenEditModePopup,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useAppDispatch } from "@/app/hooks";
|
import { useAppDispatch } from "@/app/hooks";
|
||||||
import { Calendar } from "@/features/Calendars/CalendarTypes";
|
|
||||||
import { PartStat } from "@/features/User/models/attendee";
|
import { PartStat } from "@/features/User/models/attendee";
|
||||||
import { userData } from "@/features/User/userDataTypes";
|
import { userData } from "@/features/User/userDataTypes";
|
||||||
import { Button } from "@linagora/twake-mui";
|
import { Button } from "@linagora/twake-mui";
|
||||||
@@ -20,7 +19,6 @@ interface RSVPButtonProps {
|
|||||||
rsvpValue: PartStat;
|
rsvpValue: PartStat;
|
||||||
contextualizedEvent: ContextualizedEvent;
|
contextualizedEvent: ContextualizedEvent;
|
||||||
user: userData | undefined;
|
user: userData | undefined;
|
||||||
calendarList: Calendar[];
|
|
||||||
setAfterChoiceFunc: Dispatch<SetStateAction<Function | undefined>>;
|
setAfterChoiceFunc: Dispatch<SetStateAction<Function | undefined>>;
|
||||||
setOpenEditModePopup: Dispatch<SetStateAction<string | null>>;
|
setOpenEditModePopup: Dispatch<SetStateAction<string | null>>;
|
||||||
}
|
}
|
||||||
@@ -29,7 +27,6 @@ export function RSVPButton({
|
|||||||
rsvpValue,
|
rsvpValue,
|
||||||
contextualizedEvent,
|
contextualizedEvent,
|
||||||
user,
|
user,
|
||||||
calendarList,
|
|
||||||
setAfterChoiceFunc,
|
setAfterChoiceFunc,
|
||||||
setOpenEditModePopup,
|
setOpenEditModePopup,
|
||||||
}: RSVPButtonProps) {
|
}: RSVPButtonProps) {
|
||||||
@@ -54,7 +51,6 @@ export function RSVPButton({
|
|||||||
rsvpValue,
|
rsvpValue,
|
||||||
contextualizedEvent,
|
contextualizedEvent,
|
||||||
user,
|
user,
|
||||||
calendarList,
|
|
||||||
setAfterChoiceFunc,
|
setAfterChoiceFunc,
|
||||||
setOpenEditModePopup,
|
setOpenEditModePopup,
|
||||||
dispatch
|
dispatch
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { AppDispatch } from "@/app/store";
|
import { AppDispatch } from "@/app/store";
|
||||||
import { handleRSVP } from "@/components/Event/eventHandlers/eventHandlers";
|
import { handleRSVP } from "@/components/Event/eventHandlers/eventHandlers";
|
||||||
import { Calendar } from "@/features/Calendars/CalendarTypes";
|
|
||||||
import { PartStat } from "@/features/User/models/attendee";
|
import { PartStat } from "@/features/User/models/attendee";
|
||||||
import { userData } from "@/features/User/userDataTypes";
|
import { userData } from "@/features/User/userDataTypes";
|
||||||
import { Dispatch, SetStateAction } from "react";
|
import { Dispatch, SetStateAction } from "react";
|
||||||
@@ -10,7 +9,6 @@ export async function handleRSVPClick(
|
|||||||
rsvp: PartStat,
|
rsvp: PartStat,
|
||||||
contextualizedEvent: ContextualizedEvent,
|
contextualizedEvent: ContextualizedEvent,
|
||||||
user: userData | undefined,
|
user: userData | undefined,
|
||||||
calendarList: Calendar[],
|
|
||||||
setAfterChoiceFunc: Dispatch<SetStateAction<Function | undefined>>,
|
setAfterChoiceFunc: Dispatch<SetStateAction<Function | undefined>>,
|
||||||
setOpenEditModePopup: Dispatch<SetStateAction<string | null>>,
|
setOpenEditModePopup: Dispatch<SetStateAction<string | null>>,
|
||||||
dispatch: AppDispatch
|
dispatch: AppDispatch
|
||||||
@@ -19,15 +17,7 @@ export async function handleRSVPClick(
|
|||||||
if (isRecurring) {
|
if (isRecurring) {
|
||||||
setAfterChoiceFunc(() => async (type: string) => {
|
setAfterChoiceFunc(() => async (type: string) => {
|
||||||
try {
|
try {
|
||||||
await handleRSVP(
|
await handleRSVP(dispatch, calendar, user, event, rsvp, type);
|
||||||
dispatch,
|
|
||||||
calendar,
|
|
||||||
user,
|
|
||||||
event,
|
|
||||||
rsvp,
|
|
||||||
type,
|
|
||||||
calendarList
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error handling RSVP:", error);
|
console.error("Error handling RSVP:", error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
import { useAppDispatch, useAppSelector } from "@/app/hooks";
|
import { useAppDispatch, useAppSelector } from "@/app/hooks";
|
||||||
import { CalendarName } from "@/components/Calendar/CalendarName";
|
import { CalendarName } from "@/components/Calendar/CalendarName";
|
||||||
import { getTimezoneOffset } from "@/components/Calendar/TimezoneSelector";
|
import { getTimezoneOffset } from "@/components/Calendar/TimezoneSelector";
|
||||||
import {
|
import { formatEventChipTitle } from "@/components/Calendar/utils/calendarUtils";
|
||||||
formatEventChipTitle,
|
|
||||||
updateTempCalendar,
|
|
||||||
} from "@/components/Calendar/utils/calendarUtils";
|
|
||||||
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 { handleDelete } from "@/components/Event/eventHandlers/eventHandlers";
|
import { handleDelete } 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 { browserDefaultTimeZone } from "@/utils/timezone";
|
import { browserDefaultTimeZone } from "@/utils/timezone";
|
||||||
import { DateSelectArg } from "@fullcalendar/core";
|
import { DateSelectArg } from "@fullcalendar/core";
|
||||||
import {
|
import {
|
||||||
@@ -282,18 +278,6 @@ export default function EventPreviewModal({
|
|||||||
(a) => a.cal_address === event.organizer?.cal_address
|
(a) => a.cal_address === event.organizer?.cal_address
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateTempList = async () => {
|
|
||||||
if (calendars.templist) {
|
|
||||||
const calendarRange = getCalendarRange(new Date(event.start));
|
|
||||||
await updateTempCalendar(
|
|
||||||
calendars.templist,
|
|
||||||
event,
|
|
||||||
dispatch,
|
|
||||||
calendarRange
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ResponsiveDialog
|
<ResponsiveDialog
|
||||||
@@ -429,8 +413,6 @@ export default function EventPreviewModal({
|
|||||||
if (result && typeof result.unwrap === "function") {
|
if (result && typeof result.unwrap === "function") {
|
||||||
await result.unwrap();
|
await result.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateTempList();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to delete event:", error);
|
console.error("Failed to delete event:", error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,12 +60,11 @@ function EventPopover({
|
|||||||
event?: CalendarEvent;
|
event?: CalendarEvent;
|
||||||
}) {
|
}) {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t, lang } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const organizer = useAppSelector((state) => state.user.organiserData);
|
const organizer = useAppSelector((state) => state.user.organiserData);
|
||||||
const userId =
|
const userId =
|
||||||
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
|
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
|
||||||
const tempList = useAppSelector((state) => state.calendars.templist);
|
|
||||||
const calList = useAppSelector((state) => state.calendars.list);
|
const calList = useAppSelector((state) => state.calendars.list);
|
||||||
const selectPersonalCalendars = createSelector(
|
const selectPersonalCalendars = createSelector(
|
||||||
(state: any) => state.calendars,
|
(state: any) => state.calendars,
|
||||||
@@ -838,11 +837,6 @@ function EventPopover({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tempList) {
|
|
||||||
const calendarRange = getCalendarRange(new Date(start));
|
|
||||||
await updateTempCalendar(tempList, newEvent, dispatch, calendarRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear temp data on successful save
|
// Clear temp data on successful save
|
||||||
clearEventFormTempData("create");
|
clearEventFormTempData("create");
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ function EventUpdateModal({
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const tempList = useAppSelector((state) => state.calendars.templist);
|
|
||||||
const calList = useAppSelector((state) => state.calendars.list);
|
const calList = useAppSelector((state) => state.calendars.list);
|
||||||
// Get event from Redux store (cached data) as fallback
|
// Get event from Redux store (cached data) as fallback
|
||||||
const cachedEvent = useAppSelector(
|
const cachedEvent = useAppSelector(
|
||||||
@@ -78,6 +77,10 @@ function EventUpdateModal({
|
|||||||
// Use fresh data if available, otherwise use eventData from props, otherwise use cached data
|
// Use fresh data if available, otherwise use eventData from props, otherwise use cached data
|
||||||
const event = freshEvent || eventData || cachedEvent;
|
const event = freshEvent || eventData || cachedEvent;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFreshEvent(null);
|
||||||
|
}, [eventId, calId]);
|
||||||
|
|
||||||
// Fetch fresh event data when modal opens
|
// Fetch fresh event data when modal opens
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open && cachedEvent && !eventData) {
|
if (open && cachedEvent && !eventData) {
|
||||||
@@ -714,11 +717,6 @@ function EventUpdateModal({
|
|||||||
// Clear cache to ensure navigation to other weeks works
|
// Clear cache to ensure navigation to other weeks works
|
||||||
dispatch(clearFetchCache(calId));
|
dispatch(clearFetchCache(calId));
|
||||||
|
|
||||||
if (tempList) {
|
|
||||||
const calendarRange = getCalendarRange(new Date(start));
|
|
||||||
await updateTempCalendar(tempList, event, dispatch, calendarRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
// STEP 5: Remove old recurring instances only after the rest succeeds
|
// STEP 5: Remove old recurring instances only after the rest succeeds
|
||||||
removeSeriesInstancesFromUI();
|
removeSeriesInstancesFromUI();
|
||||||
|
|
||||||
@@ -834,7 +832,7 @@ function EventUpdateModal({
|
|||||||
const repetitionRulesChanged = changes.repetitionRulesChanged;
|
const repetitionRulesChanged = changes.repetitionRulesChanged;
|
||||||
|
|
||||||
if (repetitionRulesChanged) {
|
if (repetitionRulesChanged) {
|
||||||
// Date/time or repetition rules changed - remove all overrides and refresh
|
// Date/time or repetition rules changed - remove all overrides
|
||||||
|
|
||||||
const seriesInstancesSnapshot = getSeriesInstances();
|
const seriesInstancesSnapshot = getSeriesInstances();
|
||||||
|
|
||||||
@@ -892,23 +890,6 @@ function EventUpdateModal({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// STEP 3: Fetch to get new instances with correct timing
|
|
||||||
// If refreshCalendars fails, we need to throw error to reopen modal
|
|
||||||
try {
|
|
||||||
const calendarRange = getCalendarRange(new Date(start));
|
|
||||||
await refreshCalendars(
|
|
||||||
dispatch,
|
|
||||||
Object.values(calendarsList),
|
|
||||||
calendarRange
|
|
||||||
);
|
|
||||||
} catch (refreshError: any) {
|
|
||||||
// If refreshCalendars fails, throw error to reopen modal
|
|
||||||
throw new Error(
|
|
||||||
refreshError?.message ||
|
|
||||||
"Failed to refresh calendar events. Please try again."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear cache after reload
|
// Clear cache after reload
|
||||||
dispatch(clearFetchCache(calId));
|
dispatch(clearFetchCache(calId));
|
||||||
|
|
||||||
@@ -1085,15 +1066,10 @@ function EventUpdateModal({
|
|||||||
if (moveResult && typeof moveResult.unwrap === "function") {
|
if (moveResult && typeof moveResult.unwrap === "function") {
|
||||||
await moveResult.unwrap();
|
await moveResult.unwrap();
|
||||||
}
|
}
|
||||||
dispatch(removeEvent({ calendarUid: calId, eventUid: event.uid }));
|
|
||||||
|
|
||||||
// Clear temp data on successful move
|
// Clear temp data on successful move
|
||||||
clearEventFormTempData("update");
|
clearEventFormTempData("update");
|
||||||
}
|
}
|
||||||
if (tempList) {
|
|
||||||
const calendarRange = getCalendarRange(new Date(start));
|
|
||||||
await updateTempCalendar(tempList, event, dispatch, calendarRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset all state to default values only on successful save (after all branches)
|
// Reset all state to default values only on successful save (after all branches)
|
||||||
clearEventFormTempData("update");
|
clearEventFormTempData("update");
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ export function findCalendarById(
|
|||||||
if (calendarId.length === 0) {
|
if (calendarId.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (state.calendars?.list?.[calendarId]) {
|
|
||||||
return { calendar: state.calendars.list[calendarId] };
|
|
||||||
}
|
|
||||||
if (state.calendars?.templist?.[calendarId]) {
|
if (state.calendars?.templist?.[calendarId]) {
|
||||||
return { calendar: state.calendars.templist[calendarId], type: "temp" };
|
return { calendar: state.calendars.templist[calendarId], type: "temp" };
|
||||||
}
|
}
|
||||||
|
if (state.calendars?.list?.[calendarId]) {
|
||||||
|
return { calendar: state.calendars.list[calendarId] };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { syncCalendarRegistrations } from "./operations";
|
|||||||
export function WebSocketGate() {
|
export function WebSocketGate() {
|
||||||
const socketRef = useRef<WebSocketWithCleanup | null>(null);
|
const socketRef = useRef<WebSocketWithCleanup | null>(null);
|
||||||
const previousCalendarListRef = useRef<string[]>([]);
|
const previousCalendarListRef = useRef<string[]>([]);
|
||||||
|
const previousTempCalendarListRef = useRef<string[]>([]);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const isAuthenticated = useAppSelector((state) =>
|
const isAuthenticated = useAppSelector((state) =>
|
||||||
Boolean(state.user.userData && state.user.tokens)
|
Boolean(state.user.userData && state.user.tokens)
|
||||||
@@ -18,6 +19,10 @@ export function WebSocketGate() {
|
|||||||
const [isSocketOpen, setIsSocketOpen] = useState(false);
|
const [isSocketOpen, setIsSocketOpen] = useState(false);
|
||||||
|
|
||||||
const calendarList = useSelectedCalendars();
|
const calendarList = useSelectedCalendars();
|
||||||
|
const tempCalendarList = Object.keys(
|
||||||
|
useAppSelector((state) => state?.calendars?.templist) ?? {}
|
||||||
|
);
|
||||||
|
|
||||||
const onMessage = useCallback(
|
const onMessage = useCallback(
|
||||||
(message: unknown) => {
|
(message: unknown) => {
|
||||||
updateCalendars(message, dispatch);
|
updateCalendars(message, dispatch);
|
||||||
@@ -76,5 +81,14 @@ export function WebSocketGate() {
|
|||||||
);
|
);
|
||||||
}, [isSocketOpen, calendarList]);
|
}, [isSocketOpen, calendarList]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
syncCalendarRegistrations(
|
||||||
|
isSocketOpen,
|
||||||
|
socketRef,
|
||||||
|
tempCalendarList,
|
||||||
|
previousTempCalendarListRef
|
||||||
|
);
|
||||||
|
}, [isSocketOpen, tempCalendarList]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user