Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/web/src/__tests__/utils/test.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export const mockBSON = () => {
toString() {
return crypto.randomUUID();
}

static isValid(value?: string) {
return value?.length === 36;
}
},
}));
};
49 changes: 11 additions & 38 deletions packages/web/src/common/utils/event.util.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,22 @@
import dayjs, { Dayjs } from "dayjs";
import isBetween from "dayjs/plugin/isBetween";
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
import { v4 as uuidv4 } from "uuid";
import { DropResult } from "@hello-pangea/dnd";
import { Origin, Priorities } from "@core/constants/core.constants";
import { YEAR_MONTH_DAY_COMPACT_FORMAT } from "@core/constants/date.constants";
import { Status } from "@core/errors/status.codes";
import { Categories_Event, Schema_Event } from "@core/types/event.types";
import dayjs, { Dayjs } from "@core/util/date/dayjs";
import { validateEvent } from "@core/validators/event.validator";
import { getUserId } from "@web/auth/auth.util";
import { PartialMouseEvent } from "@web/common/types/util.types";
import { validateSomedayEvent } from "@web/common/validators/someday.event.validator";
import {
DATA_EVENT_ELEMENT_ID,
ID_OPTIMISTIC_PREFIX,
} from "../constants/web.constants";
} from "@web/common/constants/web.constants";
import { PartialMouseEvent } from "@web/common/types/util.types";
import {
Schema_GridEvent,
Schema_OptimisticEvent,
} from "../types/web.event.types";

dayjs.extend(isSameOrAfter);
dayjs.extend(isSameOrBefore);
dayjs.extend(isBetween);

export const assembleBaseEvent = (
userId: string,
event: Partial<Schema_Event>,
): Schema_Event => {
const baseEvent = {
_id: event._id,
title: event.title || "",
description: event.description || "",
startDate: event.startDate,
endDate: event.endDate,
user: userId,
isAllDay: event.isAllDay || false,
isSomeday: event.isSomeday || false,
origin: event.origin || Origin.COMPASS,
priority: event.priority || Priorities.UNASSIGNED,
};

return baseEvent;
};
} from "@web/common/types/web.event.types";
import { validateSomedayEvent } from "@web/common/validators/someday.event.validator";

const gridEventDefaultPosition = {
isOverlapping: false,
Expand All @@ -55,7 +28,7 @@ const gridEventDefaultPosition = {
};

export const assembleDefaultEvent = async (
draftType: Categories_Event,
draftType?: Categories_Event | null,
startDate?: string,
endDate?: string,
): Promise<Schema_Event | Schema_GridEvent> => {
Expand Down Expand Up @@ -192,7 +165,7 @@ export const isOptimisticEvent = (event: Schema_Event) => {

export const prepEvtAfterDraftDrop = (
category: Categories_Event,
dropItem: DropResult,
dropItem: DropResult & Schema_Event,
dates: { startDate: string; endDate: string },
) => {
const baseEvent = assembleDefaultEvent(category);
Expand Down Expand Up @@ -260,10 +233,10 @@ const _assembleBaseEvent = (
startDate: event.startDate,
endDate: event.endDate,
user: userId,
isAllDay: event.isAllDay || false,
isSomeday: event.isSomeday || false,
origin: event.origin || Origin.COMPASS,
priority: event.priority || Priorities.UNASSIGNED,
isAllDay: event.isAllDay ?? false,
isSomeday: event.isSomeday ?? false,
origin: event.origin ?? Origin.COMPASS,
priority: event.priority ?? Priorities.UNASSIGNED,
};

return baseEvent;
Expand Down
8 changes: 6 additions & 2 deletions packages/web/src/components/Button/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,26 @@ export const PriorityButton = styled(Btn)<PalletteProps>`

&:hover {
background: ${({ theme }) => theme.color.bg.primary};
color: ${({ color }) => brighten(color)};
color: ${({ color }) => brighten(color!)};
transition: background-color 0.5s;
transition: color 0.55s;
}
`;
interface CustomProps {
priority: Priority;
minWidth: number;
disabled?: boolean;
}

export const StyledSaveBtn = styled(PriorityButton)<CustomProps>`
background: ${({ priority }) => darken(colorByPriority[priority])};
color: ${({ theme }) => theme.color.text.dark}

min-width: ${({ minWidth }) => minWidth}px;

opacity: ${({ disabled }) => (disabled ? 0.5 : 1)};
pointer-events: ${({ disabled }) => (disabled ? "none" : "auto")};

&:focus {
border: 2px solid ${({ theme }) => theme.color.border.primaryDark};
}
Expand Down
16 changes: 9 additions & 7 deletions packages/web/src/components/ContextMenu/ContextMenuItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ import { colorByPriority } from "@web/common/styles/theme.util";
import { Schema_GridEvent } from "@web/common/types/web.event.types";
import { assembleGridEvent } from "@web/common/utils/event.util";
import { getSomedayEventCategory } from "@web/common/utils/someday.util";
import IconButton from "@web/components/IconButton/IconButton";
import { useDraftContext } from "@web/views/Calendar/components/Draft/context/useDraftContext";
import { useSidebarContext } from "@web/views/Calendar/components/Draft/sidebar/context/useSidebarContext";
import {
MenuItem,
MenuItemLabel,
PriorityCircle,
PriorityContainer,
TooltipText,
TooltipWrapper,
} from "./styled";
} from "@web/components/ContextMenu/styled";
import IconButton from "@web/components/IconButton/IconButton";
import { useDraftContext } from "@web/views/Calendar/components/Draft/context/useDraftContext";
import { useSidebarContext } from "@web/views/Calendar/components/Draft/sidebar/context/useSidebarContext";

export interface ContextMenuAction {
id: string;
Expand All @@ -31,8 +31,8 @@ interface ContextMenuItemsProps {
}

export function ContextMenuItems({ event, close }: ContextMenuItemsProps) {
const { actions, setters } = useDraftContext();
const { openForm, deleteEvent, duplicateEvent, submit } = actions;
const { actions, setters, confirmation } = useDraftContext();
const { openForm, duplicateEvent, submit } = actions;
const { setDraft } = setters;

const sidebarContext = useSidebarContext(true);
Expand Down Expand Up @@ -80,6 +80,8 @@ export function ContextMenuItems({ event, close }: ContextMenuItemsProps) {
}
};

const { onDelete } = confirmation;

const menuActions: ContextMenuAction[] = [
{
id: "edit",
Expand All @@ -104,7 +106,7 @@ export function ContextMenuItems({ event, close }: ContextMenuItemsProps) {
{
id: "delete",
label: "Delete",
onClick: deleteEvent,
onClick: onDelete,
icon: (
<IconButton>
<Trash />
Expand Down
4 changes: 2 additions & 2 deletions packages/web/src/ducks/events/event.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const EventApi = {
create: (event: Schema_Event | Schema_Event[]) => {
return CompassApi.post(`/event`, event);
},
delete: (_id: string) => {
return CompassApi.delete(`/event/${_id}`);
delete: (_id: string, applyTo?: RecurringEventUpdateScope) => {
return CompassApi.delete(`/event/${_id}?applyTo=${applyTo}`);
},
edit: (
_id: string,
Expand Down
1 change: 1 addition & 0 deletions packages/web/src/ducks/events/event.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ interface Payload_ConvertTimedEvent {

interface Payload_DeleteEvent {
_id: string;
applyTo?: RecurringEventUpdateScope;
}

export interface Payload_EditEvent {
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/ducks/events/sagas/event.sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export function* deleteEvent({ payload }: Action_DeleteEvent) {

const isInDb = !event._id.startsWith(ID_OPTIMISTIC_PREFIX);
if (isInDb) {
yield call(EventApi.delete, payload._id);
yield call(EventApi.delete, payload._id, payload.applyTo);
}

yield put(deleteEventSlice.actions.success());
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/ducks/events/sagas/someday.sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function* deleteSomedayEvent({ payload }: Action_DeleteEvent) {
try {
yield put(eventsEntitiesSlice.actions.delete(payload));

yield call(EventApi.delete, payload._id);
yield call(EventApi.delete, payload._id, payload.applyTo);
} catch (error) {
yield put(getSomedayEventsSlice.actions.error());
handleError(error as Error);
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/ducks/events/slices/event.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const deleteEventSlice = createAsyncSlice<{ _id: string }>({

export const editEventSlice = createAsyncSlice<Payload_EditEvent>({
name: "editEvent",
initialState: {},
initialState: {} as unknown as undefined,
reducers: {
migrate: () => {},
},
Expand Down
3 changes: 3 additions & 0 deletions packages/web/src/views/Calendar/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ContextMenuWrapper } from "@web/components/ContextMenu/GridContextMenuW
import { FlexDirections } from "@web/components/Flex/styled";
import { selectIsSidebarOpen } from "@web/ducks/events/selectors/view.selectors";
import { useAppSelector } from "@web/store/store.hooks";
import { RecurringEventUpdateScopeDialog } from "@web/views/Forms/EventForm/RecurringEventUpdateScopeDialog";
import { CmdPalette } from "../CmdPalette";
import { RootProps } from "./calendarView.types";
import { Dedication } from "./components/Dedication";
Expand Down Expand Up @@ -104,6 +105,8 @@ export const CalendarView = () => {
</StyledCalendar>
</Shortcuts>
</SidebarDraftProvider>

<RecurringEventUpdateScopeDialog />
</DraftProvider>
</Styled>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createContext } from "react";
import { Actions_Draft } from "../hooks/actions/useDraftActions";
import { useDraftConfirmation } from "../hooks/state/useDraftConfirmation";
import { useDraftForm } from "../hooks/state/useDraftForm";
import { Setters_Draft, State_Draft_Local } from "../hooks/state/useDraftState";

Expand All @@ -12,6 +13,7 @@ interface DraftContextValue {
state: State_Draft;
setters: Setters_Draft;
actions: Actions_Draft;
confirmation: ReturnType<typeof useDraftConfirmation>;
}

export const DraftContext = createContext<DraftContextValue | null>(null);
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useAppSelector } from "@web/store/store.hooks";
import { DateCalcs } from "@web/views/Calendar/hooks/grid/useDateCalcs";
import { WeekProps } from "@web/views/Calendar/hooks/useWeek";
import { useDraftActions } from "../hooks/actions/useDraftActions";
import { useDraftConfirmation } from "../hooks/state/useDraftConfirmation";
import { useDraftForm } from "../hooks/state/useDraftForm";
import { useDraftState } from "../hooks/state/useDraftState";
import { DraftContext, State_Draft } from "./DraftContext";
Expand Down Expand Up @@ -35,6 +36,7 @@ export const DraftProvider = ({

const _category = useAppSelector(selectDraftCategory);
const category = _category || Categories_Event.TIMED;

const formProps = useDraftForm(
category,
isFormOpen,
Expand All @@ -48,8 +50,10 @@ export const DraftProvider = ({
formProps,
};

const confirmation = useDraftConfirmation({ actions, state });

return (
<DraftContext.Provider value={{ state, setters, actions }}>
<DraftContext.Provider value={{ state, setters, actions, confirmation }}>
{children}
</DraftContext.Provider>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React, { FC, MouseEvent } from "react";
import React, { FC, MouseEvent, useMemo } from "react";
import { FloatingFocusManager } from "@floating-ui/react";
import { YEAR_MONTH_DAY_FORMAT } from "@core/constants/date.constants";
import { Categories_Event } from "@core/types/event.types";
import { PartialMouseEvent } from "@web/common/types/util.types";
import { Schema_GridEvent } from "@web/common/types/web.event.types";
import { getEventDragOffset } from "@web/common/utils/event.util";
import { useDraftContext } from "@web/views/Calendar/components/Draft/context/useDraftContext";
import { GridEvent } from "@web/views/Calendar/components/Event/Grid";
import { useGridEventMouseDown } from "@web/views/Calendar/hooks/grid/useGridEventMouseDown";
import { Measurements_Grid } from "@web/views/Calendar/hooks/grid/useGridLayout";
import { WeekProps } from "@web/views/Calendar/hooks/useWeek";
import { EventForm } from "@web/views/Forms/EventForm/EventForm";
import { StyledFloatContainer } from "@web/views/Forms/SomedayEventForm/styled";
import { GridEvent } from "../../Event/Grid";
import { useDraftContext } from "../context/useDraftContext";

interface Props {
draft: Schema_GridEvent;
Expand All @@ -22,9 +22,9 @@ interface Props {
}

export const GridDraft: FC<Props> = ({ measurements, weekProps }) => {
const { actions, setters, state } = useDraftContext();
const { discard, deleteEvent, duplicateEvent, submit, startDragging } =
actions;
const { actions, setters, state, confirmation } = useDraftContext();
const { discard, duplicateEvent } = actions;
const { startDragging } = actions;
const { setDraft, setDateBeingChanged, setIsResizing } = setters;
const { draft, isDragging, formProps, isFormOpen, isResizing } = state;
const { context, getReferenceProps, getFloatingProps, x, y, refs, strategy } =
Expand Down Expand Up @@ -55,12 +55,19 @@ export const GridDraft: FC<Props> = ({ measurements, weekProps }) => {
startDragging();
};

const { onSubmit, onDelete } = confirmation;

const { onMouseDown } = useGridEventMouseDown(
draft?.isAllDay ? Categories_Event.ALLDAY : Categories_Event.TIMED,
handleClick,
handleDrag,
);

const formDirty = useMemo(
() => (draft ? actions.isEventDirty(draft) : false),
[draft, actions.isEventDirty],
);

if (!draft) return null;

return (
Expand All @@ -78,7 +85,7 @@ export const GridDraft: FC<Props> = ({ measurements, weekProps }) => {
onMouseDown(e, event);
}}
onScalerMouseDown={(
event: Schema_GridEvent,
_event: Schema_GridEvent,
e: MouseEvent,
dateToChange: "startDate" | "endDate",
) => {
Expand Down Expand Up @@ -106,9 +113,10 @@ export const GridDraft: FC<Props> = ({ measurements, weekProps }) => {
event={draft}
onClose={discard}
onConvert={onConvert}
onDelete={deleteEvent}
onDelete={onDelete}
onDuplicate={duplicateEvent}
onSubmit={(_draft: Schema_GridEvent) => submit(_draft)}
disableSaveBtn={!formDirty}
onSubmit={onSubmit}
setEvent={setDraft}
/>
</StyledFloatContainer>
Expand Down
Loading