Migrate to a monorepo structure (#2909)

This commit is contained in:
Charles Bochet
2023-12-10 18:10:54 +01:00
committed by GitHub
parent a70a9281eb
commit 5bdca9de6c
2304 changed files with 37152 additions and 25869 deletions

View File

@ -0,0 +1,128 @@
import { useMemo } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
import { ActivityTargetsInlineCell } from '@/activities/inline-cell/components/ActivityTargetsInlineCell';
import { GraphQLActivity } from '@/activities/types/GraphQLActivity';
import { Note } from '@/activities/types/Note';
import {
FieldContext,
GenericFieldContextType,
} from '@/object-record/field/contexts/FieldContext';
import { IconComment } from '@/ui/display/icon';
const StyledCard = styled.div<{ isSingleNote: boolean }>`
align-items: flex-start;
background: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.md};
display: flex;
flex-direction: column;
height: 300px;
justify-content: space-between;
max-width: ${({ isSingleNote }) => (isSingleNote ? '300px' : 'unset')};
`;
const StyledCardDetailsContainer = styled.div`
align-items: flex-start;
align-self: stretch;
cursor: pointer;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
height: calc(100% - 45px);
justify-content: start;
padding: ${({ theme }) => theme.spacing(2)};
width: calc(100% - ${({ theme }) => theme.spacing(4)});
`;
const StyledNoteTitle = styled.div`
color: ${({ theme }) => theme.font.color.primary};
font-weight: ${({ theme }) => theme.font.weight.medium};
`;
const StyledCardContent = styled.div`
align-self: stretch;
color: ${({ theme }) => theme.font.color.secondary};
line-break: anywhere;
margin-top: ${({ theme }) => theme.spacing(2)};
overflow: hidden;
text-overflow: ellipsis;
white-space: pre-line;
width: 100%;
`;
const StyledFooter = styled.div`
align-items: center;
align-self: stretch;
border-top: 1px solid ${({ theme }) => theme.border.color.light};
color: ${({ theme }) => theme.font.color.primary};
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(1)};
justify-content: center;
padding: ${({ theme }) => theme.spacing(2)};
width: calc(100% - ${({ theme }) => theme.spacing(4)});
`;
const StyledCommentIcon = styled.div`
align-items: center;
color: ${({ theme }) => theme.font.color.light};
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
margin-left: ${({ theme }) => theme.spacing(2)};
`;
export const NoteCard = ({
note,
isSingleNote,
}: {
note: Note;
isSingleNote: boolean;
}) => {
const theme = useTheme();
const openActivityRightDrawer = useOpenActivityRightDrawer();
const noteBody = JSON.parse(note.body ?? '[]');
const body = noteBody.length
? noteBody
.map((x: any) =>
Array.isArray(x.content)
? x.content?.map((content: any) => content?.text).join(' ')
: x.content?.text,
)
.filter((x: string) => x)
.join('\n')
: '';
const fieldContext = useMemo(
() => ({ recoilScopeId: note?.id ?? '' }),
[note?.id],
);
return (
<FieldContext.Provider value={fieldContext as GenericFieldContextType}>
<StyledCard isSingleNote={isSingleNote}>
<StyledCardDetailsContainer
onClick={() => openActivityRightDrawer(note.id)}
>
<StyledNoteTitle>{note.title ?? 'Task Title'}</StyledNoteTitle>
<StyledCardContent>{body}</StyledCardContent>
</StyledCardDetailsContainer>
<StyledFooter>
<ActivityTargetsInlineCell
activity={note as unknown as GraphQLActivity}
/>
{note.comments && note.comments.length > 0 && (
<StyledCommentIcon>
<IconComment size={theme.icon.size.md} />
{note.comments.length}
</StyledCommentIcon>
)}
</StyledFooter>
</StyledCard>
</FieldContext.Provider>
);
};

View File

@ -0,0 +1,72 @@
import { ReactElement } from 'react';
import styled from '@emotion/styled';
import { Note } from '@/activities/types/Note';
import { NoteCard } from './NoteCard';
type NoteListProps = {
title: string;
notes: Note[];
button?: ReactElement | false;
};
const StyledContainer = styled.div`
align-items: flex-start;
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: center;
padding: 8px 24px;
`;
const StyledTitleBar = styled.h3`
display: flex;
justify-content: space-between;
margin-bottom: ${({ theme }) => theme.spacing(4)};
margin-top: ${({ theme }) => theme.spacing(4)};
place-items: center;
width: 100%;
`;
const StyledTitle = styled.h3`
color: ${({ theme }) => theme.font.color.primary};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
`;
const StyledCount = styled.span`
color: ${({ theme }) => theme.font.color.light};
margin-left: ${({ theme }) => theme.spacing(2)};
`;
const StyledNoteContainer = styled.div`
display: grid;
gap: ${({ theme }) => theme.spacing(4)};
grid-auto-rows: 1fr;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
width: 100%;
`;
export const NoteList = ({ title, notes, button }: NoteListProps) => (
<>
{notes && notes.length > 0 && (
<StyledContainer>
<StyledTitleBar>
<StyledTitle>
{title} <StyledCount>{notes.length}</StyledCount>
</StyledTitle>
{button}
</StyledTitleBar>
<StyledNoteContainer>
{notes.map((note) => (
<NoteCard
key={note.id}
note={note}
isSingleNote={notes.length == 1}
/>
))}
</StyledNoteContainer>
</StyledContainer>
)}
</>
);

View File

@ -0,0 +1,94 @@
import styled from '@emotion/styled';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { NoteList } from '@/activities/notes/components/NoteList';
import { useNotes } from '@/activities/notes/hooks/useNotes';
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
import { IconPlus } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
const StyledTaskGroupEmptyContainer = styled.div`
align-items: center;
align-self: stretch;
display: flex;
flex: 1 0 0;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
justify-content: center;
padding-bottom: ${({ theme }) => theme.spacing(16)};
padding-left: ${({ theme }) => theme.spacing(4)};
padding-right: ${({ theme }) => theme.spacing(4)};
padding-top: ${({ theme }) => theme.spacing(3)};
`;
const StyledEmptyTaskGroupTitle = styled.div`
color: ${({ theme }) => theme.font.color.secondary};
font-size: ${({ theme }) => theme.font.size.xxl};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
line-height: ${({ theme }) => theme.text.lineHeight.md};
`;
const StyledEmptyTaskGroupSubTitle = styled.div`
color: ${({ theme }) => theme.font.color.extraLight};
font-size: ${({ theme }) => theme.font.size.xxl};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
line-height: ${({ theme }) => theme.text.lineHeight.md};
margin-bottom: ${({ theme }) => theme.spacing(2)};
`;
const StyledNotesContainer = styled.div`
display: flex;
flex: 1;
flex-direction: column;
height: 100%;
overflow: auto;
`;
export const Notes = ({ entity }: { entity: ActivityTargetableEntity }) => {
const { notes } = useNotes(entity);
const openCreateActivity = useOpenCreateActivityDrawer();
if (notes?.length === 0 && entity.type !== 'Custom') {
return (
<StyledTaskGroupEmptyContainer>
<StyledEmptyTaskGroupTitle>No note yet</StyledEmptyTaskGroupTitle>
<StyledEmptyTaskGroupSubTitle>Create one:</StyledEmptyTaskGroupSubTitle>
<Button
Icon={IconPlus}
title="New note"
variant="secondary"
onClick={() =>
openCreateActivity({
type: 'Note',
targetableEntities: [entity],
})
}
/>
</StyledTaskGroupEmptyContainer>
);
}
return (
<StyledNotesContainer>
<NoteList
title="All"
notes={notes ?? []}
button={
<Button
Icon={IconPlus}
size="small"
variant="secondary"
title="Add note"
onClick={() =>
openCreateActivity({
type: 'Note',
targetableEntities: [entity],
})
}
></Button>
}
/>
</StyledNotesContainer>
);
};

View File

@ -0,0 +1,34 @@
import { Note } from '@/activities/types/Note';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { ActivityTargetableEntity } from '../../types/ActivityTargetableEntity';
export const useNotes = (entity: ActivityTargetableEntity) => {
const { records: activityTargets } = useFindManyRecords({
objectNameSingular: 'activityTarget',
filter: {
[entity.type === 'Company' ? 'companyId' : 'personId']: { eq: entity.id },
},
});
const filter = {
id: {
in: activityTargets?.map((activityTarget) => activityTarget.activityId),
},
type: { eq: 'Note' },
};
const orderBy = {
createdAt: 'AscNullsFirst',
} as any; // TODO: finish typing
const { records: notes } = useFindManyRecords({
skip: !activityTargets?.length,
objectNameSingular: 'activity',
filter,
orderBy,
});
return {
notes: notes as Note[],
};
};