Basic log styling (#4634)
* basic log styling * fixed mobile wrap and changed default event icon * add group by test
This commit is contained in:
@ -1,8 +1,11 @@
|
|||||||
import { ReactElement } from 'react';
|
import { ReactElement } from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { EventRow } from '@/activities/events/components/EventRow';
|
import { EventsGroup } from '@/activities/events/components/EventsGroup';
|
||||||
import { Event } from '@/activities/events/types/Event';
|
import { Event } from '@/activities/events/types/Event';
|
||||||
|
import { groupEventsByMonth } from '@/activities/events/utils/groupEventsByMonth';
|
||||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
|
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||||
|
|
||||||
type EventListProps = {
|
type EventListProps = {
|
||||||
targetableObject: ActivityTargetableObject;
|
targetableObject: ActivityTargetableObject;
|
||||||
@ -11,12 +14,43 @@ type EventListProps = {
|
|||||||
button?: ReactElement | false;
|
button?: ReactElement | false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EventList = ({ events }: EventListProps) => {
|
const StyledTimelineContainer = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
align-self: stretch;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 0;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
padding: ${({ theme }) => theme.spacing(4)};
|
||||||
|
width: calc(100% - ${({ theme }) => theme.spacing(8)});
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const EventList = ({ events, targetableObject }: EventListProps) => {
|
||||||
|
const groupedEvents = groupEventsByMonth(events);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<ScrollWrapper>
|
||||||
{events &&
|
<StyledTimelineContainer>
|
||||||
events.length > 0 &&
|
{groupedEvents.map((group, index) => (
|
||||||
events.map((event: Event) => <EventRow key={event.id} event={event} />)}
|
<EventsGroup
|
||||||
</>
|
targetableObject={targetableObject}
|
||||||
|
key={group.year.toString() + group.month}
|
||||||
|
group={group}
|
||||||
|
month={new Date(group.items[0].createdAt).toLocaleString(
|
||||||
|
'default',
|
||||||
|
{ month: 'long' },
|
||||||
|
)}
|
||||||
|
year={
|
||||||
|
index === 0 || group.year !== groupedEvents[index - 1].year
|
||||||
|
? group.year
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</StyledTimelineContainer>
|
||||||
|
</ScrollWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,11 +1,203 @@
|
|||||||
import { Event } from '@/activities/events/types/Event';
|
import { Tooltip } from 'react-tooltip';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { EventUpdateProperty } from '@/activities/events/components/EventUpdateProperty';
|
||||||
|
import { Event } from '@/activities/events/types/Event';
|
||||||
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
|
import {
|
||||||
|
IconCirclePlus,
|
||||||
|
IconEditCircle,
|
||||||
|
IconFocusCentered,
|
||||||
|
} from '@/ui/display/icon';
|
||||||
|
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||||
|
import {
|
||||||
|
beautifyExactDateTime,
|
||||||
|
beautifyPastDateRelativeToNow,
|
||||||
|
} from '~/utils/date-utils';
|
||||||
|
|
||||||
|
const StyledIconContainer = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
|
display: flex;
|
||||||
|
user-select: none;
|
||||||
|
height: 16px;
|
||||||
|
margin: 5px;
|
||||||
|
justify-content: center;
|
||||||
|
text-decoration-line: underline;
|
||||||
|
width: 16px;
|
||||||
|
z-index: 2;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledActionName = styled.span`
|
||||||
|
overflow: hidden;
|
||||||
|
flex: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledItemContainer = styled.div`
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
|
span {
|
||||||
|
color: ${({ theme }) => theme.font.color.secondary};
|
||||||
|
}
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledItemTitleContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-flow: row ${() => (useIsMobile() ? 'wrap' : 'nowrap')};
|
||||||
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledItemAuthorText = styled.span`
|
||||||
|
display: flex;
|
||||||
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
|
white-space: nowrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledItemTitle = styled.span`
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledItemTitleDate = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
|
display: flex;
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-left: auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledVerticalLineContainer = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
align-self: stretch;
|
||||||
|
display: flex;
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
|
justify-content: center;
|
||||||
|
width: 26px;
|
||||||
|
z-index: 2;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledVerticalLine = styled.div`
|
||||||
|
align-self: stretch;
|
||||||
|
background: ${({ theme }) => theme.border.color.light};
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 2px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTooltip = styled(Tooltip)`
|
||||||
|
background-color: ${({ theme }) => theme.background.primary};
|
||||||
|
|
||||||
|
box-shadow: 0px 2px 4px 3px
|
||||||
|
${({ theme }) => theme.background.transparent.light};
|
||||||
|
|
||||||
|
box-shadow: 2px 4px 16px 6px
|
||||||
|
${({ theme }) => theme.background.transparent.light};
|
||||||
|
|
||||||
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
padding: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTimelineItemContainer = styled.div<{ isGap?: boolean }>`
|
||||||
|
align-items: center;
|
||||||
|
align-self: stretch;
|
||||||
|
display: flex;
|
||||||
|
gap: ${({ theme }) => theme.spacing(4)};
|
||||||
|
height: ${({ isGap, theme }) =>
|
||||||
|
isGap ? (useIsMobile() ? theme.spacing(6) : theme.spacing(3)) : 'auto'};
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type EventRowProps = {
|
||||||
|
targetableObject: ActivityTargetableObject;
|
||||||
|
isLastEvent?: boolean;
|
||||||
|
event: Event;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EventRow = ({
|
||||||
|
isLastEvent,
|
||||||
|
event,
|
||||||
|
targetableObject,
|
||||||
|
}: EventRowProps) => {
|
||||||
|
const beautifiedCreatedAt = beautifyPastDateRelativeToNow(event.createdAt);
|
||||||
|
const exactCreatedAt = beautifyExactDateTime(event.createdAt);
|
||||||
|
|
||||||
|
const properties = JSON.parse(event.properties);
|
||||||
|
const diff: Record<string, { before: any; after: any }> = properties?.diff;
|
||||||
|
|
||||||
|
const isEventType = (type: 'created' | 'updated') => {
|
||||||
|
return (
|
||||||
|
event.name === type + '.' + targetableObject.targetObjectNameSingular
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const EventRow = ({ event }: { event: Event }) => {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p>
|
<StyledTimelineItemContainer>
|
||||||
{event.name}:<pre>{event.properties}</pre>
|
<StyledIconContainer>
|
||||||
</p>
|
{isEventType('created') && <IconCirclePlus />}
|
||||||
|
{isEventType('updated') && <IconEditCircle />}
|
||||||
|
{!isEventType('created') && !isEventType('updated') && (
|
||||||
|
<IconFocusCentered />
|
||||||
|
)}
|
||||||
|
</StyledIconContainer>
|
||||||
|
<StyledItemContainer>
|
||||||
|
<StyledItemTitleContainer>
|
||||||
|
<StyledItemAuthorText>
|
||||||
|
{event.workspaceMember?.name.firstName}{' '}
|
||||||
|
{event.workspaceMember?.name.lastName}
|
||||||
|
</StyledItemAuthorText>
|
||||||
|
<StyledActionName>
|
||||||
|
{isEventType('created') && 'created'}
|
||||||
|
{isEventType('updated') && 'updated'}
|
||||||
|
{!isEventType('created') && !isEventType('updated') && event.name}
|
||||||
|
</StyledActionName>
|
||||||
|
<StyledItemTitle>
|
||||||
|
{isEventType('created') &&
|
||||||
|
`a new ${targetableObject.targetObjectNameSingular}`}
|
||||||
|
{isEventType('updated') &&
|
||||||
|
Object.entries(diff).map(([key, value]) => (
|
||||||
|
<EventUpdateProperty
|
||||||
|
propertyName={key}
|
||||||
|
after={value?.after}
|
||||||
|
></EventUpdateProperty>
|
||||||
|
))}
|
||||||
|
{!isEventType('created') &&
|
||||||
|
!isEventType('updated') &&
|
||||||
|
JSON.stringify(diff)}
|
||||||
|
</StyledItemTitle>
|
||||||
|
</StyledItemTitleContainer>
|
||||||
|
<StyledItemTitleDate id={`id-${event.id}`}>
|
||||||
|
{beautifiedCreatedAt}
|
||||||
|
</StyledItemTitleDate>
|
||||||
|
<StyledTooltip
|
||||||
|
anchorSelect={`#id-${event.id}`}
|
||||||
|
content={exactCreatedAt}
|
||||||
|
clickable
|
||||||
|
noArrow
|
||||||
|
/>
|
||||||
|
</StyledItemContainer>
|
||||||
|
</StyledTimelineItemContainer>
|
||||||
|
{!isLastEvent && (
|
||||||
|
<StyledTimelineItemContainer isGap>
|
||||||
|
<StyledVerticalLineContainer>
|
||||||
|
<StyledVerticalLine></StyledVerticalLine>
|
||||||
|
</StyledVerticalLineContainer>
|
||||||
|
</StyledTimelineItemContainer>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { IconArrowRight } from '@/ui/display/icon';
|
||||||
|
|
||||||
|
type EventUpdatePropertyProps = {
|
||||||
|
propertyName: string;
|
||||||
|
after?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
margin-right: ${({ theme }) => theme.spacing(1)};
|
||||||
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
|
white-space: nowrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledPropertyName = styled.div``;
|
||||||
|
|
||||||
|
export const EventUpdateProperty = ({
|
||||||
|
propertyName,
|
||||||
|
after,
|
||||||
|
}: EventUpdatePropertyProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
return (
|
||||||
|
<StyledContainer>
|
||||||
|
<StyledPropertyName>{propertyName}</StyledPropertyName>
|
||||||
|
<IconArrowRight size={theme.icon.size.sm} stroke={theme.icon.stroke.sm} />
|
||||||
|
{after}
|
||||||
|
</StyledContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,8 +1,29 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
import { isNonEmptyArray } from '@sniptt/guards';
|
import { isNonEmptyArray } from '@sniptt/guards';
|
||||||
|
|
||||||
import { EventList } from '@/activities/events/components/EventList';
|
import { EventList } from '@/activities/events/components/EventList';
|
||||||
import { useEvents } from '@/activities/events/hooks/useEvents';
|
import { useEvents } from '@/activities/events/hooks/useEvents';
|
||||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
|
import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
|
||||||
|
import {
|
||||||
|
AnimatedPlaceholderEmptyContainer,
|
||||||
|
AnimatedPlaceholderEmptySubTitle,
|
||||||
|
AnimatedPlaceholderEmptyTextContainer,
|
||||||
|
AnimatedPlaceholderEmptyTitle,
|
||||||
|
} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
|
||||||
|
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||||
|
|
||||||
|
const StyledMainContainer = styled.div`
|
||||||
|
align-items: flex-start;
|
||||||
|
align-self: stretch;
|
||||||
|
border-top: ${({ theme }) =>
|
||||||
|
useIsMobile() ? `1px solid ${theme.border.color.medium}` : 'none'};
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
export const Events = ({
|
export const Events = ({
|
||||||
targetableObject,
|
targetableObject,
|
||||||
@ -12,14 +33,28 @@ export const Events = ({
|
|||||||
const { events } = useEvents(targetableObject);
|
const { events } = useEvents(targetableObject);
|
||||||
|
|
||||||
if (!isNonEmptyArray(events)) {
|
if (!isNonEmptyArray(events)) {
|
||||||
return <div>No log yet</div>;
|
return (
|
||||||
|
<AnimatedPlaceholderEmptyContainer>
|
||||||
|
<AnimatedPlaceholder type="emptyTimeline" />
|
||||||
|
<AnimatedPlaceholderEmptyTextContainer>
|
||||||
|
<AnimatedPlaceholderEmptyTitle>
|
||||||
|
No Events
|
||||||
|
</AnimatedPlaceholderEmptyTitle>
|
||||||
|
<AnimatedPlaceholderEmptySubTitle>
|
||||||
|
There are no events associated with this record.{' '}
|
||||||
|
</AnimatedPlaceholderEmptySubTitle>
|
||||||
|
</AnimatedPlaceholderEmptyTextContainer>
|
||||||
|
</AnimatedPlaceholderEmptyContainer>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EventList
|
<StyledMainContainer>
|
||||||
targetableObject={targetableObject}
|
<EventList
|
||||||
title="All"
|
targetableObject={targetableObject}
|
||||||
events={events ?? []}
|
title="All"
|
||||||
/>
|
events={events ?? []}
|
||||||
|
/>
|
||||||
|
</StyledMainContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,81 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { EventRow } from '@/activities/events/components/EventRow';
|
||||||
|
import { EventGroup } from '@/activities/events/utils/groupEventsByMonth';
|
||||||
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
|
|
||||||
|
type EventsGroupProps = {
|
||||||
|
group: EventGroup;
|
||||||
|
month: string;
|
||||||
|
year?: number;
|
||||||
|
targetableObject: ActivityTargetableObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledActivityGroup = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
gap: ${({ theme }) => theme.spacing(4)};
|
||||||
|
margin-bottom: ${({ theme }) => theme.spacing(4)};
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledActivityGroupContainer = styled.div`
|
||||||
|
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||||
|
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||||
|
position: relative;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledActivityGroupBar = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
background: ${({ theme }) => theme.background.secondary};
|
||||||
|
border: 1px solid ${({ theme }) => theme.border.color.light};
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.xl};
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 24px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledMonthSeperator = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
align-self: stretch;
|
||||||
|
color: ${({ theme }) => theme.font.color.light};
|
||||||
|
display: flex;
|
||||||
|
gap: ${({ theme }) => theme.spacing(4)};
|
||||||
|
`;
|
||||||
|
const StyledMonthSeperatorLine = styled.div`
|
||||||
|
background: ${({ theme }) => theme.border.color.light};
|
||||||
|
border-radius: 50px;
|
||||||
|
flex: 1 0 0;
|
||||||
|
height: 1px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const EventsGroup = ({
|
||||||
|
group,
|
||||||
|
month,
|
||||||
|
year,
|
||||||
|
targetableObject,
|
||||||
|
}: EventsGroupProps) => {
|
||||||
|
return (
|
||||||
|
<StyledActivityGroup>
|
||||||
|
<StyledMonthSeperator>
|
||||||
|
{month} {year}
|
||||||
|
<StyledMonthSeperatorLine />
|
||||||
|
</StyledMonthSeperator>
|
||||||
|
<StyledActivityGroupContainer>
|
||||||
|
<StyledActivityGroupBar />
|
||||||
|
{group.items.map((event, index) => (
|
||||||
|
<EventRow
|
||||||
|
targetableObject={targetableObject}
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
isLastEvent={index === group.items.length - 1}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</StyledActivityGroupContainer>
|
||||||
|
</StyledActivityGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,12 +1,15 @@
|
|||||||
|
import { WorkspaceMember } from '~/generated/graphql';
|
||||||
|
|
||||||
export type Event = {
|
export type Event = {
|
||||||
id: string;
|
id: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
deletedAt: string | null;
|
deletedAt: string | null;
|
||||||
opportunityId: string | null;
|
opportunityId: string | null;
|
||||||
companyId: string;
|
companyId: string | null;
|
||||||
personId: string;
|
personId: string | null;
|
||||||
workspaceMemberId: string;
|
workspaceMemberId: string;
|
||||||
|
workspaceMember: WorkspaceMember;
|
||||||
properties: any;
|
properties: any;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
import { mockedEvents } from '~/testing/mock-data/events';
|
||||||
|
|
||||||
|
import { groupEventsByMonth } from '../groupEventsByMonth';
|
||||||
|
|
||||||
|
describe('groupEventsByMonth', () => {
|
||||||
|
it('should group activities by month', () => {
|
||||||
|
const grouped = groupEventsByMonth(mockedEvents as unknown as Event[]);
|
||||||
|
|
||||||
|
expect(grouped).toHaveLength(2);
|
||||||
|
expect(grouped[0].items).toHaveLength(1);
|
||||||
|
expect(grouped[1].items).toHaveLength(1);
|
||||||
|
|
||||||
|
expect(grouped[0].year).toBe(new Date().getFullYear());
|
||||||
|
expect(grouped[1].year).toBe(2023);
|
||||||
|
|
||||||
|
expect(grouped[0].month).toBe(new Date().getMonth());
|
||||||
|
expect(grouped[1].month).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { Event } from '@/activities/events/types/Event';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
|
export type EventGroup = {
|
||||||
|
month: number;
|
||||||
|
year: number;
|
||||||
|
items: Event[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const groupEventsByMonth = (events: Event[]) => {
|
||||||
|
const acitivityGroups: EventGroup[] = [];
|
||||||
|
|
||||||
|
for (const event of events) {
|
||||||
|
const d = new Date(event.createdAt);
|
||||||
|
const month = d.getMonth();
|
||||||
|
const year = d.getFullYear();
|
||||||
|
|
||||||
|
const matchingGroup = acitivityGroups.find(
|
||||||
|
(x) => x.year === year && x.month === month,
|
||||||
|
);
|
||||||
|
if (isDefined(matchingGroup)) {
|
||||||
|
matchingGroup.items.push(event);
|
||||||
|
} else {
|
||||||
|
acitivityGroups.push({
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
items: [event],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acitivityGroups.sort((a, b) => b.year - a.year || b.month - a.month);
|
||||||
|
};
|
||||||
@ -37,6 +37,7 @@ export {
|
|||||||
IconChevronUp,
|
IconChevronUp,
|
||||||
IconCircleDot,
|
IconCircleDot,
|
||||||
IconCircleOff,
|
IconCircleOff,
|
||||||
|
IconCirclePlus,
|
||||||
IconCircleX,
|
IconCircleX,
|
||||||
IconClick,
|
IconClick,
|
||||||
IconCode,
|
IconCode,
|
||||||
@ -56,6 +57,7 @@ export {
|
|||||||
IconDoorEnter,
|
IconDoorEnter,
|
||||||
IconDotsVertical,
|
IconDotsVertical,
|
||||||
IconDownload,
|
IconDownload,
|
||||||
|
IconEditCircle,
|
||||||
IconEye,
|
IconEye,
|
||||||
IconEyeOff,
|
IconEyeOff,
|
||||||
IconFile,
|
IconFile,
|
||||||
@ -66,6 +68,7 @@ export {
|
|||||||
IconFileUpload,
|
IconFileUpload,
|
||||||
IconFileZip,
|
IconFileZip,
|
||||||
IconFilterOff,
|
IconFilterOff,
|
||||||
|
IconFocusCentered,
|
||||||
IconForbid,
|
IconForbid,
|
||||||
IconGripVertical,
|
IconGripVertical,
|
||||||
IconH1,
|
IconH1,
|
||||||
|
|||||||
53
packages/twenty-front/src/testing/mock-data/events.ts
Normal file
53
packages/twenty-front/src/testing/mock-data/events.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { Event } from '@/activities/events/types/Event';
|
||||||
|
|
||||||
|
export const mockedEvents: Array<Event> = [
|
||||||
|
{
|
||||||
|
properties: '{"diff": {"address": {"after": "TEST", "before": ""}}}',
|
||||||
|
updatedAt: '2023-04-26T10:12:42.33625+00:00',
|
||||||
|
id: '79f84835-b2f9-4ab5-8ab9-17dbcc45dda3',
|
||||||
|
personId: null,
|
||||||
|
companyId: 'ce40eca0-8f4b-4bba-ba91-5cbd870c64d0',
|
||||||
|
name: 'updated.company',
|
||||||
|
opportunityId: null,
|
||||||
|
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||||
|
workspaceMember: {
|
||||||
|
__typename: 'WorkspaceMember',
|
||||||
|
id: '20202020-0687-4c41-b707-ed1bfca972a7',
|
||||||
|
avatarUrl: '',
|
||||||
|
locale: 'en',
|
||||||
|
name: {
|
||||||
|
__typename: 'FullName',
|
||||||
|
firstName: 'Tim',
|
||||||
|
lastName: 'Apple',
|
||||||
|
},
|
||||||
|
colorScheme: 'Light',
|
||||||
|
},
|
||||||
|
workspaceMemberId: '20202020-0687-4c41-b707-ed1bfca972a7',
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties:
|
||||||
|
'{"after": {"id": "ce40eca0-8f4b-4bba-ba91-5cbd870c64d0", "name": "", "xLink": {"url": "", "label": ""}, "events": {"edges": [], "__typename": "eventConnection"}, "people": {"edges": [], "__typename": "personConnection"}, "address": "", "position": 0.5, "createdAt": "2024-03-24T21:33:45.765295", "employees": null, "favorites": {"edges": [], "__typename": "favoriteConnection"}, "updatedAt": "2024-03-24T21:33:45.765295", "__typename": "company", "domainName": "", "attachments": {"edges": [], "__typename": "attachmentConnection"}, "accountOwner": null, "linkedinLink": {"url": "", "label": ""}, "opportunities": {"edges": [], "__typename": "opportunityConnection"}, "accountOwnerId": null, "activityTargets": {"edges": [], "__typename": "activityTargetConnection"}, "idealCustomerProfile": false, "annualRecurringRevenue": {"amountMicros": null, "currencyCode": ""}}}',
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
id: '1ad72a42-6ab4-4474-a62a-a57cae3c0298',
|
||||||
|
personId: null,
|
||||||
|
companyId: 'ce40eca0-8f4b-4bba-ba91-5cbd870c64d0',
|
||||||
|
name: 'created.company',
|
||||||
|
opportunityId: null,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
workspaceMember: {
|
||||||
|
__typename: 'WorkspaceMember',
|
||||||
|
id: '20202020-0687-4c41-b707-ed1bfca972a7',
|
||||||
|
avatarUrl: '',
|
||||||
|
locale: 'en',
|
||||||
|
name: {
|
||||||
|
__typename: 'FullName',
|
||||||
|
firstName: 'Tim',
|
||||||
|
lastName: 'Apple',
|
||||||
|
},
|
||||||
|
colorScheme: 'Light',
|
||||||
|
},
|
||||||
|
workspaceMemberId: '20202020-0687-4c41-b707-ed1bfca972a7',
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
Reference in New Issue
Block a user