mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-18 09:02:11 +03:00
feat: open event details drawer on event row click (#4464)
* feat: open event details drawer on event row click Closes #4294 * feat: review - display Calendar Event details Inline Cells in readonly mode * fix: fix calendar event field values not being set * chore: review - reactivate no-extra-boolean-cast eslint rule
This commit is contained in:
parent
680bb11f19
commit
38f28de4a6
@ -4,10 +4,11 @@ import { format, getYear } from 'date-fns';
|
|||||||
import { CalendarMonthCard } from '@/activities/calendar/components/CalendarMonthCard';
|
import { CalendarMonthCard } from '@/activities/calendar/components/CalendarMonthCard';
|
||||||
import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext';
|
import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext';
|
||||||
import { useCalendarEvents } from '@/activities/calendar/hooks/useCalendarEvents';
|
import { useCalendarEvents } from '@/activities/calendar/hooks/useCalendarEvents';
|
||||||
import { sortCalendarEventsDesc } from '@/activities/calendar/utils/sortCalendarEvents';
|
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||||
import { H3Title } from '@/ui/display/typography/components/H3Title';
|
import { H3Title } from '@/ui/display/typography/components/H3Title';
|
||||||
import { Section } from '@/ui/layout/section/components/Section';
|
import { Section } from '@/ui/layout/section/components/Section';
|
||||||
import { mockedCalendarEvents } from '~/testing/mock-data/calendar';
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -23,9 +24,11 @@ const StyledYear = styled.span`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const Calendar = () => {
|
export const Calendar = () => {
|
||||||
const sortedCalendarEvents = [...mockedCalendarEvents].sort(
|
const { records: calendarEvents } = useFindManyRecords<CalendarEvent>({
|
||||||
sortCalendarEventsDesc,
|
objectNameSingular: CoreObjectNameSingular.CalendarEvent,
|
||||||
);
|
orderBy: { startsAt: 'DescNullsLast', endsAt: 'DescNullsLast' },
|
||||||
|
useRecordsWithoutConnection: true,
|
||||||
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
calendarEventsByDayTime,
|
calendarEventsByDayTime,
|
||||||
@ -35,7 +38,13 @@ export const Calendar = () => {
|
|||||||
monthTimes,
|
monthTimes,
|
||||||
monthTimesByYear,
|
monthTimesByYear,
|
||||||
updateCurrentCalendarEvent,
|
updateCurrentCalendarEvent,
|
||||||
} = useCalendarEvents(sortedCalendarEvents);
|
} = useCalendarEvents(
|
||||||
|
calendarEvents.map((calendarEvent) => ({
|
||||||
|
...calendarEvent,
|
||||||
|
// TODO: retrieve CalendarChannel visibility from backend
|
||||||
|
visibility: 'SHARE_EVERYTHING',
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CalendarContext.Provider
|
<CalendarContext.Provider
|
||||||
|
@ -12,6 +12,7 @@ import { AnimatePresence, motion } from 'framer-motion';
|
|||||||
import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext';
|
import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext';
|
||||||
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||||
import { getCalendarEventEndDate } from '@/activities/calendar/utils/getCalendarEventEndDate';
|
import { getCalendarEventEndDate } from '@/activities/calendar/utils/getCalendarEventEndDate';
|
||||||
|
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
|
||||||
import { hasCalendarEventEnded } from '@/activities/calendar/utils/hasCalendarEventEnded';
|
import { hasCalendarEventEnded } from '@/activities/calendar/utils/hasCalendarEventEnded';
|
||||||
import { hasCalendarEventStarted } from '@/activities/calendar/utils/hasCalendarEventStarted';
|
import { hasCalendarEventStarted } from '@/activities/calendar/utils/hasCalendarEventStarted';
|
||||||
|
|
||||||
@ -52,12 +53,16 @@ export const CalendarCurrentEventCursor = ({
|
|||||||
} = useContext(CalendarContext);
|
} = useContext(CalendarContext);
|
||||||
|
|
||||||
const nextCalendarEvent = getNextCalendarEvent(calendarEvent);
|
const nextCalendarEvent = getNextCalendarEvent(calendarEvent);
|
||||||
|
const nextCalendarEventStartsAt = nextCalendarEvent
|
||||||
|
? getCalendarEventStartDate(nextCalendarEvent)
|
||||||
|
: undefined;
|
||||||
const isNextEventThisMonth =
|
const isNextEventThisMonth =
|
||||||
!!nextCalendarEvent && isThisMonth(nextCalendarEvent.startsAt);
|
!!nextCalendarEventStartsAt && isThisMonth(nextCalendarEventStartsAt);
|
||||||
|
|
||||||
|
const calendarEventStartsAt = getCalendarEventStartDate(calendarEvent);
|
||||||
const calendarEventEndsAt = getCalendarEventEndDate(calendarEvent);
|
const calendarEventEndsAt = getCalendarEventEndDate(calendarEvent);
|
||||||
|
|
||||||
const isCurrent = currentCalendarEvent.id === calendarEvent.id;
|
const isCurrent = currentCalendarEvent?.id === calendarEvent.id;
|
||||||
const [hasStarted, setHasStarted] = useState(
|
const [hasStarted, setHasStarted] = useState(
|
||||||
hasCalendarEventStarted(calendarEvent),
|
hasCalendarEventStarted(calendarEvent),
|
||||||
);
|
);
|
||||||
@ -66,7 +71,7 @@ export const CalendarCurrentEventCursor = ({
|
|||||||
);
|
);
|
||||||
const [isWaiting, setIsWaiting] = useState(hasEnded && !isNextEventThisMonth);
|
const [isWaiting, setIsWaiting] = useState(hasEnded && !isNextEventThisMonth);
|
||||||
|
|
||||||
const dayTime = startOfDay(calendarEvent.startsAt).getTime();
|
const dayTime = startOfDay(calendarEventStartsAt).getTime();
|
||||||
const dayEvents = calendarEventsByDayTime[dayTime];
|
const dayEvents = calendarEventsByDayTime[dayTime];
|
||||||
const isFirstEventOfDay = dayEvents?.slice(-1)[0] === calendarEvent;
|
const isFirstEventOfDay = dayEvents?.slice(-1)[0] === calendarEvent;
|
||||||
const isLastEventOfDay = dayEvents?.[0] === calendarEvent;
|
const isLastEventOfDay = dayEvents?.[0] === calendarEvent;
|
||||||
@ -81,7 +86,7 @@ export const CalendarCurrentEventCursor = ({
|
|||||||
transition: {
|
transition: {
|
||||||
delay: Math.max(
|
delay: Math.max(
|
||||||
0,
|
0,
|
||||||
differenceInSeconds(calendarEvent.startsAt, new Date()),
|
differenceInSeconds(calendarEventStartsAt, new Date()),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -99,9 +104,9 @@ export const CalendarCurrentEventCursor = ({
|
|||||||
top: `-${topOffset}px`,
|
top: `-${topOffset}px`,
|
||||||
transition: {
|
transition: {
|
||||||
delay:
|
delay:
|
||||||
isWaiting && nextCalendarEvent
|
isWaiting && nextCalendarEventStartsAt
|
||||||
? differenceInSeconds(
|
? differenceInSeconds(
|
||||||
startOfMonth(nextCalendarEvent.startsAt),
|
startOfMonth(nextCalendarEventStartsAt),
|
||||||
new Date(),
|
new Date(),
|
||||||
)
|
)
|
||||||
: 0,
|
: 0,
|
||||||
|
@ -4,6 +4,7 @@ import { differenceInSeconds, endOfDay, format } from 'date-fns';
|
|||||||
|
|
||||||
import { CalendarEventRow } from '@/activities/calendar/components/CalendarEventRow';
|
import { CalendarEventRow } from '@/activities/calendar/components/CalendarEventRow';
|
||||||
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||||
|
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
|
||||||
import { CardContent } from '@/ui/layout/card/components/CardContent';
|
import { CardContent } from '@/ui/layout/card/components/CardContent';
|
||||||
|
|
||||||
type CalendarDayCardContentProps = {
|
type CalendarDayCardContentProps = {
|
||||||
@ -53,8 +54,8 @@ export const CalendarDayCardContent = ({
|
|||||||
}: CalendarDayCardContentProps) => {
|
}: CalendarDayCardContentProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const endOfDayDate = endOfDay(calendarEvents[0].startsAt);
|
const endOfDayDate = endOfDay(getCalendarEventStartDate(calendarEvents[0]));
|
||||||
const endsIn = differenceInSeconds(endOfDayDate, Date.now());
|
const dayEndsIn = differenceInSeconds(endOfDayDate, Date.now());
|
||||||
|
|
||||||
const weekDayLabel = format(endOfDayDate, 'EE');
|
const weekDayLabel = format(endOfDayDate, 'EE');
|
||||||
const monthDayLabel = format(endOfDayDate, 'dd');
|
const monthDayLabel = format(endOfDayDate, 'dd');
|
||||||
@ -71,7 +72,7 @@ export const CalendarDayCardContent = ({
|
|||||||
animate="ended"
|
animate="ended"
|
||||||
variants={upcomingDayCardContentVariants}
|
variants={upcomingDayCardContentVariants}
|
||||||
transition={{
|
transition={{
|
||||||
delay: Math.max(0, endsIn),
|
delay: Math.max(0, dayEndsIn),
|
||||||
duration: theme.animation.duration.fast,
|
duration: theme.animation.duration.fast,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -0,0 +1,146 @@
|
|||||||
|
import { css, useTheme } from '@emotion/react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||||
|
import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
|
||||||
|
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||||
|
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
|
||||||
|
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||||
|
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
|
||||||
|
import { PropertyBox } from '@/object-record/record-inline-cell/property-box/components/PropertyBox';
|
||||||
|
import {
|
||||||
|
Chip,
|
||||||
|
ChipAccent,
|
||||||
|
ChipSize,
|
||||||
|
ChipVariant,
|
||||||
|
} from '@/ui/display/chip/components/Chip';
|
||||||
|
import { IconCalendarEvent } from '@/ui/display/icon';
|
||||||
|
import { mapArrayToObject } from '~/utils/array/mapArrayToObject';
|
||||||
|
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
|
||||||
|
|
||||||
|
type CalendarEventDetailsProps = {
|
||||||
|
calendarEvent: CalendarEvent;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledContainer = styled.div`
|
||||||
|
background: ${({ theme }) => theme.background.secondary};
|
||||||
|
align-items: flex-start;
|
||||||
|
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: ${({ theme }) => theme.spacing(6)};
|
||||||
|
padding: ${({ theme }) => theme.spacing(6)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledEventChip = styled(Chip)`
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||||
|
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledHeader = styled.header``;
|
||||||
|
|
||||||
|
const StyledTitle = styled.h2<{ canceled?: boolean }>`
|
||||||
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
|
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||||
|
margin: ${({ theme }) => theme.spacing(0, 0, 2)};
|
||||||
|
|
||||||
|
${({ canceled }) =>
|
||||||
|
canceled &&
|
||||||
|
css`
|
||||||
|
text-decoration: line-through;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledCreatedAt = styled.div`
|
||||||
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledFields = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: ${({ theme }) => theme.spacing(3)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledPropertyBox = styled(PropertyBox)`
|
||||||
|
height: ${({ theme }) => theme.spacing(6)};
|
||||||
|
padding: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CalendarEventDetails = ({
|
||||||
|
calendarEvent,
|
||||||
|
}: CalendarEventDetailsProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const { objectMetadataItem } = useObjectMetadataItemOnly({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.CalendarEvent,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fieldsToDisplay: Partial<
|
||||||
|
Record<
|
||||||
|
keyof CalendarEvent,
|
||||||
|
Partial<Pick<FieldDefinition<FieldMetadata>, 'label'>>
|
||||||
|
>
|
||||||
|
> = {
|
||||||
|
startsAt: { label: 'Start Date' },
|
||||||
|
endsAt: { label: 'End Date' },
|
||||||
|
conferenceUri: { label: 'Meet link' },
|
||||||
|
location: {},
|
||||||
|
description: {},
|
||||||
|
};
|
||||||
|
const fieldsByName = mapArrayToObject(
|
||||||
|
objectMetadataItem.fields,
|
||||||
|
({ name }) => name,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledContainer>
|
||||||
|
<StyledEventChip
|
||||||
|
accent={ChipAccent.TextSecondary}
|
||||||
|
size={ChipSize.Large}
|
||||||
|
variant={ChipVariant.Highlighted}
|
||||||
|
clickable={false}
|
||||||
|
leftComponent={<IconCalendarEvent size={theme.icon.size.md} />}
|
||||||
|
label="Event"
|
||||||
|
/>
|
||||||
|
<StyledHeader>
|
||||||
|
<StyledTitle canceled={calendarEvent.isCanceled}>
|
||||||
|
{calendarEvent.title}
|
||||||
|
</StyledTitle>
|
||||||
|
<StyledCreatedAt>
|
||||||
|
Created{' '}
|
||||||
|
{beautifyPastDateRelativeToNow(
|
||||||
|
new Date(calendarEvent.externalCreatedAt),
|
||||||
|
)}
|
||||||
|
</StyledCreatedAt>
|
||||||
|
</StyledHeader>
|
||||||
|
<StyledFields>
|
||||||
|
{Object.entries(fieldsToDisplay).map(([fieldName, fieldOverride]) => (
|
||||||
|
<StyledPropertyBox key={fieldName}>
|
||||||
|
<FieldContext.Provider
|
||||||
|
value={{
|
||||||
|
entityId: calendarEvent.id,
|
||||||
|
hotkeyScope: 'calendar-event-details',
|
||||||
|
recoilScopeId: `${calendarEvent.id}-${fieldName}`,
|
||||||
|
isLabelIdentifier: false,
|
||||||
|
fieldDefinition: formatFieldMetadataItemAsFieldDefinition({
|
||||||
|
field: {
|
||||||
|
...fieldsByName[fieldName],
|
||||||
|
...fieldOverride,
|
||||||
|
},
|
||||||
|
objectMetadataItem,
|
||||||
|
showLabel: true,
|
||||||
|
labelWidth: 72,
|
||||||
|
}),
|
||||||
|
useUpdateRecord: () => [() => undefined, { loading: false }],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RecordInlineCell readonly />
|
||||||
|
</FieldContext.Provider>
|
||||||
|
</StyledPropertyBox>
|
||||||
|
))}
|
||||||
|
</StyledFields>
|
||||||
|
</StyledContainer>
|
||||||
|
);
|
||||||
|
};
|
@ -6,8 +6,10 @@ import { useRecoilValue } from 'recoil';
|
|||||||
|
|
||||||
import { CalendarCurrentEventCursor } from '@/activities/calendar/components/CalendarCurrentEventCursor';
|
import { CalendarCurrentEventCursor } from '@/activities/calendar/components/CalendarCurrentEventCursor';
|
||||||
import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext';
|
import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext';
|
||||||
|
import { useOpenCalendarEventRightDrawer } from '@/activities/calendar/right-drawer/hooks/useOpenCalendarEventRightDrawer';
|
||||||
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||||
import { getCalendarEventEndDate } from '@/activities/calendar/utils/getCalendarEventEndDate';
|
import { getCalendarEventEndDate } from '@/activities/calendar/utils/getCalendarEventEndDate';
|
||||||
|
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
|
||||||
import { hasCalendarEventEnded } from '@/activities/calendar/utils/hasCalendarEventEnded';
|
import { hasCalendarEventEnded } from '@/activities/calendar/utils/hasCalendarEventEnded';
|
||||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||||
import { IconArrowRight, IconLock } from '@/ui/display/icon';
|
import { IconArrowRight, IconLock } from '@/ui/display/icon';
|
||||||
@ -22,12 +24,13 @@ type CalendarEventRowProps = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div<{ showTitle?: boolean }>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
gap: ${({ theme }) => theme.spacing(3)};
|
gap: ${({ theme }) => theme.spacing(3)};
|
||||||
height: ${({ theme }) => theme.spacing(6)};
|
height: ${({ theme }) => theme.spacing(6)};
|
||||||
position: relative;
|
position: relative;
|
||||||
|
cursor: ${({ showTitle }) => (showTitle ? 'pointer' : 'not-allowed')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledAttendanceIndicator = styled.div<{ active?: boolean }>`
|
const StyledAttendanceIndicator = styled.div<{ active?: boolean }>`
|
||||||
@ -101,21 +104,32 @@ export const CalendarEventRow = ({
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState());
|
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState());
|
||||||
const { displayCurrentEventCursor = false } = useContext(CalendarContext);
|
const { displayCurrentEventCursor = false } = useContext(CalendarContext);
|
||||||
|
const { openCalendarEventRightDrawer } = useOpenCalendarEventRightDrawer();
|
||||||
|
|
||||||
|
const startsAt = getCalendarEventStartDate(calendarEvent);
|
||||||
const endsAt = getCalendarEventEndDate(calendarEvent);
|
const endsAt = getCalendarEventEndDate(calendarEvent);
|
||||||
const hasEnded = hasCalendarEventEnded(calendarEvent);
|
const hasEnded = hasCalendarEventEnded(calendarEvent);
|
||||||
|
|
||||||
const startTimeLabel = calendarEvent.isFullDay
|
const startTimeLabel = calendarEvent.isFullDay
|
||||||
? 'All day'
|
? 'All day'
|
||||||
: format(calendarEvent.startsAt, 'HH:mm');
|
: format(startsAt, 'HH:mm');
|
||||||
const endTimeLabel = calendarEvent.isFullDay ? '' : format(endsAt, 'HH:mm');
|
const endTimeLabel = calendarEvent.isFullDay ? '' : format(endsAt, 'HH:mm');
|
||||||
|
|
||||||
const isCurrentWorkspaceMemberAttending = !!calendarEvent.attendees?.find(
|
const isCurrentWorkspaceMemberAttending = calendarEvent.attendees?.some(
|
||||||
({ workspaceMemberId }) => workspaceMemberId === currentWorkspaceMember?.id,
|
({ workspaceMemberId }) => workspaceMemberId === currentWorkspaceMember?.id,
|
||||||
);
|
);
|
||||||
|
const showTitle = calendarEvent.visibility === 'SHARE_EVERYTHING';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer className={className}>
|
<StyledContainer
|
||||||
|
className={className}
|
||||||
|
showTitle={showTitle}
|
||||||
|
onClick={
|
||||||
|
showTitle
|
||||||
|
? () => openCalendarEventRightDrawer(calendarEvent.id)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
<StyledAttendanceIndicator active={isCurrentWorkspaceMemberAttending} />
|
<StyledAttendanceIndicator active={isCurrentWorkspaceMemberAttending} />
|
||||||
<StyledLabels>
|
<StyledLabels>
|
||||||
<StyledTime>
|
<StyledTime>
|
||||||
@ -127,17 +141,17 @@ export const CalendarEventRow = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</StyledTime>
|
</StyledTime>
|
||||||
{calendarEvent.visibility === 'METADATA' ? (
|
{showTitle ? (
|
||||||
|
<StyledTitle active={!hasEnded} canceled={!!calendarEvent.isCanceled}>
|
||||||
|
{calendarEvent.title}
|
||||||
|
</StyledTitle>
|
||||||
|
) : (
|
||||||
<StyledVisibilityCard active={!hasEnded}>
|
<StyledVisibilityCard active={!hasEnded}>
|
||||||
<StyledVisibilityCardContent>
|
<StyledVisibilityCardContent>
|
||||||
<IconLock size={theme.icon.size.sm} />
|
<IconLock size={theme.icon.size.sm} />
|
||||||
Not shared
|
Not shared
|
||||||
</StyledVisibilityCardContent>
|
</StyledVisibilityCardContent>
|
||||||
</StyledVisibilityCard>
|
</StyledVisibilityCard>
|
||||||
) : (
|
|
||||||
<StyledTitle active={!hasEnded} canceled={!!calendarEvent.isCanceled}>
|
|
||||||
{calendarEvent.title}
|
|
||||||
</StyledTitle>
|
|
||||||
)}
|
)}
|
||||||
</StyledLabels>
|
</StyledLabels>
|
||||||
{!!calendarEvent.attendees?.length && (
|
{!!calendarEvent.attendees?.length && (
|
||||||
|
@ -4,7 +4,7 @@ import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
|||||||
|
|
||||||
type CalendarContextValue = {
|
type CalendarContextValue = {
|
||||||
calendarEventsByDayTime: Record<number, CalendarEvent[] | undefined>;
|
calendarEventsByDayTime: Record<number, CalendarEvent[] | undefined>;
|
||||||
currentCalendarEvent: CalendarEvent;
|
currentCalendarEvent?: CalendarEvent;
|
||||||
displayCurrentEventCursor?: boolean;
|
displayCurrentEventCursor?: boolean;
|
||||||
getNextCalendarEvent: (
|
getNextCalendarEvent: (
|
||||||
calendarEvent: CalendarEvent,
|
calendarEvent: CalendarEvent,
|
||||||
@ -14,7 +14,6 @@ type CalendarContextValue = {
|
|||||||
|
|
||||||
export const CalendarContext = createContext<CalendarContextValue>({
|
export const CalendarContext = createContext<CalendarContextValue>({
|
||||||
calendarEventsByDayTime: {},
|
calendarEventsByDayTime: {},
|
||||||
currentCalendarEvent: {} as CalendarEvent,
|
|
||||||
getNextCalendarEvent: () => undefined,
|
getNextCalendarEvent: () => undefined,
|
||||||
updateCurrentCalendarEvent: () => {},
|
updateCurrentCalendarEvent: () => {},
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,7 @@ import { getYear, isThisMonth, startOfDay, startOfMonth } from 'date-fns';
|
|||||||
|
|
||||||
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||||
import { findUpcomingCalendarEvent } from '@/activities/calendar/utils/findUpcomingCalendarEvent';
|
import { findUpcomingCalendarEvent } from '@/activities/calendar/utils/findUpcomingCalendarEvent';
|
||||||
|
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
|
||||||
import { groupArrayItemsBy } from '~/utils/array/groupArrayItemsBy';
|
import { groupArrayItemsBy } from '~/utils/array/groupArrayItemsBy';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { sortDesc } from '~/utils/sort';
|
import { sortDesc } from '~/utils/sort';
|
||||||
@ -10,7 +11,8 @@ import { sortDesc } from '~/utils/sort';
|
|||||||
export const useCalendarEvents = (calendarEvents: CalendarEvent[]) => {
|
export const useCalendarEvents = (calendarEvents: CalendarEvent[]) => {
|
||||||
const calendarEventsByDayTime = groupArrayItemsBy(
|
const calendarEventsByDayTime = groupArrayItemsBy(
|
||||||
calendarEvents,
|
calendarEvents,
|
||||||
({ startsAt }) => startOfDay(startsAt).getTime(),
|
(calendarEvent) =>
|
||||||
|
startOfDay(getCalendarEventStartDate(calendarEvent)).getTime(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const sortedDayTimes = Object.keys(calendarEventsByDayTime)
|
const sortedDayTimes = Object.keys(calendarEventsByDayTime)
|
||||||
@ -45,17 +47,21 @@ export const useCalendarEvents = (calendarEvents: CalendarEvent[]) => {
|
|||||||
() => findUpcomingCalendarEvent(calendarEvents),
|
() => findUpcomingCalendarEvent(calendarEvents),
|
||||||
[calendarEvents],
|
[calendarEvents],
|
||||||
);
|
);
|
||||||
const lastEventInCalendar = calendarEvents[0];
|
const lastEventInCalendar = calendarEvents.length
|
||||||
|
? calendarEvents[0]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const [currentCalendarEvent, setCurrentCalendarEvent] = useState(
|
const [currentCalendarEvent, setCurrentCalendarEvent] = useState(
|
||||||
(initialUpcomingCalendarEvent &&
|
(initialUpcomingCalendarEvent &&
|
||||||
(isThisMonth(initialUpcomingCalendarEvent.startsAt)
|
(isThisMonth(getCalendarEventStartDate(initialUpcomingCalendarEvent))
|
||||||
? initialUpcomingCalendarEvent
|
? initialUpcomingCalendarEvent
|
||||||
: getPreviousCalendarEvent(initialUpcomingCalendarEvent))) ||
|
: getPreviousCalendarEvent(initialUpcomingCalendarEvent))) ||
|
||||||
lastEventInCalendar,
|
lastEventInCalendar,
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateCurrentCalendarEvent = () => {
|
const updateCurrentCalendarEvent = () => {
|
||||||
|
if (!currentCalendarEvent) return;
|
||||||
|
|
||||||
const nextCurrentCalendarEvent = getNextCalendarEvent(currentCalendarEvent);
|
const nextCurrentCalendarEvent = getNextCalendarEvent(currentCalendarEvent);
|
||||||
|
|
||||||
if (isDefined(nextCurrentCalendarEvent)) {
|
if (isDefined(nextCurrentCalendarEvent)) {
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { CalendarEventDetails } from '@/activities/calendar/components/CalendarEventDetails';
|
||||||
|
import { viewableCalendarEventIdState } from '@/activities/calendar/states/viewableCalendarEventIdState';
|
||||||
|
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||||
|
import { useSetRecordInStore } from '@/object-record/record-store/hooks/useSetRecordInStore';
|
||||||
|
|
||||||
|
export const RightDrawerCalendarEvent = () => {
|
||||||
|
const { setRecords } = useSetRecordInStore();
|
||||||
|
const calendarEventId = useRecoilValue(viewableCalendarEventIdState());
|
||||||
|
const { record: calendarEvent } = useFindOneRecord<CalendarEvent>({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.CalendarEvent,
|
||||||
|
objectRecordId: calendarEventId ?? '',
|
||||||
|
onCompleted: (record) => setRecords([record]),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!calendarEvent) return null;
|
||||||
|
|
||||||
|
return <CalendarEventDetails calendarEvent={calendarEvent} />;
|
||||||
|
};
|
@ -0,0 +1,23 @@
|
|||||||
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { viewableCalendarEventIdState } from '@/activities/calendar/states/viewableCalendarEventIdState';
|
||||||
|
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||||
|
import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope';
|
||||||
|
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
||||||
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
|
|
||||||
|
export const useOpenCalendarEventRightDrawer = () => {
|
||||||
|
const { openRightDrawer } = useRightDrawer();
|
||||||
|
const setHotkeyScope = useSetHotkeyScope();
|
||||||
|
const setViewableCalendarEventId = useSetRecoilState(
|
||||||
|
viewableCalendarEventIdState(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const openCalendarEventRightDrawer = (calendarEventId: string) => {
|
||||||
|
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||||
|
openRightDrawer(RightDrawerPages.ViewCalendarEvent);
|
||||||
|
setViewableCalendarEventId(calendarEventId);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { openCalendarEventRightDrawer };
|
||||||
|
};
|
@ -0,0 +1,6 @@
|
|||||||
|
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||||
|
|
||||||
|
export const viewableCalendarEventIdState = createState<string | null>({
|
||||||
|
key: 'viewableCalendarEventIdState',
|
||||||
|
defaultValue: null,
|
||||||
|
});
|
@ -1,10 +1,14 @@
|
|||||||
// TODO: use backend CalendarEvent type when ready
|
// TODO: use backend CalendarEvent type when ready
|
||||||
export type CalendarEvent = {
|
export type CalendarEvent = {
|
||||||
endsAt?: Date;
|
conferenceUri?: string;
|
||||||
|
description?: string;
|
||||||
|
endsAt?: string;
|
||||||
|
externalCreatedAt: string;
|
||||||
id: string;
|
id: string;
|
||||||
isFullDay: boolean;
|
|
||||||
startsAt: Date;
|
|
||||||
isCanceled?: boolean;
|
isCanceled?: boolean;
|
||||||
|
isFullDay: boolean;
|
||||||
|
location?: string;
|
||||||
|
startsAt: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
visibility: 'METADATA' | 'SHARE_EVERYTHING';
|
visibility: 'METADATA' | 'SHARE_EVERYTHING';
|
||||||
attendees?: {
|
attendees?: {
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import { endOfDay } from 'date-fns';
|
import { endOfDay } from 'date-fns';
|
||||||
|
|
||||||
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||||
|
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
|
||||||
|
|
||||||
export const getCalendarEventEndDate = (
|
export const getCalendarEventEndDate = (
|
||||||
calendarEvent: Pick<CalendarEvent, 'endsAt' | 'isFullDay' | 'startsAt'>,
|
calendarEvent: Pick<CalendarEvent, 'endsAt' | 'isFullDay' | 'startsAt'>,
|
||||||
) => calendarEvent.endsAt ?? endOfDay(calendarEvent.startsAt);
|
) =>
|
||||||
|
calendarEvent.endsAt
|
||||||
|
? new Date(calendarEvent.endsAt)
|
||||||
|
: endOfDay(getCalendarEventStartDate(calendarEvent));
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||||
|
|
||||||
|
export const getCalendarEventStartDate = (
|
||||||
|
calendarEvent: Pick<CalendarEvent, 'startsAt'>,
|
||||||
|
) => new Date(calendarEvent.startsAt);
|
@ -1,7 +1,8 @@
|
|||||||
import { isPast } from 'date-fns';
|
import { isPast } from 'date-fns';
|
||||||
|
|
||||||
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||||
|
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
|
||||||
|
|
||||||
export const hasCalendarEventStarted = (
|
export const hasCalendarEventStarted = (
|
||||||
calendarEvent: Pick<CalendarEvent, 'startsAt'>,
|
calendarEvent: Pick<CalendarEvent, 'startsAt'>,
|
||||||
) => isPast(calendarEvent.startsAt);
|
) => isPast(getCalendarEventStartDate(calendarEvent));
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||||
import { getCalendarEventEndDate } from '@/activities/calendar/utils/getCalendarEventEndDate';
|
import { getCalendarEventEndDate } from '@/activities/calendar/utils/getCalendarEventEndDate';
|
||||||
|
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
|
||||||
import { sortAsc } from '~/utils/sort';
|
import { sortAsc } from '~/utils/sort';
|
||||||
|
|
||||||
export const sortCalendarEventsAsc = (
|
export const sortCalendarEventsAsc = (
|
||||||
@ -7,18 +8,16 @@ export const sortCalendarEventsAsc = (
|
|||||||
calendarEventB: Pick<CalendarEvent, 'startsAt' | 'endsAt' | 'isFullDay'>,
|
calendarEventB: Pick<CalendarEvent, 'startsAt' | 'endsAt' | 'isFullDay'>,
|
||||||
) => {
|
) => {
|
||||||
const startsAtSort = sortAsc(
|
const startsAtSort = sortAsc(
|
||||||
calendarEventA.startsAt.getTime(),
|
getCalendarEventStartDate(calendarEventA).getTime(),
|
||||||
calendarEventB.startsAt.getTime(),
|
getCalendarEventStartDate(calendarEventB).getTime(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (startsAtSort === 0) {
|
if (startsAtSort !== 0) return startsAtSort;
|
||||||
const endsAtA = getCalendarEventEndDate(calendarEventA);
|
|
||||||
const endsAtB = getCalendarEventEndDate(calendarEventB);
|
|
||||||
|
|
||||||
return sortAsc(endsAtA.getTime(), endsAtB.getTime());
|
return sortAsc(
|
||||||
}
|
getCalendarEventEndDate(calendarEventA).getTime(),
|
||||||
|
getCalendarEventEndDate(calendarEventB).getTime(),
|
||||||
return startsAtSort;
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sortCalendarEventsDesc = (
|
export const sortCalendarEventsDesc = (
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
import { StyledRightDrawerTopBar } from '@/ui/layout/right-drawer/components/StyledRightDrawerTopBar';
|
|
||||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
|
||||||
|
|
||||||
import { RightDrawerTopBarCloseButton } from '../../../../ui/layout/right-drawer/components/RightDrawerTopBarCloseButton';
|
|
||||||
import { RightDrawerTopBarExpandButton } from '../../../../ui/layout/right-drawer/components/RightDrawerTopBarExpandButton';
|
|
||||||
|
|
||||||
const StyledTopBarWrapper = styled.div`
|
|
||||||
display: flex;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const RightDrawerEmailThreadTopBar = () => {
|
|
||||||
const isMobile = useIsMobile();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledRightDrawerTopBar>
|
|
||||||
<StyledTopBarWrapper>
|
|
||||||
<RightDrawerTopBarCloseButton />
|
|
||||||
{!isMobile && <RightDrawerTopBarExpandButton />}
|
|
||||||
</StyledTopBarWrapper>
|
|
||||||
</StyledRightDrawerTopBar>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,26 +0,0 @@
|
|||||||
import { Meta, StoryObj } from '@storybook/react';
|
|
||||||
|
|
||||||
import { RightDrawerEmailThreadTopBar } from '@/activities/emails/right-drawer/components/RightDrawerEmailThreadTopBar';
|
|
||||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
|
||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
|
||||||
|
|
||||||
const meta: Meta<typeof RightDrawerEmailThreadTopBar> = {
|
|
||||||
title: 'Modules/Activities/Emails/RightDrawer/RightDrawerEmailThreadTopBar',
|
|
||||||
component: RightDrawerEmailThreadTopBar,
|
|
||||||
decorators: [
|
|
||||||
(Story) => (
|
|
||||||
<div style={{ width: '500px' }}>
|
|
||||||
<Story />
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
ComponentDecorator,
|
|
||||||
],
|
|
||||||
parameters: {
|
|
||||||
msw: graphqlMocks,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default meta;
|
|
||||||
type Story = StoryObj<typeof RightDrawerEmailThreadTopBar>;
|
|
||||||
|
|
||||||
export const Default: Story = {};
|
|
@ -1,17 +1,20 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { ActivityActionBar } from '@/activities/right-drawer/components/ActivityActionBar';
|
import { ActivityActionBar } from '@/activities/right-drawer/components/ActivityActionBar';
|
||||||
|
import { RightDrawerTopBarCloseButton } from '@/ui/layout/right-drawer/components/RightDrawerTopBarCloseButton';
|
||||||
|
import { RightDrawerTopBarExpandButton } from '@/ui/layout/right-drawer/components/RightDrawerTopBarExpandButton';
|
||||||
import { StyledRightDrawerTopBar } from '@/ui/layout/right-drawer/components/StyledRightDrawerTopBar';
|
import { StyledRightDrawerTopBar } from '@/ui/layout/right-drawer/components/StyledRightDrawerTopBar';
|
||||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||||
|
|
||||||
import { RightDrawerTopBarCloseButton } from '../../../ui/layout/right-drawer/components/RightDrawerTopBarCloseButton';
|
type RightDrawerActivityTopBarProps = { showActionBar?: boolean };
|
||||||
import { RightDrawerTopBarExpandButton } from '../../../ui/layout/right-drawer/components/RightDrawerTopBarExpandButton';
|
|
||||||
|
|
||||||
const StyledTopBarWrapper = styled.div`
|
const StyledTopBarWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const RightDrawerActivityTopBar = () => {
|
export const RightDrawerActivityTopBar = ({
|
||||||
|
showActionBar = true,
|
||||||
|
}: RightDrawerActivityTopBarProps) => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -20,7 +23,7 @@ export const RightDrawerActivityTopBar = () => {
|
|||||||
<RightDrawerTopBarCloseButton />
|
<RightDrawerTopBarCloseButton />
|
||||||
{!isMobile && <RightDrawerTopBarExpandButton />}
|
{!isMobile && <RightDrawerTopBarExpandButton />}
|
||||||
</StyledTopBarWrapper>
|
</StyledTopBarWrapper>
|
||||||
<ActivityActionBar />
|
{showActionBar && <ActivityActionBar />}
|
||||||
</StyledRightDrawerTopBar>
|
</StyledRightDrawerTopBar>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@ export enum CoreObjectNameSingular {
|
|||||||
ApiKey = 'apiKey',
|
ApiKey = 'apiKey',
|
||||||
Attachment = 'attachment',
|
Attachment = 'attachment',
|
||||||
Blocklist = 'blocklist',
|
Blocklist = 'blocklist',
|
||||||
|
CalendarEvent = 'calendarEvent',
|
||||||
Comment = 'comment',
|
Comment = 'comment',
|
||||||
Company = 'company',
|
Company = 'company',
|
||||||
ConnectedAccount = 'connectedAccount',
|
ConnectedAccount = 'connectedAccount',
|
||||||
|
@ -7,9 +7,5 @@ import { isFieldRating } from '../types/guards/isFieldRating';
|
|||||||
export const useIsFieldInputOnly = () => {
|
export const useIsFieldInputOnly = () => {
|
||||||
const { fieldDefinition } = useContext(FieldContext);
|
const { fieldDefinition } = useContext(FieldContext);
|
||||||
|
|
||||||
if (isFieldBoolean(fieldDefinition) || isFieldRating(fieldDefinition)) {
|
return isFieldBoolean(fieldDefinition) || isFieldRating(fieldDefinition);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,11 @@ import { useInlineCell } from '../hooks/useInlineCell';
|
|||||||
|
|
||||||
import { RecordInlineCellContainer } from './RecordInlineCellContainer';
|
import { RecordInlineCellContainer } from './RecordInlineCellContainer';
|
||||||
|
|
||||||
export const RecordInlineCell = () => {
|
type RecordInlineCellProps = {
|
||||||
|
readonly?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RecordInlineCell = ({ readonly }: RecordInlineCellProps) => {
|
||||||
const { fieldDefinition, entityId } = useContext(FieldContext);
|
const { fieldDefinition, entityId } = useContext(FieldContext);
|
||||||
|
|
||||||
const buttonIcon = useGetButtonIcon();
|
const buttonIcon = useGetButtonIcon();
|
||||||
@ -63,6 +67,7 @@ export const RecordInlineCell = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<RecordInlineCellContainer
|
<RecordInlineCellContainer
|
||||||
|
readonly={readonly}
|
||||||
buttonIcon={buttonIcon}
|
buttonIcon={buttonIcon}
|
||||||
customEditHotkeyScope={
|
customEditHotkeyScope={
|
||||||
isFieldRelation(fieldDefinition)
|
isFieldRelation(fieldDefinition)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useContext, useState } from 'react';
|
import { useContext, useState } from 'react';
|
||||||
import { Tooltip } from 'react-tooltip';
|
import { Tooltip } from 'react-tooltip';
|
||||||
import { useTheme } from '@emotion/react';
|
import { css, useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
@ -52,11 +52,16 @@ const StyledEditButtonContainer = styled(motion.div)`
|
|||||||
display: flex;
|
display: flex;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledClickableContainer = styled.div`
|
const StyledClickableContainer = styled.div<{ readonly?: boolean }>`
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: ${({ theme }) => theme.spacing(1)};
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
${({ readonly }) =>
|
||||||
|
!readonly &&
|
||||||
|
css`
|
||||||
|
cursor: pointer;
|
||||||
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledInlineCellBaseContainer = styled.div`
|
const StyledInlineCellBaseContainer = styled.div`
|
||||||
@ -83,6 +88,7 @@ const StyledTooltip = styled(Tooltip)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
type RecordInlineCellContainerProps = {
|
type RecordInlineCellContainerProps = {
|
||||||
|
readonly?: boolean;
|
||||||
IconLabel?: IconComponent;
|
IconLabel?: IconComponent;
|
||||||
label?: string;
|
label?: string;
|
||||||
labelWidth?: number;
|
labelWidth?: number;
|
||||||
@ -98,6 +104,7 @@ type RecordInlineCellContainerProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const RecordInlineCellContainer = ({
|
export const RecordInlineCellContainer = ({
|
||||||
|
readonly,
|
||||||
IconLabel,
|
IconLabel,
|
||||||
label,
|
label,
|
||||||
labelWidth,
|
labelWidth,
|
||||||
@ -115,17 +122,21 @@ export const RecordInlineCellContainer = ({
|
|||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
|
||||||
const handleContainerMouseEnter = () => {
|
const handleContainerMouseEnter = () => {
|
||||||
|
if (!readonly) {
|
||||||
setIsHovered(true);
|
setIsHovered(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleContainerMouseLeave = () => {
|
const handleContainerMouseLeave = () => {
|
||||||
|
if (!readonly) {
|
||||||
setIsHovered(false);
|
setIsHovered(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const { isInlineCellInEditMode, openInlineCell } = useInlineCell();
|
const { isInlineCellInEditMode, openInlineCell } = useInlineCell();
|
||||||
|
|
||||||
const handleDisplayModeClick = () => {
|
const handleDisplayModeClick = () => {
|
||||||
if (!editModeContentOnly) {
|
if (!readonly && !editModeContentOnly) {
|
||||||
openInlineCell(customEditHotkeyScope);
|
openInlineCell(customEditHotkeyScope);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -167,10 +178,10 @@ export const RecordInlineCellContainer = ({
|
|||||||
</StyledLabelAndIconContainer>
|
</StyledLabelAndIconContainer>
|
||||||
)}
|
)}
|
||||||
<StyledValueContainer>
|
<StyledValueContainer>
|
||||||
{isInlineCellInEditMode ? (
|
{!readonly && isInlineCellInEditMode ? (
|
||||||
<RecordInlineCellEditMode>{editModeContent}</RecordInlineCellEditMode>
|
<RecordInlineCellEditMode>{editModeContent}</RecordInlineCellEditMode>
|
||||||
) : editModeContentOnly ? (
|
) : editModeContentOnly ? (
|
||||||
<StyledClickableContainer>
|
<StyledClickableContainer readonly={readonly}>
|
||||||
<RecordInlineCellDisplayMode
|
<RecordInlineCellDisplayMode
|
||||||
disableHoverEffect={disableHoverEffect}
|
disableHoverEffect={disableHoverEffect}
|
||||||
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
|
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
|
||||||
@ -182,7 +193,10 @@ export const RecordInlineCellContainer = ({
|
|||||||
</RecordInlineCellDisplayMode>
|
</RecordInlineCellDisplayMode>
|
||||||
</StyledClickableContainer>
|
</StyledClickableContainer>
|
||||||
) : (
|
) : (
|
||||||
<StyledClickableContainer onClick={handleDisplayModeClick}>
|
<StyledClickableContainer
|
||||||
|
readonly={readonly}
|
||||||
|
onClick={handleDisplayModeClick}
|
||||||
|
>
|
||||||
<RecordInlineCellDisplayMode
|
<RecordInlineCellDisplayMode
|
||||||
disableHoverEffect={disableHoverEffect}
|
disableHoverEffect={disableHoverEffect}
|
||||||
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
|
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
|
||||||
|
@ -82,7 +82,7 @@ export const RecordTableWithWrappers = ({
|
|||||||
|
|
||||||
const { deleteOneRecord } = useDeleteOneRecord({ objectNameSingular });
|
const { deleteOneRecord } = useDeleteOneRecord({ objectNameSingular });
|
||||||
|
|
||||||
const objectLabel = foundObjectMetadataItem?.nameSingular;
|
const objectLabel = foundObjectMetadataItem?.labelSingular;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityDeleteContext.Provider value={deleteOneRecord}>
|
<EntityDeleteContext.Provider value={deleteOneRecord}>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { MouseEvent, ReactNode } from 'react';
|
import { MouseEvent, ReactNode } from 'react';
|
||||||
|
import { css } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { OverflowingTextWithTooltip } from '../../tooltip/OverflowingTextWithTooltip';
|
import { OverflowingTextWithTooltip } from '../../tooltip/OverflowingTextWithTooltip';
|
||||||
@ -34,86 +35,108 @@ type ChipProps = {
|
|||||||
onClick?: (event: MouseEvent<HTMLDivElement>) => void;
|
onClick?: (event: MouseEvent<HTMLDivElement>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledContainer = styled.div<Partial<ChipProps>>`
|
const StyledContainer = styled.div<
|
||||||
|
Pick<
|
||||||
|
ChipProps,
|
||||||
|
'accent' | 'clickable' | 'disabled' | 'maxWidth' | 'size' | 'variant'
|
||||||
|
>
|
||||||
|
>`
|
||||||
|
--chip-horizontal-padding: ${({ theme }) => theme.spacing(1)};
|
||||||
|
--chip-vertical-padding: ${({ theme }) => theme.spacing(1)};
|
||||||
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
background-color: ${({ theme, variant }) =>
|
color: ${({ theme, disabled }) =>
|
||||||
variant === ChipVariant.Highlighted
|
disabled ? theme.font.color.light : theme.font.color.secondary};
|
||||||
? theme.background.transparent.light
|
cursor: ${({ clickable, disabled }) =>
|
||||||
: variant === ChipVariant.Rounded
|
clickable ? 'pointer' : disabled ? 'not-allowed' : 'inherit'};
|
||||||
? theme.background.transparent.lighter
|
|
||||||
: 'transparent'};
|
|
||||||
border-color: ${({ theme, variant }) =>
|
|
||||||
variant === ChipVariant.Rounded ? theme.border.color.medium : 'none'};
|
|
||||||
border-radius: ${({ theme, variant }) =>
|
|
||||||
variant === ChipVariant.Rounded ? '50px' : theme.border.radius.sm};
|
|
||||||
border-style: ${({ variant }) =>
|
|
||||||
variant === ChipVariant.Rounded ? 'solid' : 'none'};
|
|
||||||
border-width: ${({ variant }) =>
|
|
||||||
variant === ChipVariant.Rounded ? '1px' : '0px'};
|
|
||||||
|
|
||||||
color: ${({ theme, disabled, accent }) =>
|
|
||||||
disabled
|
|
||||||
? theme.font.color.light
|
|
||||||
: accent === ChipAccent.TextPrimary
|
|
||||||
? theme.font.color.primary
|
|
||||||
: theme.font.color.secondary};
|
|
||||||
cursor: ${({ clickable, disabled, variant }) =>
|
|
||||||
disabled || variant === ChipVariant.Transparent
|
|
||||||
? 'inherit'
|
|
||||||
: clickable
|
|
||||||
? 'pointer'
|
|
||||||
: 'inherit'};
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
font-weight: ${({ theme, accent }) =>
|
|
||||||
accent === ChipAccent.TextSecondary ? theme.font.weight.medium : 'inherit'};
|
|
||||||
gap: ${({ theme }) => theme.spacing(1)};
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
|
height: ${({ theme }) => theme.spacing(3)};
|
||||||
height: ${({ size }) => (size === ChipSize.Large ? '16px' : '12px')};
|
|
||||||
--chip-horizontal-padding: ${({ theme, variant }) =>
|
|
||||||
variant === ChipVariant.Rounded ? theme.spacing(2) : theme.spacing(1)};
|
|
||||||
max-width: ${({ maxWidth }) =>
|
max-width: ${({ maxWidth }) =>
|
||||||
maxWidth
|
maxWidth
|
||||||
? `calc(${maxWidth}px - 2 * var(--chip-horizontal-padding))`
|
? `calc(${maxWidth}px - 2 * var(--chip-horizontal-padding))`
|
||||||
: '200px'};
|
: '200px'};
|
||||||
|
|
||||||
--chip-vertical-padding: ${({ theme, variant }) =>
|
|
||||||
variant === ChipVariant.Rounded ? '3px' : theme.spacing(1)};
|
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: var(--chip-vertical-padding) var(--chip-horizontal-padding);
|
padding: var(--chip-vertical-padding) var(--chip-horizontal-padding);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
|
// Accent style overrides
|
||||||
|
${({ accent, disabled, theme }) => {
|
||||||
|
if (accent === ChipAccent.TextPrimary) {
|
||||||
|
return (
|
||||||
|
!disabled &&
|
||||||
|
css`
|
||||||
|
color: ${theme.font.color.primary};
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accent === ChipAccent.TextSecondary) {
|
||||||
|
return css`
|
||||||
|
font-weight: ${theme.font.weight.medium};
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Size style overrides
|
||||||
|
${({ theme, size }) =>
|
||||||
|
size === ChipSize.Large &&
|
||||||
|
css`
|
||||||
|
height: ${theme.spacing(4)};
|
||||||
|
`}
|
||||||
|
|
||||||
|
// Variant style overrides
|
||||||
|
${({ disabled, theme, variant }) => {
|
||||||
|
if (variant === ChipVariant.Regular) {
|
||||||
|
return (
|
||||||
|
!disabled &&
|
||||||
|
css`
|
||||||
:hover {
|
:hover {
|
||||||
${({ variant, theme, disabled }) => {
|
background-color: ${theme.background.transparent.light};
|
||||||
if (!disabled) {
|
|
||||||
return (
|
|
||||||
'background-color: ' +
|
|
||||||
(variant === ChipVariant.Highlighted
|
|
||||||
? theme.background.transparent.medium
|
|
||||||
: variant === ChipVariant.Regular
|
|
||||||
? theme.background.transparent.light
|
|
||||||
: 'transparent') +
|
|
||||||
';'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:active {
|
:active {
|
||||||
${({ variant, theme, disabled }) => {
|
background-color: ${theme.background.transparent.medium};
|
||||||
if (!disabled) {
|
}
|
||||||
return (
|
`
|
||||||
'background-color: ' +
|
|
||||||
(variant === ChipVariant.Highlighted
|
|
||||||
? theme.background.transparent.strong
|
|
||||||
: variant === ChipVariant.Regular
|
|
||||||
? theme.background.transparent.medium
|
|
||||||
: 'transparent') +
|
|
||||||
';'
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}}
|
|
||||||
|
if (variant === ChipVariant.Highlighted) {
|
||||||
|
return css`
|
||||||
|
background-color: ${theme.background.transparent.light};
|
||||||
|
|
||||||
|
${!disabled &&
|
||||||
|
css`
|
||||||
|
:hover {
|
||||||
|
background-color: ${theme.background.transparent.medium};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:active {
|
||||||
|
background-color: ${theme.background.transparent.strong};
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant === ChipVariant.Rounded) {
|
||||||
|
return css`
|
||||||
|
--chip-horizontal-padding: ${theme.spacing(2)};
|
||||||
|
--chip-vertical-padding: 3px;
|
||||||
|
|
||||||
|
background-color: ${theme.background.transparent.lighter};
|
||||||
|
border: 1px solid ${theme.border.color.medium};
|
||||||
|
border-radius: 50px;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant === ChipVariant.Transparent) {
|
||||||
|
return css`
|
||||||
|
cursor: inherit;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledLabel = styled.span`
|
const StyledLabel = styled.span`
|
||||||
|
@ -950,6 +950,7 @@ import {
|
|||||||
IconCalendarBolt,
|
IconCalendarBolt,
|
||||||
IconCalendarCancel,
|
IconCalendarCancel,
|
||||||
IconCalendarCheck,
|
IconCalendarCheck,
|
||||||
|
IconCalendarClock,
|
||||||
IconCalendarCode,
|
IconCalendarCode,
|
||||||
IconCalendarCog,
|
IconCalendarCog,
|
||||||
IconCalendarDollar,
|
IconCalendarDollar,
|
||||||
@ -4199,14 +4200,14 @@ import {
|
|||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
Icon123,
|
||||||
|
Icon24Hours,
|
||||||
Icon2fa,
|
Icon2fa,
|
||||||
|
Icon360,
|
||||||
|
Icon360View,
|
||||||
Icon3dCubeSphere,
|
Icon3dCubeSphere,
|
||||||
Icon3dCubeSphereOff,
|
Icon3dCubeSphereOff,
|
||||||
Icon3dRotate,
|
Icon3dRotate,
|
||||||
Icon24Hours,
|
|
||||||
Icon123,
|
|
||||||
Icon360,
|
|
||||||
Icon360View,
|
|
||||||
IconAB,
|
IconAB,
|
||||||
IconAB2,
|
IconAB2,
|
||||||
IconAbacus,
|
IconAbacus,
|
||||||
@ -5149,6 +5150,7 @@ export default {
|
|||||||
IconCalendarBolt,
|
IconCalendarBolt,
|
||||||
IconCalendarCancel,
|
IconCalendarCancel,
|
||||||
IconCalendarCheck,
|
IconCalendarCheck,
|
||||||
|
IconCalendarClock,
|
||||||
IconCalendarCode,
|
IconCalendarCode,
|
||||||
IconCalendarCog,
|
IconCalendarCog,
|
||||||
IconCalendarDollar,
|
IconCalendarDollar,
|
||||||
@ -5440,6 +5442,9 @@ export default {
|
|||||||
IconClockExclamation,
|
IconClockExclamation,
|
||||||
IconClockHeart,
|
IconClockHeart,
|
||||||
IconClockHour1,
|
IconClockHour1,
|
||||||
|
IconClockHour10,
|
||||||
|
IconClockHour11,
|
||||||
|
IconClockHour12,
|
||||||
IconClockHour2,
|
IconClockHour2,
|
||||||
IconClockHour3,
|
IconClockHour3,
|
||||||
IconClockHour4,
|
IconClockHour4,
|
||||||
@ -5448,9 +5453,6 @@ export default {
|
|||||||
IconClockHour7,
|
IconClockHour7,
|
||||||
IconClockHour8,
|
IconClockHour8,
|
||||||
IconClockHour9,
|
IconClockHour9,
|
||||||
IconClockHour10,
|
|
||||||
IconClockHour11,
|
|
||||||
IconClockHour12,
|
|
||||||
IconClockMinus,
|
IconClockMinus,
|
||||||
IconClockOff,
|
IconClockOff,
|
||||||
IconClockPause,
|
IconClockPause,
|
||||||
@ -7117,10 +7119,10 @@ export default {
|
|||||||
IconMovieOff,
|
IconMovieOff,
|
||||||
IconMug,
|
IconMug,
|
||||||
IconMugOff,
|
IconMugOff,
|
||||||
IconMultiplier1x,
|
|
||||||
IconMultiplier2x,
|
|
||||||
IconMultiplier05x,
|
IconMultiplier05x,
|
||||||
IconMultiplier15x,
|
IconMultiplier15x,
|
||||||
|
IconMultiplier1x,
|
||||||
|
IconMultiplier2x,
|
||||||
IconMushroom,
|
IconMushroom,
|
||||||
IconMushroomOff,
|
IconMushroomOff,
|
||||||
IconMusic,
|
IconMusic,
|
||||||
@ -7515,20 +7517,20 @@ export default {
|
|||||||
IconReservedLine,
|
IconReservedLine,
|
||||||
IconResize,
|
IconResize,
|
||||||
IconRestore,
|
IconRestore,
|
||||||
IconRewindBackward5,
|
|
||||||
IconRewindBackward10,
|
IconRewindBackward10,
|
||||||
IconRewindBackward15,
|
IconRewindBackward15,
|
||||||
IconRewindBackward20,
|
IconRewindBackward20,
|
||||||
IconRewindBackward30,
|
IconRewindBackward30,
|
||||||
IconRewindBackward40,
|
IconRewindBackward40,
|
||||||
|
IconRewindBackward5,
|
||||||
IconRewindBackward50,
|
IconRewindBackward50,
|
||||||
IconRewindBackward60,
|
IconRewindBackward60,
|
||||||
IconRewindForward5,
|
|
||||||
IconRewindForward10,
|
IconRewindForward10,
|
||||||
IconRewindForward15,
|
IconRewindForward15,
|
||||||
IconRewindForward20,
|
IconRewindForward20,
|
||||||
IconRewindForward30,
|
IconRewindForward30,
|
||||||
IconRewindForward40,
|
IconRewindForward40,
|
||||||
|
IconRewindForward5,
|
||||||
IconRewindForward50,
|
IconRewindForward50,
|
||||||
IconRewindForward60,
|
IconRewindForward60,
|
||||||
IconRibbonHealth,
|
IconRibbonHealth,
|
||||||
@ -8080,11 +8082,11 @@ export default {
|
|||||||
IconTiltShift,
|
IconTiltShift,
|
||||||
IconTiltShiftOff,
|
IconTiltShiftOff,
|
||||||
IconTimeDuration0,
|
IconTimeDuration0,
|
||||||
IconTimeDuration5,
|
|
||||||
IconTimeDuration10,
|
IconTimeDuration10,
|
||||||
IconTimeDuration15,
|
IconTimeDuration15,
|
||||||
IconTimeDuration30,
|
IconTimeDuration30,
|
||||||
IconTimeDuration45,
|
IconTimeDuration45,
|
||||||
|
IconTimeDuration5,
|
||||||
IconTimeDuration60,
|
IconTimeDuration60,
|
||||||
IconTimeDuration90,
|
IconTimeDuration90,
|
||||||
IconTimeDurationOff,
|
IconTimeDurationOff,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { RightDrawerCalendarEvent } from '@/activities/calendar/right-drawer/components/RightDrawerCalendarEvent';
|
||||||
import { RightDrawerEmailThread } from '@/activities/emails/right-drawer/components/RightDrawerEmailThread';
|
import { RightDrawerEmailThread } from '@/activities/emails/right-drawer/components/RightDrawerEmailThread';
|
||||||
import { RightDrawerEmailThreadTopBar } from '@/activities/emails/right-drawer/components/RightDrawerEmailThreadTopBar';
|
|
||||||
import { RightDrawerCreateActivity } from '@/activities/right-drawer/components/create/RightDrawerCreateActivity';
|
import { RightDrawerCreateActivity } from '@/activities/right-drawer/components/create/RightDrawerCreateActivity';
|
||||||
import { RightDrawerEditActivity } from '@/activities/right-drawer/components/edit/RightDrawerEditActivity';
|
import { RightDrawerEditActivity } from '@/activities/right-drawer/components/edit/RightDrawerEditActivity';
|
||||||
|
|
||||||
@ -27,28 +27,31 @@ const StyledRightDrawerBody = styled.div`
|
|||||||
position: relative;
|
position: relative;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const RIGHT_DRAWER_PAGES_CONFIG = {
|
||||||
|
[RightDrawerPages.CreateActivity]: {
|
||||||
|
page: <RightDrawerCreateActivity />,
|
||||||
|
topBar: <RightDrawerActivityTopBar />,
|
||||||
|
},
|
||||||
|
[RightDrawerPages.EditActivity]: {
|
||||||
|
page: <RightDrawerEditActivity />,
|
||||||
|
topBar: <RightDrawerActivityTopBar />,
|
||||||
|
},
|
||||||
|
[RightDrawerPages.ViewEmailThread]: {
|
||||||
|
page: <RightDrawerEmailThread />,
|
||||||
|
topBar: <RightDrawerActivityTopBar showActionBar={false} />,
|
||||||
|
},
|
||||||
|
[RightDrawerPages.ViewCalendarEvent]: {
|
||||||
|
page: <RightDrawerCalendarEvent />,
|
||||||
|
topBar: <RightDrawerActivityTopBar showActionBar={false} />,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const RightDrawerRouter = () => {
|
export const RightDrawerRouter = () => {
|
||||||
const [rightDrawerPage] = useRecoilState(rightDrawerPageState());
|
const [rightDrawerPage] = useRecoilState(rightDrawerPageState());
|
||||||
|
|
||||||
let page = <></>;
|
const { topBar = null, page = null } = rightDrawerPage
|
||||||
let topBar = <></>;
|
? RIGHT_DRAWER_PAGES_CONFIG[rightDrawerPage]
|
||||||
|
: {};
|
||||||
switch (rightDrawerPage) {
|
|
||||||
case RightDrawerPages.CreateActivity:
|
|
||||||
page = <RightDrawerCreateActivity />;
|
|
||||||
topBar = <RightDrawerActivityTopBar />;
|
|
||||||
break;
|
|
||||||
case RightDrawerPages.EditActivity:
|
|
||||||
page = <RightDrawerEditActivity />;
|
|
||||||
topBar = <RightDrawerActivityTopBar />;
|
|
||||||
break;
|
|
||||||
case RightDrawerPages.ViewEmailThread:
|
|
||||||
page = <RightDrawerEmailThread />;
|
|
||||||
topBar = <RightDrawerEmailThreadTopBar />;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledRightDrawerPage>
|
<StyledRightDrawerPage>
|
||||||
|
@ -2,4 +2,5 @@ export enum RightDrawerPages {
|
|||||||
CreateActivity = 'create-activity',
|
CreateActivity = 'create-activity',
|
||||||
EditActivity = 'edit-activity',
|
EditActivity = 'edit-activity',
|
||||||
ViewEmailThread = 'view-email-thread',
|
ViewEmailThread = 'view-email-thread',
|
||||||
|
ViewCalendarEvent = 'view-calendar-event',
|
||||||
}
|
}
|
||||||
|
@ -50,9 +50,10 @@ export const SettingsAccountsCalendars = () => {
|
|||||||
workspaceMemberId: currentWorkspaceMember?.id ?? '',
|
workspaceMemberId: currentWorkspaceMember?.id ?? '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
endsAt: exampleEndDate,
|
endsAt: exampleEndDate.toISOString(),
|
||||||
|
externalCreatedAt: new Date().toISOString(),
|
||||||
isFullDay: false,
|
isFullDay: false,
|
||||||
startsAt: exampleStartDate,
|
startsAt: exampleStartDate.toISOString(),
|
||||||
title: 'Onboarding call',
|
title: 'Onboarding call',
|
||||||
visibility: 'SHARE_EVERYTHING',
|
visibility: 'SHARE_EVERYTHING',
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { addDays, subMonths } from 'date-fns';
|
import { addDays, subHours, subMonths } from 'date-fns';
|
||||||
|
|
||||||
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||||
|
|
||||||
export const mockedCalendarEvents: CalendarEvent[] = [
|
export const mockedCalendarEvents: CalendarEvent[] = [
|
||||||
{
|
{
|
||||||
|
externalCreatedAt: new Date().toISOString(),
|
||||||
|
endsAt: addDays(new Date().setHours(11, 30), 1).toISOString(),
|
||||||
id: '9a6b35f1-6078-415b-9540-f62671bb81d0',
|
id: '9a6b35f1-6078-415b-9540-f62671bb81d0',
|
||||||
endsAt: addDays(new Date().setHours(11, 30), 1),
|
|
||||||
isFullDay: false,
|
isFullDay: false,
|
||||||
startsAt: addDays(new Date().setHours(10, 0), 1),
|
startsAt: addDays(new Date().setHours(10, 0), 1).toISOString(),
|
||||||
visibility: 'METADATA',
|
visibility: 'METADATA',
|
||||||
attendees: [
|
attendees: [
|
||||||
{ displayName: 'John Doe', workspaceMemberId: 'john-doe' },
|
{ displayName: 'John Doe', workspaceMemberId: 'john-doe' },
|
||||||
@ -16,41 +17,46 @@ export const mockedCalendarEvents: CalendarEvent[] = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
externalCreatedAt: subHours(new Date(), 2).toISOString(),
|
||||||
id: '19b32878-a950-4968-9e3b-ce5da514ea41',
|
id: '19b32878-a950-4968-9e3b-ce5da514ea41',
|
||||||
endsAt: new Date(new Date().setHours(18, 40)),
|
endsAt: new Date(new Date().setHours(18, 40)).toISOString(),
|
||||||
isCanceled: true,
|
isCanceled: true,
|
||||||
isFullDay: false,
|
isFullDay: false,
|
||||||
startsAt: new Date(new Date().setHours(18, 0)),
|
startsAt: new Date(new Date().setHours(18, 0)).toISOString(),
|
||||||
title: 'Bug solving',
|
title: 'Bug solving',
|
||||||
visibility: 'SHARE_EVERYTHING',
|
visibility: 'SHARE_EVERYTHING',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
externalCreatedAt: subHours(new Date(), 2).toISOString(),
|
||||||
id: '6ad1cbcb-2ac4-409e-aff0-48165556fc0c',
|
id: '6ad1cbcb-2ac4-409e-aff0-48165556fc0c',
|
||||||
endsAt: new Date(new Date().setHours(16, 30)),
|
endsAt: new Date(new Date().setHours(16, 30)).toISOString(),
|
||||||
isFullDay: false,
|
isFullDay: false,
|
||||||
startsAt: new Date(new Date().setHours(15, 15)),
|
startsAt: new Date(new Date().setHours(15, 15)).toISOString(),
|
||||||
title: 'Onboarding Follow-Up Call',
|
title: 'Onboarding Follow-Up Call',
|
||||||
visibility: 'SHARE_EVERYTHING',
|
visibility: 'SHARE_EVERYTHING',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
externalCreatedAt: subHours(new Date(), 2).toISOString(),
|
||||||
id: '52cc83e3-f3dc-4c25-8a7d-5ff857612142',
|
id: '52cc83e3-f3dc-4c25-8a7d-5ff857612142',
|
||||||
endsAt: new Date(new Date().setHours(10, 30)),
|
endsAt: new Date(new Date().setHours(10, 30)).toISOString(),
|
||||||
isFullDay: false,
|
isFullDay: false,
|
||||||
startsAt: new Date(new Date().setHours(10, 0)),
|
startsAt: new Date(new Date().setHours(10, 0)).toISOString(),
|
||||||
title: 'Onboarding Call',
|
title: 'Onboarding Call',
|
||||||
visibility: 'SHARE_EVERYTHING',
|
visibility: 'SHARE_EVERYTHING',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
externalCreatedAt: subHours(new Date(), 2).toISOString(),
|
||||||
id: '5a792d11-259a-4099-af51-59eb85e15d83',
|
id: '5a792d11-259a-4099-af51-59eb85e15d83',
|
||||||
isFullDay: true,
|
isFullDay: true,
|
||||||
startsAt: subMonths(new Date().setHours(8, 0), 1),
|
startsAt: subMonths(new Date().setHours(8, 0), 1).toISOString(),
|
||||||
visibility: 'METADATA',
|
visibility: 'METADATA',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
externalCreatedAt: subHours(new Date(), 2).toISOString(),
|
||||||
id: '89e2a1c7-3d3f-4e79-a492-aa5de3785fc5',
|
id: '89e2a1c7-3d3f-4e79-a492-aa5de3785fc5',
|
||||||
endsAt: subMonths(new Date().setHours(14, 30), 3),
|
endsAt: subMonths(new Date().setHours(14, 30), 3).toISOString(),
|
||||||
isFullDay: false,
|
isFullDay: false,
|
||||||
startsAt: subMonths(new Date().setHours(14, 0), 3),
|
startsAt: subMonths(new Date().setHours(14, 0), 3).toISOString(),
|
||||||
title: 'Alan x Garry',
|
title: 'Alan x Garry',
|
||||||
visibility: 'SHARE_EVERYTHING',
|
visibility: 'SHARE_EVERYTHING',
|
||||||
},
|
},
|
||||||
|
@ -11,9 +11,10 @@ import { seedWorkspaceMember } from 'src/database/typeorm-seeds/workspace/worksp
|
|||||||
import { seedPeople } from 'src/database/typeorm-seeds/workspace/people';
|
import { seedPeople } from 'src/database/typeorm-seeds/workspace/people';
|
||||||
import { seedCoreSchema } from 'src/database/typeorm-seeds/core';
|
import { seedCoreSchema } from 'src/database/typeorm-seeds/core';
|
||||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||||
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
|
import { seedCalendarEvents } from 'src/database/typeorm-seeds/workspace/calendar-events';
|
||||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
|
||||||
import { ObjectMetadataService } from 'src/engine-metadata/object-metadata/object-metadata.service';
|
import { ObjectMetadataService } from 'src/engine-metadata/object-metadata/object-metadata.service';
|
||||||
|
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||||
|
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
|
||||||
|
|
||||||
// TODO: implement dry-run
|
// TODO: implement dry-run
|
||||||
@Command({
|
@Command({
|
||||||
@ -105,6 +106,7 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
|
|||||||
await seedPeople(workspaceDataSource, dataSourceMetadata.schema);
|
await seedPeople(workspaceDataSource, dataSourceMetadata.schema);
|
||||||
await seedPipelineStep(workspaceDataSource, dataSourceMetadata.schema);
|
await seedPipelineStep(workspaceDataSource, dataSourceMetadata.schema);
|
||||||
await seedOpportunity(workspaceDataSource, dataSourceMetadata.schema);
|
await seedOpportunity(workspaceDataSource, dataSourceMetadata.schema);
|
||||||
|
await seedCalendarEvents(workspaceDataSource, dataSourceMetadata.schema);
|
||||||
|
|
||||||
await seedViews(
|
await seedViews(
|
||||||
workspaceDataSource,
|
workspaceDataSource,
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
|
||||||
|
const tableName = 'calendarEvent';
|
||||||
|
|
||||||
|
export const seedCalendarEvents = async (
|
||||||
|
workspaceDataSource: DataSource,
|
||||||
|
schemaName: string,
|
||||||
|
) => {
|
||||||
|
await workspaceDataSource
|
||||||
|
.createQueryBuilder()
|
||||||
|
.insert()
|
||||||
|
.into(`${schemaName}.${tableName}`, [
|
||||||
|
'id',
|
||||||
|
'title',
|
||||||
|
'isCanceled',
|
||||||
|
'isFullDay',
|
||||||
|
'startsAt',
|
||||||
|
'endsAt',
|
||||||
|
'externalCreatedAt',
|
||||||
|
'externalUpdatedAt',
|
||||||
|
'description',
|
||||||
|
'location',
|
||||||
|
'iCalUID',
|
||||||
|
'conferenceSolution',
|
||||||
|
'conferenceUri',
|
||||||
|
'recurringEventExternalId',
|
||||||
|
])
|
||||||
|
.orIgnore()
|
||||||
|
.values([
|
||||||
|
{
|
||||||
|
id: '86083141-1c0e-494c-a1b6-85b1c6fefaa5',
|
||||||
|
title: 'Meeting with Christoph',
|
||||||
|
isCanceled: false,
|
||||||
|
isFullDay: false,
|
||||||
|
startsAt: new Date(new Date().setHours(10, 0)).toISOString(),
|
||||||
|
endsAt: new Date(new Date().setHours(11, 0)).toISOString(),
|
||||||
|
externalCreatedAt: new Date().toISOString(),
|
||||||
|
externalUpdatedAt: new Date().toISOString(),
|
||||||
|
description: 'Discuss project progress',
|
||||||
|
location: 'Seattle',
|
||||||
|
iCalUID: 'event1@calendar.com',
|
||||||
|
conferenceSolution: 'Zoom',
|
||||||
|
conferenceUri: 'https://zoom.us/j/1234567890',
|
||||||
|
recurringEventExternalId: 'recurring1',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.execute();
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user