Fix calendar events & messages fetching + fix timeline design (#11840)
Preview : <img width="501" alt="Screenshot 2025-05-02 at 16 24 34" src="https://github.com/user-attachments/assets/0c649df1-0e26-4ddc-8e13-ebd78af7ec09" /> Done : - Fix getCalendarEventsFromPersonIds and getCalendarEventsFromCompanyId (include accountOwner check) - Fix permission check on pre-hook - Pre-hook seems useless, calendar events are always on METADATA or SHARE_EVERYTHING visibility, else post hook always has the responsibility of returning the data user can access. >> To delete or to keep in case other visibility options are added ? - Add post hook to secure finOne / findMany calendarEvents resolver - Update design To do : - same on messages (PR to arrive) closes : https://github.com/twentyhq/twenty/issues/9826
This commit is contained in:
@ -0,0 +1,36 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { IconLock } from 'twenty-ui/display';
|
||||
import { Card, CardContent } from 'twenty-ui/layout';
|
||||
|
||||
const StyledVisibilityCard = styled(Card)`
|
||||
border-color: ${({ theme }) => theme.border.color.light};
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
flex: 1;
|
||||
transition: color ${({ theme }) => theme.animation.duration.normal} ease;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledVisibilityCardContent = styled(CardContent)`
|
||||
align-items: center;
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
padding: ${({ theme }) => theme.spacing(0, 1)};
|
||||
height: ${({ theme }) => theme.spacing(6)};
|
||||
background-color: ${({ theme }) => theme.background.transparent.lighter};
|
||||
`;
|
||||
|
||||
export const CalendarEventNotSharedContent = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<StyledVisibilityCard>
|
||||
<StyledVisibilityCardContent>
|
||||
<IconLock size={theme.icon.size.sm} />
|
||||
Not shared
|
||||
</StyledVisibilityCardContent>
|
||||
</StyledVisibilityCard>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,68 @@
|
||||
import { CalendarEventParticipant } from '@/activities/calendar/types/CalendarEventParticipant';
|
||||
import { isTimelineCalendarEventParticipant } from '@/activities/calendar/types/guards/IsTimelineCalendarEventParticipant';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Avatar, AvatarGroup } from 'twenty-ui/display';
|
||||
import { TimelineCalendarEventParticipant } from '~/generated-metadata/graphql';
|
||||
|
||||
type CalendarEventParticipantsAvatarGroupProps = {
|
||||
participants: CalendarEventParticipant[] | TimelineCalendarEventParticipant[];
|
||||
};
|
||||
|
||||
export const CalendarEventParticipantsAvatarGroup = ({
|
||||
participants,
|
||||
}: CalendarEventParticipantsAvatarGroupProps) => {
|
||||
const timelineParticipants: TimelineCalendarEventParticipant[] =
|
||||
participants.map((participant) => {
|
||||
if (isTimelineCalendarEventParticipant(participant)) {
|
||||
return participant;
|
||||
} else {
|
||||
return {
|
||||
personId: participant.person?.id ?? null,
|
||||
workspaceMemberId: participant.workspaceMember?.id ?? null,
|
||||
firstName:
|
||||
participant.person?.name?.firstName ||
|
||||
participant.workspaceMember?.name.firstName ||
|
||||
'',
|
||||
lastName:
|
||||
participant.person?.name?.lastName ||
|
||||
participant.workspaceMember?.name.lastName ||
|
||||
'',
|
||||
displayName:
|
||||
participant.person?.name?.firstName ||
|
||||
participant.person?.name?.lastName ||
|
||||
participant.workspaceMember?.name.firstName ||
|
||||
participant.workspaceMember?.name.lastName ||
|
||||
participant.displayName ||
|
||||
participant.handle ||
|
||||
'',
|
||||
avatarUrl:
|
||||
participant.person?.avatarUrl ||
|
||||
participant.workspaceMember?.avatarUrl ||
|
||||
'',
|
||||
handle: participant.handle,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<AvatarGroup
|
||||
avatars={timelineParticipants.map((participant) => (
|
||||
<Avatar
|
||||
key={[participant.workspaceMemberId, participant.displayName]
|
||||
.filter(isDefined)
|
||||
.join('-')}
|
||||
avatarUrl={participant.avatarUrl}
|
||||
placeholder={
|
||||
participant.firstName && participant.lastName
|
||||
? `${participant.firstName} ${participant.lastName}`
|
||||
: participant.displayName
|
||||
}
|
||||
placeholderColorSeed={
|
||||
participant.workspaceMemberId || participant.personId
|
||||
}
|
||||
type="rounded"
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -5,24 +5,19 @@ import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { CalendarCurrentEventCursor } from '@/activities/calendar/components/CalendarCurrentEventCursor';
|
||||
import { CalendarEventNotSharedContent } from '@/activities/calendar/components/CalendarEventNotSharedContent';
|
||||
import { CalendarEventParticipantsAvatarGroup } from '@/activities/calendar/components/CalendarEventParticipantsAvatarGroup';
|
||||
import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext';
|
||||
import { getCalendarEventEndDate } from '@/activities/calendar/utils/getCalendarEventEndDate';
|
||||
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
|
||||
import { hasCalendarEventEnded } from '@/activities/calendar/utils/hasCalendarEventEnded';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useOpenCalendarEventInCommandMenu } from '@/command-menu/hooks/useOpenCalendarEventInCommandMenu';
|
||||
import { IconArrowRight } from 'twenty-ui/display';
|
||||
import {
|
||||
CalendarChannelVisibility,
|
||||
TimelineCalendarEvent,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
Avatar,
|
||||
AvatarGroup,
|
||||
IconArrowRight,
|
||||
IconLock,
|
||||
} from 'twenty-ui/display';
|
||||
import { Card, CardContent } from 'twenty-ui/layout';
|
||||
|
||||
type CalendarEventRowProps = {
|
||||
calendarEvent: TimelineCalendarEvent;
|
||||
@ -87,25 +82,6 @@ const StyledTitle = styled.div<{ active: boolean; canceled: boolean }>`
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledVisibilityCard = styled(Card)<{ active: boolean }>`
|
||||
color: ${({ active, theme }) =>
|
||||
active ? theme.font.color.primary : theme.font.color.light};
|
||||
border-color: ${({ theme }) => theme.border.color.light};
|
||||
flex: 1 0 auto;
|
||||
transition: color ${({ theme }) => theme.animation.duration.normal} ease;
|
||||
`;
|
||||
|
||||
const StyledVisibilityCardContent = styled(CardContent)`
|
||||
align-items: center;
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
padding: ${({ theme }) => theme.spacing(0, 1)};
|
||||
height: ${({ theme }) => theme.spacing(6)};
|
||||
background-color: ${({ theme }) => theme.background.transparent.lighter};
|
||||
`;
|
||||
|
||||
export const CalendarEventRow = ({
|
||||
calendarEvent,
|
||||
className,
|
||||
@ -159,33 +135,12 @@ export const CalendarEventRow = ({
|
||||
{calendarEvent.title}
|
||||
</StyledTitle>
|
||||
) : (
|
||||
<StyledVisibilityCard active={!hasEnded}>
|
||||
<StyledVisibilityCardContent>
|
||||
<IconLock size={theme.icon.size.sm} />
|
||||
Not shared
|
||||
</StyledVisibilityCardContent>
|
||||
</StyledVisibilityCard>
|
||||
<CalendarEventNotSharedContent />
|
||||
)}
|
||||
</StyledLabels>
|
||||
{!!calendarEvent.participants?.length && (
|
||||
<AvatarGroup
|
||||
avatars={calendarEvent.participants.map((participant) => (
|
||||
<Avatar
|
||||
key={[participant.workspaceMemberId, participant.displayName]
|
||||
.filter(isDefined)
|
||||
.join('-')}
|
||||
avatarUrl={participant.avatarUrl}
|
||||
placeholder={
|
||||
participant.firstName && participant.lastName
|
||||
? `${participant.firstName} ${participant.lastName}`
|
||||
: participant.displayName
|
||||
}
|
||||
placeholderColorSeed={
|
||||
participant.workspaceMemberId || participant.personId
|
||||
}
|
||||
type="rounded"
|
||||
/>
|
||||
))}
|
||||
<CalendarEventParticipantsAvatarGroup
|
||||
participants={calendarEvent.participants}
|
||||
/>
|
||||
)}
|
||||
{displayCurrentEventCursor && (
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
import { CalendarEventParticipant } from '@/activities/calendar/types/CalendarEventParticipant';
|
||||
import { TimelineCalendarEventParticipant } from '~/generated-metadata/graphql';
|
||||
|
||||
export const isTimelineCalendarEventParticipant = (
|
||||
participant: CalendarEventParticipant | TimelineCalendarEventParticipant,
|
||||
): participant is TimelineCalendarEventParticipant => {
|
||||
return 'avatarUrl' in participant;
|
||||
};
|
||||
Reference in New Issue
Block a user