Dropdown button, design improvement
This commit is contained in:
@@ -1941,7 +1941,7 @@ export type GetCommentThreadQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type GetCommentThreadQuery = { __typename?: 'Query', findManyCommentThreads: Array<{ __typename?: 'CommentThread', id: string, createdAt: string, author: { __typename?: 'User', id: string, firstName: string, lastName: string }, comments?: Array<{ __typename?: 'Comment', id: string, body: string, createdAt: string, updatedAt: string, author: { __typename?: 'User', id: string, displayName: string, firstName: string, lastName: string, avatarUrl?: string | null } }> | null, commentThreadTargets?: Array<{ __typename?: 'CommentThreadTarget', commentableId: string, commentableType: CommentableType }> | null }> };
|
||||
export type GetCommentThreadQuery = { __typename?: 'Query', findManyCommentThreads: Array<{ __typename?: 'CommentThread', id: string, createdAt: string, author: { __typename?: 'User', id: string, firstName: string, lastName: string }, comments?: Array<{ __typename?: 'Comment', id: string, body: string, createdAt: string, updatedAt: string, author: { __typename?: 'User', id: string, displayName: string, firstName: string, lastName: string, avatarUrl?: string | null } }> | null, commentThreadTargets?: Array<{ __typename?: 'CommentThreadTarget', id: string, commentableId: string, commentableType: CommentableType }> | null }> };
|
||||
|
||||
export type AddCommentThreadTargetOnCommentThreadMutationVariables = Exact<{
|
||||
commentThreadId: Scalars['String'];
|
||||
@@ -2540,6 +2540,7 @@ export const GetCommentThreadDocument = gql`
|
||||
}
|
||||
}
|
||||
commentThreadTargets {
|
||||
id
|
||||
commentableId
|
||||
commentableType
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { createdCommentThreadIdState } from '@/comments/states/createdCommentThr
|
||||
import CompanyChip from '@/companies/components/CompanyChip';
|
||||
import { GET_COMPANIES } from '@/companies/services';
|
||||
import { GET_PEOPLE } from '@/people/services';
|
||||
import { DropdownButton } from '@/ui/components/buttons/DropdownButton';
|
||||
import { AutosizeTextInput } from '@/ui/components/inputs/AutosizeTextInput';
|
||||
import { PropertyBox } from '@/ui/components/property-box/PropertyBox';
|
||||
import { PropertyBoxItem } from '@/ui/components/property-box/PropertyBoxItem';
|
||||
@@ -26,9 +27,11 @@ import {
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { GET_COMMENT_THREAD } from '../services';
|
||||
import { CommentableEntity } from '../types/CommentableEntity';
|
||||
|
||||
import { CommentThreadItem } from './CommentThreadItem';
|
||||
import { CommentThreadRelationPicker } from './CommentThreadRelationPicker';
|
||||
import { CommentThreadTypeDropdown } from './CommentThreadTypeDropdown';
|
||||
|
||||
import '@blocknote/core/style.css';
|
||||
|
||||
@@ -40,116 +43,74 @@ const StyledContainer = styled.div`
|
||||
|
||||
justify-content: flex-start;
|
||||
|
||||
max-height: calc(100% - 16px);
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledThreadItemListContainer = styled.div`
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
|
||||
flex-direction: column-reverse;
|
||||
gap: ${({ theme }) => theme.spacing(4)};
|
||||
|
||||
justify-content: flex-start;
|
||||
overflow: auto;
|
||||
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const BlockNoteStyledContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export function CommentThreadCreateMode() {
|
||||
const [commentableEntityArray] = useRecoilState(commentableEntityArrayState);
|
||||
const StyledTopContainer = styled.div`
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
padding: 24px;
|
||||
`;
|
||||
|
||||
const [createdCommmentThreadId, setCreatedCommentThreadId] = useRecoilState(
|
||||
createdCommentThreadIdState,
|
||||
);
|
||||
const StyledEditableTitleInput = styled.input`
|
||||
background: transparent;
|
||||
|
||||
const openRightDrawer = useOpenRightDrawer();
|
||||
border: none;
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
display: flex;
|
||||
flex: 1 0 0;
|
||||
|
||||
const [createCommentMutation] = useCreateCommentMutation();
|
||||
flex-direction: column;
|
||||
font-family: Inter;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
justify-content: center;
|
||||
|
||||
const [createCommentThreadWithComment] =
|
||||
useCreateCommentThreadWithCommentMutation();
|
||||
line-height: 120%;
|
||||
outline: none;
|
||||
width: 318px;
|
||||
|
||||
const { data } = useGetCommentThreadQuery({
|
||||
variables: {
|
||||
commentThreadId: createdCommmentThreadId ?? '',
|
||||
},
|
||||
skip: !createdCommmentThreadId,
|
||||
});
|
||||
|
||||
const comments = data?.findManyCommentThreads[0]?.comments;
|
||||
|
||||
const displayCommentList = (comments?.length ?? 0) > 0;
|
||||
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
|
||||
function handleNewComment(commentText: string) {
|
||||
if (!isNonEmptyString(commentText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isDefined(currentUser)) {
|
||||
logError(
|
||||
'In handleCreateCommentThread, currentUser is not defined, this should not happen.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
createCommentThreadWithComment({
|
||||
variables: {
|
||||
authorId: currentUser.id,
|
||||
commentText: commentText,
|
||||
commentThreadId: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
commentThreadTargetArray: commentableEntityArray.map(
|
||||
(commentableEntity) => ({
|
||||
commentableId: commentableEntity.id,
|
||||
commentableType: commentableEntity.type,
|
||||
id: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
}),
|
||||
),
|
||||
},
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMPANIES) ?? '',
|
||||
getOperationName(GET_PEOPLE) ?? '',
|
||||
getOperationName(GET_COMMENT_THREAD) ?? '',
|
||||
],
|
||||
onCompleted(data) {
|
||||
setCreatedCommentThreadId(data.createOneCommentThread.id);
|
||||
openRightDrawer('comments');
|
||||
},
|
||||
});
|
||||
:placeholder {
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
}
|
||||
`;
|
||||
|
||||
export function CommentThreadCreateMode({
|
||||
commentableEntityArray,
|
||||
}: {
|
||||
commentableEntityArray: CommentableEntity[];
|
||||
}) {
|
||||
const editor: BlockNoteEditor | null = useBlockNote({
|
||||
theme: 'light',
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
{displayCommentList && (
|
||||
<StyledThreadItemListContainer>
|
||||
{comments?.map((comment) => (
|
||||
<CommentThreadItem key={comment.id} comment={comment} />
|
||||
))}
|
||||
</StyledThreadItemListContainer>
|
||||
)}
|
||||
<PropertyBox>
|
||||
<PropertyBoxItem
|
||||
icon={<IconArrowUpRight />}
|
||||
value={
|
||||
<CommentThreadRelationPicker preselected={commentableEntityArray} />
|
||||
}
|
||||
label="Relations"
|
||||
/>
|
||||
</PropertyBox>
|
||||
<AutosizeTextInput minRows={5} onValidate={handleNewComment} />
|
||||
<StyledTopContainer>
|
||||
<CommentThreadTypeDropdown />
|
||||
<StyledEditableTitleInput placeholder="Note title (optional)" />
|
||||
<PropertyBox>
|
||||
<PropertyBoxItem
|
||||
icon={<IconArrowUpRight />}
|
||||
value={
|
||||
<CommentThreadRelationPicker
|
||||
preselected={commentableEntityArray}
|
||||
/>
|
||||
}
|
||||
label="Relations"
|
||||
/>
|
||||
</PropertyBox>
|
||||
</StyledTopContainer>
|
||||
<BlockNoteStyledContainer>
|
||||
<BlockNoteView editor={editor} />
|
||||
</BlockNoteStyledContainer>
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import { BlockNoteEditor } from '@blocknote/core';
|
||||
import { BlockNoteView, useBlockNote } from '@blocknote/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { PropertyBox } from '@/ui/components/property-box/PropertyBox';
|
||||
import { PropertyBoxItem } from '@/ui/components/property-box/PropertyBoxItem';
|
||||
import { IconArrowUpRight } from '@/ui/icons/index';
|
||||
import { useGetCommentThreadQuery } from '~/generated/graphql';
|
||||
|
||||
import { CommentableEntity } from '../types/CommentableEntity';
|
||||
import { CommentThreadForDrawer } from '../types/CommentThreadForDrawer';
|
||||
|
||||
import { Comments } from './Comments';
|
||||
import { CommentThreadRelationPicker } from './CommentThreadRelationPicker';
|
||||
import { CommentThreadTypeDropdown } from './CommentThreadTypeDropdown';
|
||||
|
||||
import '@blocknote/core/style.css';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(4)};
|
||||
|
||||
justify-content: flex-start;
|
||||
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const BlockNoteStyledContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledTopContainer = styled.div`
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
padding: 24px;
|
||||
`;
|
||||
|
||||
const StyledEditableTitleInput = styled.input`
|
||||
background: transparent;
|
||||
|
||||
border: none;
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
display: flex;
|
||||
flex: 1 0 0;
|
||||
|
||||
flex-direction: column;
|
||||
font-family: Inter;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
justify-content: center;
|
||||
|
||||
line-height: 120%;
|
||||
outline: none;
|
||||
width: 318px;
|
||||
|
||||
:placeholder {
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
}
|
||||
`;
|
||||
|
||||
export function CommentThreadEditMode({
|
||||
commentThreadId,
|
||||
}: {
|
||||
commentThreadId: string;
|
||||
}) {
|
||||
const editor: BlockNoteEditor | null = useBlockNote({
|
||||
theme: 'light',
|
||||
});
|
||||
|
||||
const { data } = useGetCommentThreadQuery({
|
||||
variables: {
|
||||
commentThreadId: commentThreadId ?? '',
|
||||
},
|
||||
skip: !commentThreadId,
|
||||
});
|
||||
|
||||
if (typeof data?.findManyCommentThreads[0] === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const commentThread = data?.findManyCommentThreads[0];
|
||||
|
||||
// TODO : prevent editor from creating loops
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledTopContainer>
|
||||
<CommentThreadTypeDropdown />
|
||||
<StyledEditableTitleInput placeholder="Note title (optional)" />
|
||||
<PropertyBox>
|
||||
<PropertyBoxItem
|
||||
icon={<IconArrowUpRight />}
|
||||
value={
|
||||
<CommentThreadRelationPicker commentThread={commentThread} />
|
||||
}
|
||||
label="Relations"
|
||||
/>
|
||||
</PropertyBox>
|
||||
</StyledTopContainer>
|
||||
<BlockNoteStyledContainer>
|
||||
<BlockNoteView editor={editor} />
|
||||
</BlockNoteStyledContainer>
|
||||
<Comments commentThread={commentThread} />
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import { flatMapAndSortEntityForSelectArrayOfArrayByName } from '@/ui/utils/flat
|
||||
import { getLogoUrlFromDomainName } from '@/utils/utils';
|
||||
import {
|
||||
CommentableType,
|
||||
GetCommentThreadQuery,
|
||||
useSearchCompanyQuery,
|
||||
useSearchPeopleQuery,
|
||||
} from '~/generated/graphql';
|
||||
@@ -29,7 +30,7 @@ import { CommentableEntity } from '../types/CommentableEntity';
|
||||
import { CommentableEntityForSelect } from '../types/CommentableEntityForSelect';
|
||||
|
||||
type OwnProps = {
|
||||
commentThread?: CommentThreadForDrawer;
|
||||
commentThread?: GetCommentThreadQuery['findManyCommentThreads'][0];
|
||||
preselected?: CommentableEntity[];
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
DropdownButton,
|
||||
DropdownOptionType,
|
||||
} from '@/ui/components/buttons/DropdownButton';
|
||||
import { IconNotes, IconPhone } from '@/ui/icons/index';
|
||||
|
||||
export function CommentThreadTypeDropdown() {
|
||||
const options: DropdownOptionType[] = [
|
||||
{ label: 'Notes', icon: <IconNotes /> },
|
||||
// { label: 'Call', icon: <IconPhone /> },
|
||||
];
|
||||
|
||||
const handleSelect = (selectedOption: DropdownOptionType) => {
|
||||
console.log(`You selected: ${selectedOption.label}`);
|
||||
};
|
||||
|
||||
return <DropdownButton options={options} onSelection={handleSelect} />;
|
||||
}
|
||||
+7
-10
@@ -14,7 +14,10 @@ import { IconArrowUpRight } from '@/ui/icons/index';
|
||||
import { logError } from '@/utils/logs/logError';
|
||||
import { isDefined } from '@/utils/type-guards/isDefined';
|
||||
import { isNonEmptyString } from '@/utils/type-guards/isNonEmptyString';
|
||||
import { useCreateCommentMutation } from '~/generated/graphql';
|
||||
import {
|
||||
GetCommentThreadQuery,
|
||||
useCreateCommentMutation,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { GET_COMMENT_THREADS_BY_TARGETS } from '../services';
|
||||
|
||||
@@ -23,7 +26,7 @@ import { CommentThreadItem } from './CommentThreadItem';
|
||||
import { CommentThreadRelationPicker } from './CommentThreadRelationPicker';
|
||||
|
||||
type OwnProps = {
|
||||
commentThread: CommentThreadForDrawer;
|
||||
commentThread: GetCommentThreadQuery['findManyCommentThreads'][0];
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
@@ -36,6 +39,7 @@ const StyledContainer = styled.div`
|
||||
|
||||
justify-content: flex-start;
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledThreadItemListContainer = styled.div`
|
||||
@@ -50,7 +54,7 @@ const StyledThreadItemListContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export function CommentThread({ commentThread }: OwnProps) {
|
||||
export function Comments({ commentThread }: OwnProps) {
|
||||
const [createCommentMutation] = useCreateCommentMutation();
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
|
||||
@@ -89,13 +93,6 @@ export function CommentThread({ commentThread }: OwnProps) {
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<PropertyBox>
|
||||
<PropertyBoxItem
|
||||
icon={<IconArrowUpRight />}
|
||||
value={<CommentThreadRelationPicker commentThread={commentThread} />}
|
||||
label="Relations"
|
||||
/>
|
||||
</PropertyBox>
|
||||
<StyledThreadItemListContainer>
|
||||
{commentThread.comments?.map((comment, index) => (
|
||||
<CommentThreadItem
|
||||
@@ -1,15 +1,83 @@
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { GET_COMPANIES } from '@/companies/services';
|
||||
import { GET_PEOPLE } from '@/people/services';
|
||||
import { RightDrawerBody } from '@/ui/layout/right-drawer/components/RightDrawerBody';
|
||||
import { RightDrawerPage } from '@/ui/layout/right-drawer/components/RightDrawerPage';
|
||||
import { RightDrawerTopBar } from '@/ui/layout/right-drawer/components/RightDrawerTopBar';
|
||||
import { useOpenRightDrawer } from '@/ui/layout/right-drawer/hooks/useOpenRightDrawer';
|
||||
import { logError } from '@/utils/logs/logError';
|
||||
import { isDefined } from '@/utils/type-guards/isDefined';
|
||||
import { isNonEmptyString } from '@/utils/type-guards/isNonEmptyString';
|
||||
import { useCreateCommentThreadWithCommentMutation } from '~/generated/graphql';
|
||||
|
||||
import { GET_COMMENT_THREAD } from '../services';
|
||||
import { commentableEntityArrayState } from '../states/commentableEntityArrayState';
|
||||
|
||||
import { CommentThreadCreateMode } from './CommentThreadCreateMode';
|
||||
|
||||
export function RightDrawerCreateCommentThread() {
|
||||
const [commentableEntityArray] = useRecoilState(commentableEntityArrayState);
|
||||
|
||||
const openRightDrawer = useOpenRightDrawer();
|
||||
|
||||
const [createCommentThreadWithComment] =
|
||||
useCreateCommentThreadWithCommentMutation();
|
||||
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
|
||||
function handleNewCommentThread(commentText: string) {
|
||||
if (!isNonEmptyString(commentText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isDefined(currentUser)) {
|
||||
logError(
|
||||
'In handleCreateCommentThread, currentUser is not defined, this should not happen.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
createCommentThreadWithComment({
|
||||
variables: {
|
||||
authorId: currentUser.id,
|
||||
commentText: commentText,
|
||||
commentThreadId: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
commentThreadTargetArray: commentableEntityArray.map(
|
||||
(commentableEntity) => ({
|
||||
commentableId: commentableEntity.id,
|
||||
commentableType: commentableEntity.type,
|
||||
id: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
}),
|
||||
),
|
||||
},
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMPANIES) ?? '',
|
||||
getOperationName(GET_PEOPLE) ?? '',
|
||||
getOperationName(GET_COMMENT_THREAD) ?? '',
|
||||
],
|
||||
onCompleted(data) {
|
||||
// TODO : redirect to drawer comment thread with data.createOneCommentThread.id
|
||||
openRightDrawer('comments');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<RightDrawerPage>
|
||||
<RightDrawerTopBar title="New note" />
|
||||
<RightDrawerTopBar
|
||||
title="New note"
|
||||
onClick={() => handleNewCommentThread('text')}
|
||||
/>
|
||||
<RightDrawerBody>
|
||||
<CommentThreadCreateMode />
|
||||
<CommentThreadCreateMode
|
||||
commentableEntityArray={commentableEntityArray}
|
||||
/>
|
||||
</RightDrawerBody>
|
||||
</RightDrawerPage>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { RightDrawerBody } from '@/ui/layout/right-drawer/components/RightDrawerBody';
|
||||
import { RightDrawerPage } from '@/ui/layout/right-drawer/components/RightDrawerPage';
|
||||
import { RightDrawerTopBar } from '@/ui/layout/right-drawer/components/RightDrawerTopBar';
|
||||
import { CommentableType } from '~/generated/graphql';
|
||||
|
||||
import { CommentThreadEditMode } from './CommentThreadEditMode';
|
||||
|
||||
export function RightDrawerEditCommentThread() {
|
||||
return (
|
||||
<RightDrawerPage>
|
||||
<RightDrawerTopBar title="" />
|
||||
<RightDrawerBody>
|
||||
<CommentThreadEditMode commentThreadId="twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb400" />
|
||||
</RightDrawerBody>
|
||||
</RightDrawerPage>
|
||||
);
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { CommentThreadForDrawer } from '@/comments/types/CommentThreadForDrawer';
|
||||
import { RightDrawerBody } from '@/ui/layout/right-drawer/components/RightDrawerBody';
|
||||
import { RightDrawerPage } from '@/ui/layout/right-drawer/components/RightDrawerPage';
|
||||
import {
|
||||
SortOrder,
|
||||
useGetCommentThreadsByTargetsQuery,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { CommentableEntity } from '../types/CommentableEntity';
|
||||
|
||||
import { CommentThread } from './CommentThread';
|
||||
|
||||
const StyledTopBar = styled.div`
|
||||
align-items: center;
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 13px;
|
||||
justify-content: space-between;
|
||||
min-height: 40px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
`;
|
||||
|
||||
const StyledTopBarTitle = styled.div`
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
margin-right: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
export function ShowPageComments({
|
||||
commentableEntity,
|
||||
}: {
|
||||
commentableEntity?: CommentableEntity;
|
||||
}) {
|
||||
const { data: queryResult } = useGetCommentThreadsByTargetsQuery({
|
||||
variables: {
|
||||
commentThreadTargetIds: commentableEntity ? [commentableEntity.id] : [],
|
||||
orderBy: [
|
||||
{
|
||||
createdAt: SortOrder.Desc,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const commentThreads: CommentThreadForDrawer[] =
|
||||
queryResult?.findManyCommentThreads ?? [];
|
||||
|
||||
return (
|
||||
<RightDrawerPage>
|
||||
<StyledTopBar>
|
||||
<StyledTopBarTitle>Timeline</StyledTopBarTitle>
|
||||
</StyledTopBar>
|
||||
<RightDrawerBody>
|
||||
{commentThreads.map((commentThread) => (
|
||||
<CommentThread key={commentThread.id} commentThread={commentThread} />
|
||||
))}
|
||||
</RightDrawerBody>
|
||||
</RightDrawerPage>
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { Tooltip } from 'react-tooltip';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconNotes, IconPlus } from '@/ui/icons/index';
|
||||
import { useOpenRightDrawer } from '@/ui/layout/right-drawer/hooks/useOpenRightDrawer';
|
||||
import {
|
||||
beautifyExactDate,
|
||||
beautifyPastDateRelativeToNow,
|
||||
@@ -24,7 +25,7 @@ const StyledTimelineContainer = styled.div`
|
||||
gap: 4px;
|
||||
justify-content: flex-start;
|
||||
overflow-y: auto;
|
||||
padding: 12px 16px 64px 16px;
|
||||
padding: 64px 16px 12px 16px;
|
||||
`;
|
||||
|
||||
const StyledTimelineEmptyContainer = styled.div`
|
||||
@@ -38,6 +39,20 @@ const StyledTimelineEmptyContainer = styled.div`
|
||||
padding: 12px 16px 64px 16px;
|
||||
`;
|
||||
|
||||
const StyledEmptyTimelineTitle = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 120%;
|
||||
`;
|
||||
|
||||
const StyledEmptyTimelineSubTitle = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.extraLight};
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 120%;
|
||||
`;
|
||||
|
||||
const StyledTimelineItemContainer = styled.div`
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
@@ -73,7 +88,6 @@ const StyledItemTitleDate = styled.div`
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
width: 110px;
|
||||
`;
|
||||
|
||||
const StyledVerticalLineContainer = styled.div`
|
||||
@@ -94,6 +108,7 @@ const StyledVerticalLine = styled.div`
|
||||
|
||||
const StyledCardContainer = styled.div`
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
@@ -145,13 +160,13 @@ const StyledTooltip = styled(Tooltip)`
|
||||
padding: 8px;
|
||||
`;
|
||||
|
||||
const StyledBottomActionBar = styled.div`
|
||||
const StyledTopActionBar = styled.div`
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
bottom: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
`;
|
||||
|
||||
export function Timeline({ entity }: { entity: CommentableEntity }) {
|
||||
@@ -166,13 +181,16 @@ export function Timeline({ entity }: { entity: CommentableEntity }) {
|
||||
},
|
||||
});
|
||||
|
||||
const openRightDrawer = useOpenRightDrawer();
|
||||
|
||||
const commentThreads: CommentThreadForDrawer[] =
|
||||
queryResult?.findManyCommentThreads ?? [];
|
||||
|
||||
if (!commentThreads.length) {
|
||||
return (
|
||||
<StyledTimelineEmptyContainer>
|
||||
<h1>No activity yet</h1>
|
||||
<StyledEmptyTimelineTitle>No activity yet</StyledEmptyTimelineTitle>
|
||||
<StyledEmptyTimelineSubTitle>Create one:</StyledEmptyTimelineSubTitle>
|
||||
<TableActionBarButtonCreateCommentThreadCompany />
|
||||
</StyledTimelineEmptyContainer>
|
||||
);
|
||||
@@ -188,6 +206,16 @@ export function Timeline({ entity }: { entity: CommentableEntity }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledTopActionBar>
|
||||
<StyledTimelineItemContainer>
|
||||
<StyledIconContainer>
|
||||
<IconPlus />
|
||||
</StyledIconContainer>
|
||||
|
||||
<TableActionBarButtonCreateCommentThreadCompany />
|
||||
</StyledTimelineItemContainer>
|
||||
</StyledTopActionBar>
|
||||
|
||||
<StyledTimelineItemContainer>
|
||||
<StyledIconContainer>
|
||||
<IconNotes />
|
||||
@@ -214,7 +242,9 @@ export function Timeline({ entity }: { entity: CommentableEntity }) {
|
||||
<StyledVerticalLine></StyledVerticalLine>
|
||||
</StyledVerticalLineContainer>
|
||||
<StyledCardContainer>
|
||||
<StyledCard onClick={() => null}>
|
||||
<StyledCard
|
||||
onClick={() => openRightDrawer('edit-comment-thread')}
|
||||
>
|
||||
<StyledCardTitle>{commentThread.title}</StyledCardTitle>
|
||||
<StyledCardContent>{commentThread.body}</StyledCardContent>
|
||||
</StyledCard>
|
||||
@@ -223,16 +253,6 @@ export function Timeline({ entity }: { entity: CommentableEntity }) {
|
||||
</>
|
||||
);
|
||||
})}
|
||||
|
||||
<StyledBottomActionBar>
|
||||
<StyledTimelineItemContainer>
|
||||
<StyledIconContainer>
|
||||
<IconPlus />
|
||||
</StyledIconContainer>
|
||||
|
||||
<TableActionBarButtonCreateCommentThreadCompany />
|
||||
</StyledTimelineItemContainer>
|
||||
</StyledBottomActionBar>
|
||||
</StyledTimelineContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { v4 } from 'uuid';
|
||||
import { GET_COMPANIES } from '@/companies/services';
|
||||
import { GET_PEOPLE } from '@/people/services';
|
||||
import {
|
||||
GetCommentThreadQuery,
|
||||
useAddCommentThreadTargetOnCommentThreadMutation,
|
||||
useRemoveCommentThreadTargetOnCommentThreadMutation,
|
||||
} from '~/generated/graphql';
|
||||
@@ -15,7 +16,7 @@ import { CommentThreadForDrawer } from '../types/CommentThreadForDrawer';
|
||||
export function useHandleCheckableCommentThreadTargetChange({
|
||||
commentThread,
|
||||
}: {
|
||||
commentThread?: CommentThreadForDrawer;
|
||||
commentThread?: GetCommentThreadQuery['findManyCommentThreads'][0];
|
||||
}) {
|
||||
const [addCommentThreadTargetOnCommentThread] =
|
||||
useAddCommentThreadTargetOnCommentThreadMutation({
|
||||
|
||||
@@ -68,6 +68,7 @@ export const GET_COMMENT_THREAD = gql`
|
||||
}
|
||||
}
|
||||
commentThreadTargets {
|
||||
id
|
||||
commentableId
|
||||
commentableType
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import React, { useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconChevronDown } from '@/ui/icons/index';
|
||||
|
||||
type ButtonProps = React.ComponentProps<'button'>;
|
||||
|
||||
export type DropdownOptionType = {
|
||||
label: string;
|
||||
icon: React.ReactNode;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
options: DropdownOptionType[];
|
||||
onSelection: (value: DropdownOptionType) => void;
|
||||
} & ButtonProps;
|
||||
|
||||
const StyledButton = styled.button<ButtonProps>`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.tertiary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: 4px;
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
gap: 8px;
|
||||
height: 24px;
|
||||
line-height: 150%;
|
||||
padding: 3px 8px;
|
||||
|
||||
svg {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 14px;
|
||||
justify-content: center;
|
||||
width: 14px;
|
||||
}
|
||||
`;
|
||||
|
||||
const DropdownContainer = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const DropdownMenu = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: -2px;
|
||||
position: absolute;
|
||||
`;
|
||||
|
||||
export function DropdownButton({
|
||||
options,
|
||||
onSelection,
|
||||
...buttonProps
|
||||
}: Props) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selectedOption, setSelectedOption] = useState(options[0]);
|
||||
|
||||
if (!options.length) {
|
||||
throw new Error('You must provide at least one option.');
|
||||
}
|
||||
|
||||
const handleSelect =
|
||||
(option: DropdownOptionType) =>
|
||||
(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
onSelection(option);
|
||||
setSelectedOption(option);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownContainer>
|
||||
<StyledButton onClick={() => setIsOpen(!isOpen)} {...buttonProps}>
|
||||
{selectedOption.icon}
|
||||
{selectedOption.label}
|
||||
{options.length > 1 && <IconChevronDown />}
|
||||
</StyledButton>
|
||||
{isOpen && (
|
||||
<DropdownMenu>
|
||||
{options
|
||||
.filter((option) => option.label !== selectedOption.label)
|
||||
.map((option, index) => (
|
||||
<StyledButton key={index} onClick={handleSelect(option)}>
|
||||
{option.icon}
|
||||
{option.label}
|
||||
</StyledButton>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</DropdownContainer>
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,6 @@ const StyledCompanyPropertyBox = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 0px 12px;
|
||||
`;
|
||||
|
||||
const StyledCompanyPropertyBoxContainer = styled.div`
|
||||
|
||||
@@ -12,7 +12,7 @@ import { rightDrawerPageState } from '../states/rightDrawerPageState';
|
||||
import { RightDrawerRouter } from './RightDrawerRouter';
|
||||
|
||||
const ClickableBackground = styled.div`
|
||||
backdrop-filter: blur(1px);
|
||||
//backdrop-filter: blur(1px);
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
@@ -33,6 +33,7 @@ const StyledContainer = styled.div`
|
||||
width: ${({ theme }) => theme.rightDrawerWidth};
|
||||
z-index: 2;
|
||||
`;
|
||||
|
||||
const StyledRightDrawer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@@ -3,5 +3,7 @@ import styled from '@emotion/styled';
|
||||
export const RightDrawerBody = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - 41px); // TODO: improve
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { RightDrawerCreateCommentThread } from '@/comments/components/RightDrawerCreateCommentThread';
|
||||
import { RightDrawerEditCommentThread } from '@/comments/components/RightDrawerEditCommentThread';
|
||||
import { RightDrawerTimeline } from '@/comments/components/RightDrawerTimeline';
|
||||
import { isDefined } from '@/utils/type-guards/isDefined';
|
||||
|
||||
@@ -17,6 +18,8 @@ export function RightDrawerRouter() {
|
||||
<RightDrawerTimeline />
|
||||
) : rightDrawerPage === 'create-comment-thread' ? (
|
||||
<RightDrawerCreateCommentThread />
|
||||
) : rightDrawerPage === 'edit-comment-thread' ? (
|
||||
<RightDrawerEditCommentThread />
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
|
||||
@@ -25,14 +25,19 @@ const StyledTopBarTitle = styled.div`
|
||||
|
||||
export function RightDrawerTopBar({
|
||||
title,
|
||||
onClick,
|
||||
}: {
|
||||
title: string | null | undefined;
|
||||
onClick?: () => void;
|
||||
}) {
|
||||
function handleOnClick() {
|
||||
onClick?.();
|
||||
}
|
||||
return (
|
||||
<StyledRightDrawerTopBar>
|
||||
<RightDrawerTopBarCloseButton />
|
||||
<StyledTopBarTitle>{title}</StyledTopBarTitle>
|
||||
<Button title="Save" />
|
||||
{onClick ? <Button title="Save" onClick={handleOnClick} /> : ''}
|
||||
</StyledRightDrawerTopBar>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
export type RightDrawerPage = 'comments' | 'create-comment-thread';
|
||||
export type RightDrawerPage =
|
||||
| 'comments'
|
||||
| 'create-comment-thread'
|
||||
| 'edit-comment-thread';
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { ShowPageComments } from '@/comments/components/ShowPageComments';
|
||||
import { Timeline } from '@/comments/components/Timeline';
|
||||
import { useCompanyQuery } from '@/companies/services';
|
||||
import { PropertyBox } from '@/ui/components/property-box/PropertyBox';
|
||||
@@ -48,6 +46,10 @@ const StyledRightPanelContainer = styled.div`
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledPropertyBoxContainer = styled.div`
|
||||
padding: 0px 12px;
|
||||
`;
|
||||
|
||||
export function CompanyShow() {
|
||||
const companyId = useParams().companyId ?? '';
|
||||
|
||||
@@ -68,21 +70,23 @@ export function CompanyShow() {
|
||||
title={company?.name ?? ''}
|
||||
date={company?.createdAt ?? ''}
|
||||
/>
|
||||
<PropertyBox>
|
||||
<>
|
||||
<PropertyBoxItem
|
||||
icon={<IconLink />}
|
||||
value={company?.domainName ?? ''}
|
||||
link={
|
||||
company?.domainName ? 'https://' + company?.domainName : ''
|
||||
}
|
||||
/>
|
||||
<PropertyBoxItem
|
||||
icon={<IconMap />}
|
||||
value={company?.address ? company?.address : 'No address'}
|
||||
/>
|
||||
</>
|
||||
</PropertyBox>
|
||||
<StyledPropertyBoxContainer>
|
||||
<PropertyBox>
|
||||
<>
|
||||
<PropertyBoxItem
|
||||
icon={<IconLink />}
|
||||
value={company?.domainName ?? ''}
|
||||
link={
|
||||
company?.domainName ? 'https://' + company?.domainName : ''
|
||||
}
|
||||
/>
|
||||
<PropertyBoxItem
|
||||
icon={<IconMap />}
|
||||
value={company?.address ? company?.address : 'No address'}
|
||||
/>
|
||||
</>
|
||||
</PropertyBox>
|
||||
</StyledPropertyBoxContainer>
|
||||
</StyledLeftPanelContainer>
|
||||
<StyledRightPanelContainer>
|
||||
<Timeline
|
||||
|
||||
Reference in New Issue
Block a user