diff --git a/front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts b/front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts index 5ea0d41285..26ecc523c5 100644 --- a/front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts +++ b/front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts @@ -37,7 +37,6 @@ export function useOpenCreateActivityDrawer() { entities?: ActivityTargetableEntity[], ) { const now = new Date().toISOString(); - return createActivityMutation({ variables: { data: { diff --git a/front/src/modules/activities/notes/components/NoteCard.tsx b/front/src/modules/activities/notes/components/NoteCard.tsx new file mode 100644 index 0000000000..b233b00061 --- /dev/null +++ b/front/src/modules/activities/notes/components/NoteCard.tsx @@ -0,0 +1,87 @@ +import styled from '@emotion/styled'; + +import { ActivityRelationEditableField } from '@/activities/editable-fields/components/ActivityRelationEditableField'; +import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer'; +import { Activity, ActivityTarget } from '~/generated/graphql'; + +const StyledCard = styled.div` + 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; +`; + +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; + 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: column; + gap: ${({ theme }) => theme.spacing(1)}; + justify-content: center; + padding: ${({ theme }) => theme.spacing(2)}; + width: calc(100% - ${({ theme }) => theme.spacing(4)}); +`; + +export function NoteCard({ + note, +}: { + note: Pick< + Activity, + 'id' | 'title' | 'body' | 'type' | 'completedAt' | 'dueAt' + > & { + activityTargets?: Array> | null; + }; +}) { + const openActivityRightDrawer = useOpenActivityRightDrawer(); + const body = JSON.parse(note.body ?? '{}')[0] + ?.content.map((x: any) => x.text) + .join('\n'); + + return ( + + openActivityRightDrawer(note.id)} + > + {note.title ?? 'Task Title'} + {body} + + + + + + ); +} diff --git a/front/src/modules/activities/notes/components/NoteList.tsx b/front/src/modules/activities/notes/components/NoteList.tsx new file mode 100644 index 0000000000..d6a9ab714a --- /dev/null +++ b/front/src/modules/activities/notes/components/NoteList.tsx @@ -0,0 +1,70 @@ +import { ReactElement } from 'react'; +import styled from '@emotion/styled'; + +import { NoteForList } from '../../types/NoteForList'; + +import { NoteCard } from './NoteCard'; + +type OwnProps = { + title: string; + notes: NoteForList[]; + 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 function NoteList({ title, notes, button }: OwnProps) { + return ( + <> + {notes && notes.length > 0 && ( + + + + {title} {notes.length} + + {button} + + + {notes.map((note) => ( + + ))} + + + )} + + ); +} diff --git a/front/src/modules/activities/notes/components/Notes.tsx b/front/src/modules/activities/notes/components/Notes.tsx new file mode 100644 index 0000000000..392542014e --- /dev/null +++ b/front/src/modules/activities/notes/components/Notes.tsx @@ -0,0 +1,91 @@ +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import { IconNotes } from '@tabler/icons-react'; + +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 { + Button, + ButtonSize, + ButtonVariant, +} from '@/ui/button/components/Button'; +import { ActivityType } from '~/generated/graphql'; + +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 function Notes({ entity }: { entity: ActivityTargetableEntity }) { + const { notes } = useNotes(entity); + const theme = useTheme(); + + const openCreateActivity = useOpenCreateActivityDrawer(); + + if (notes?.length === 0) { + return ( + + No note yet + Create one: + + } + /> + + ); +} diff --git a/front/src/modules/activities/notes/hooks/useNotes.ts b/front/src/modules/activities/notes/hooks/useNotes.ts new file mode 100644 index 0000000000..db8533697b --- /dev/null +++ b/front/src/modules/activities/notes/hooks/useNotes.ts @@ -0,0 +1,27 @@ +import { ActivityType, useGetActivitiesQuery } from '~/generated/graphql'; + +import { ActivityTargetableEntity } from '../../types/ActivityTargetableEntity'; + +export function useNotes(entity: ActivityTargetableEntity) { + const { data: notesData } = useGetActivitiesQuery({ + variables: { + where: { + type: { equals: ActivityType.Note }, + activityTargets: { + some: { + OR: [ + { companyId: { equals: entity.id } }, + { personId: { equals: entity.id } }, + ], + }, + }, + }, + }, + }); + + const notes = notesData?.findManyActivities; + + return { + notes, + }; +} diff --git a/front/src/modules/activities/components/__stories__/TaskGroups.stories.tsx b/front/src/modules/activities/tasks/__stories__/TaskGroups.stories.tsx similarity index 91% rename from front/src/modules/activities/components/__stories__/TaskGroups.stories.tsx rename to front/src/modules/activities/tasks/__stories__/TaskGroups.stories.tsx index 688bce83cc..64a8428bdc 100644 --- a/front/src/modules/activities/components/__stories__/TaskGroups.stories.tsx +++ b/front/src/modules/activities/tasks/__stories__/TaskGroups.stories.tsx @@ -1,12 +1,11 @@ import type { Meta, StoryObj } from '@storybook/react'; import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext'; +import { TaskGroups } from '@/activities/tasks/components/TaskGroups'; import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator'; import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { TaskGroups } from '../TaskGroups'; - const meta: Meta = { title: 'Modules/Activity/TaskGroups', component: TaskGroups, diff --git a/front/src/modules/activities/components/__stories__/TaskGroupsWithoutTasks.stories.tsx b/front/src/modules/activities/tasks/__stories__/TaskGroupsWithoutTasks.stories.tsx similarity index 95% rename from front/src/modules/activities/components/__stories__/TaskGroupsWithoutTasks.stories.tsx rename to front/src/modules/activities/tasks/__stories__/TaskGroupsWithoutTasks.stories.tsx index e4b314df45..7975ebc96c 100644 --- a/front/src/modules/activities/components/__stories__/TaskGroupsWithoutTasks.stories.tsx +++ b/front/src/modules/activities/tasks/__stories__/TaskGroupsWithoutTasks.stories.tsx @@ -4,12 +4,11 @@ import { graphql } from 'msw'; import { GET_ACTIVITIES } from '@/activities/graphql/queries/getActivities'; import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext'; +import { TaskGroups } from '@/activities/tasks/components/TaskGroups'; import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator'; import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { TaskGroups } from '../TaskGroups'; - const meta: Meta = { title: 'Modules/Activity/TaskGroupsWithoutTasks', component: TaskGroups, diff --git a/front/src/modules/activities/components/__stories__/TaskList.stories.tsx b/front/src/modules/activities/tasks/__stories__/TaskList.stories.tsx similarity index 92% rename from front/src/modules/activities/components/__stories__/TaskList.stories.tsx rename to front/src/modules/activities/tasks/__stories__/TaskList.stories.tsx index 295f74d621..1787248616 100644 --- a/front/src/modules/activities/components/__stories__/TaskList.stories.tsx +++ b/front/src/modules/activities/tasks/__stories__/TaskList.stories.tsx @@ -1,12 +1,11 @@ import { MemoryRouter } from 'react-router-dom'; import type { Meta, StoryObj } from '@storybook/react'; +import { TaskList } from '@/activities/tasks/components/TaskList'; import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; import { mockedActivities } from '~/testing/mock-data/activities'; -import { TaskList } from '../TaskList'; - const meta: Meta = { title: 'Modules/Activity/TaskList', component: TaskList, diff --git a/front/src/modules/activities/tasks/components/AddTaskButton.tsx b/front/src/modules/activities/tasks/components/AddTaskButton.tsx new file mode 100644 index 0000000000..6405df62c2 --- /dev/null +++ b/front/src/modules/activities/tasks/components/AddTaskButton.tsx @@ -0,0 +1,34 @@ +import { useTheme } from '@emotion/react'; + +import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer'; +import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity'; +import { + Button, + ButtonSize, + ButtonVariant, +} from '@/ui/button/components/Button'; +import { IconPlus } from '@/ui/icon'; +import { ActivityType } from '~/generated/graphql'; + +export function AddTaskButton({ + entity, +}: { + entity?: ActivityTargetableEntity; +}) { + const theme = useTheme(); + const openCreateActivity = useOpenCreateActivityDrawer(); + + if (!entity) { + return <>; + } + + return ( + + ); +} diff --git a/front/src/modules/activities/tasks/components/EntityTasks.tsx b/front/src/modules/activities/tasks/components/EntityTasks.tsx new file mode 100644 index 0000000000..d7854a33de --- /dev/null +++ b/front/src/modules/activities/tasks/components/EntityTasks.tsx @@ -0,0 +1,24 @@ +import styled from '@emotion/styled'; + +import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext'; +import { TaskGroups } from '@/activities/tasks/components/TaskGroups'; +import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity'; +import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; + +const StyledContainer = styled.div` + display: flex; + flex: 1; + flex-direction: column; + height: 100%; + overflow: auto; +`; + +export function EntityTasks({ entity }: { entity: ActivityTargetableEntity }) { + return ( + + + + + + ); +} diff --git a/front/src/modules/activities/components/TaskGroups.tsx b/front/src/modules/activities/tasks/components/TaskGroups.tsx similarity index 64% rename from front/src/modules/activities/components/TaskGroups.tsx rename to front/src/modules/activities/tasks/components/TaskGroups.tsx index d94ef1fd07..4d17f5a1ac 100644 --- a/front/src/modules/activities/components/TaskGroups.tsx +++ b/front/src/modules/activities/tasks/components/TaskGroups.tsx @@ -2,14 +2,20 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconCheckbox } from '@tabler/icons-react'; +import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer'; +import { useTasks } from '@/activities/tasks/hooks/useTasks'; +import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity'; import { Button, ButtonVariant } from '@/ui/button/components/Button'; import { ActivityType } from '~/generated/graphql'; -import { useOpenCreateActivityDrawer } from '../hooks/useOpenCreateActivityDrawer'; -import { useTasks } from '../hooks/useTasks'; - +import { AddTaskButton } from './AddTaskButton'; import { TaskList } from './TaskList'; +type OwnProps = { + entity?: ActivityTargetableEntity; + showAddButton?: boolean; +}; + const StyledTaskGroupEmptyContainer = styled.div` align-items: center; align-self: stretch; @@ -44,8 +50,9 @@ const StyledContainer = styled.div` flex-direction: column; `; -export function TaskGroups() { - const { todayOrPreviousTasks, upcomingTasks, unscheduledTasks } = useTasks(); +export function TaskGroups({ entity, showAddButton }: OwnProps) { + const { todayOrPreviousTasks, upcomingTasks, unscheduledTasks } = + useTasks(entity); const theme = useTheme(); const openCreateActivity = useOpenCreateActivityDrawer(); @@ -63,7 +70,9 @@ export function TaskGroups() { icon={} title="New task" variant={ButtonVariant.Secondary} - onClick={() => openCreateActivity(ActivityType.Task)} + onClick={() => + openCreateActivity(ActivityType.Task, entity ? [entity] : undefined) + } /> ); @@ -71,9 +80,28 @@ export function TaskGroups() { return ( - - - + } + /> + + } + /> + + } + /> ); } diff --git a/front/src/modules/activities/components/TaskList.tsx b/front/src/modules/activities/tasks/components/TaskList.tsx similarity index 70% rename from front/src/modules/activities/components/TaskList.tsx rename to front/src/modules/activities/tasks/components/TaskList.tsx index 8d97f0d0f3..c945f61952 100644 --- a/front/src/modules/activities/components/TaskList.tsx +++ b/front/src/modules/activities/tasks/components/TaskList.tsx @@ -1,12 +1,14 @@ +import { ReactElement } from 'react'; import styled from '@emotion/styled'; -import { TaskForList } from '../types/TaskForList'; +import { TaskForList } from '@/activities/types/TaskForList'; import { TaskRow } from './TaskRow'; type OwnProps = { title: string; tasks: TaskForList[]; + button?: ReactElement | false; }; const StyledContainer = styled.div` @@ -18,11 +20,18 @@ const StyledContainer = styled.div` 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}; - margin-bottom: ${({ theme }) => theme.spacing(4)}; - margin-top: ${({ theme }) => theme.spacing(4)}; `; const StyledCount = styled.span` @@ -37,14 +46,17 @@ const StyledTaskRows = styled.div` width: 100%; `; -export function TaskList({ title, tasks }: OwnProps) { +export function TaskList({ title, tasks, button }: OwnProps) { return ( <> {tasks && tasks.length > 0 && ( - - {title} {tasks.length} - + + + {title} {tasks.length} + + {button} + {tasks.map((task) => ( diff --git a/front/src/modules/activities/components/TaskRow.tsx b/front/src/modules/activities/tasks/components/TaskRow.tsx similarity index 98% rename from front/src/modules/activities/components/TaskRow.tsx rename to front/src/modules/activities/tasks/components/TaskRow.tsx index 9da962789c..3bf508df77 100644 --- a/front/src/modules/activities/components/TaskRow.tsx +++ b/front/src/modules/activities/tasks/components/TaskRow.tsx @@ -11,8 +11,8 @@ import { import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip'; import { beautifyExactDate, hasDatePassed } from '~/utils/date-utils'; +import { TaskForList } from '../../types/TaskForList'; import { useCompleteTask } from '../hooks/useCompleteTask'; -import { TaskForList } from '../types/TaskForList'; const StyledContainer = styled.div` align-items: center; diff --git a/front/src/modules/activities/hooks/useCompleteTask.ts b/front/src/modules/activities/tasks/hooks/useCompleteTask.ts similarity index 87% rename from front/src/modules/activities/hooks/useCompleteTask.ts rename to front/src/modules/activities/tasks/hooks/useCompleteTask.ts index 2e64597a20..df0315b014 100644 --- a/front/src/modules/activities/hooks/useCompleteTask.ts +++ b/front/src/modules/activities/tasks/hooks/useCompleteTask.ts @@ -4,8 +4,8 @@ import { getOperationName } from '@apollo/client/utilities'; import { Activity, useUpdateActivityMutation } from '~/generated/graphql'; -import { ACTIVITY_UPDATE_FRAGMENT } from '../graphql/fragments/activityUpdateFragment'; -import { GET_ACTIVITIES } from '../graphql/queries/getActivities'; +import { ACTIVITY_UPDATE_FRAGMENT } from '../../graphql/fragments/activityUpdateFragment'; +import { GET_ACTIVITIES } from '../../graphql/queries/getActivities'; type Task = Pick; diff --git a/front/src/modules/activities/hooks/useInitializeTasksFilters.ts b/front/src/modules/activities/tasks/hooks/useInitializeTasksFilters.ts similarity index 86% rename from front/src/modules/activities/hooks/useInitializeTasksFilters.ts rename to front/src/modules/activities/tasks/hooks/useInitializeTasksFilters.ts index e8c83cdbe6..b1993797fe 100644 --- a/front/src/modules/activities/hooks/useInitializeTasksFilters.ts +++ b/front/src/modules/activities/tasks/hooks/useInitializeTasksFilters.ts @@ -4,7 +4,7 @@ import { availableFiltersScopedState } from '@/ui/filter-n-sort/states/available import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; -import { TasksRecoilScopeContext } from '../states/recoil-scope-contexts/TasksRecoilScopeContext'; +import { TasksRecoilScopeContext } from '../../states/recoil-scope-contexts/TasksRecoilScopeContext'; export function useInitializeTasksFilters({ availableFilters, diff --git a/front/src/modules/activities/hooks/useTasks.ts b/front/src/modules/activities/tasks/hooks/useTasks.ts similarity index 74% rename from front/src/modules/activities/hooks/useTasks.ts rename to front/src/modules/activities/tasks/hooks/useTasks.ts index 8a61ba5b95..f070a76a4b 100644 --- a/front/src/modules/activities/hooks/useTasks.ts +++ b/front/src/modules/activities/tasks/hooks/useTasks.ts @@ -2,6 +2,9 @@ import { useEffect } from 'react'; import { DateTime } from 'luxon'; import { useRecoilState } from 'recoil'; +import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext'; +import { useInitializeTasksFilters } from '@/activities/tasks/hooks/useInitializeTasksFilters'; +import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity'; import { currentUserState } from '@/auth/states/currentUserState'; import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState'; import { FilterOperand } from '@/ui/filter-n-sort/types/FilterOperand'; @@ -12,11 +15,7 @@ import { ActivityType, useGetActivitiesQuery } from '~/generated/graphql'; import { tasksFilters } from '~/pages/tasks/tasks-filters'; import { parseDate } from '~/utils/date-utils'; -import { TasksRecoilScopeContext } from '../states/recoil-scope-contexts/TasksRecoilScopeContext'; - -import { useInitializeTasksFilters } from './useInitializeTasksFilters'; - -export function useTasks() { +export function useTasks(entity?: ActivityTargetableEntity) { useInitializeTasksFilters({ availableFilters: tasksFilters, }); @@ -35,7 +34,7 @@ export function useTasks() { const [currentUser] = useRecoilState(currentUserState); useEffect(() => { - if (currentUser && !filters.length) { + if (currentUser && !filters.length && !entity) { setFilters([ { key: 'assigneeId', @@ -47,14 +46,25 @@ export function useTasks() { }, ]); } - }, [currentUser, filters, setFilters]); + }, [currentUser, filters, setFilters, entity]); - const whereFilters = Object.assign( - {}, - ...filters.map((filter) => { - return turnFilterIntoWhereClause(filter); - }), - ); + const whereFilters = entity + ? { + activityTargets: { + some: { + OR: [ + { companyId: { equals: entity.id } }, + { personId: { equals: entity.id } }, + ], + }, + }, + } + : Object.assign( + {}, + ...filters.map((filter) => { + return turnFilterIntoWhereClause(filter); + }), + ); const { data: completeTasksData } = useGetActivitiesQuery({ variables: { @@ -77,7 +87,7 @@ export function useTasks() { }); const tasksData = - activeTabId === 'done' ? completeTasksData : incompleteTaskData; + activeTabId === 'done' || !entity ? completeTasksData : incompleteTaskData; const todayOrPreviousTasks = tasksData?.findManyActivities.filter((task) => { if (!task.dueAt) { diff --git a/front/src/modules/activities/timeline/components/Timeline.tsx b/front/src/modules/activities/timeline/components/Timeline.tsx index edccddd3ac..235618aef2 100644 --- a/front/src/modules/activities/timeline/components/Timeline.tsx +++ b/front/src/modules/activities/timeline/components/Timeline.tsx @@ -51,24 +51,6 @@ const StyledEmptyTimelineSubTitle = styled.div` margin-bottom: ${({ theme }) => theme.spacing(2)}; `; -const StyledTopActionBar = styled.div` - align-items: flex-start; - align-self: stretch; - backdrop-filter: ${() => (useIsMobile() ? 'none' : `blur(5px)`)}; - - border-bottom: ${({ theme }) => - useIsMobile() ? 'none' : `1px solid ${theme.border.color.medium}`}; - - border-top-right-radius: ${() => (useIsMobile() ? 'none' : `8px`)}; - - display: flex; - flex-direction: column; - left: 0px; - padding: 12px 16px 12px 16px; - position: ${() => (useIsMobile() ? 'relative' : 'sticky')}; - top: 0px; -`; - export function Timeline({ entity }: { entity: ActivityTargetableEntity }) { const { data: queryResult, loading } = useGetActivitiesByTargetsQuery({ variables: { @@ -104,12 +86,6 @@ export function Timeline({ entity }: { entity: ActivityTargetableEntity }) { return ( - - openCreateActivity(ActivityType.Note, [entity])} - onTaskClick={() => openCreateActivity(ActivityType.Task, [entity])} - /> - ); diff --git a/front/src/modules/activities/timeline/components/TimelineActivity.tsx b/front/src/modules/activities/timeline/components/TimelineActivity.tsx index d86e1653e7..ed3bb1dc2b 100644 --- a/front/src/modules/activities/timeline/components/TimelineActivity.tsx +++ b/front/src/modules/activities/timeline/components/TimelineActivity.tsx @@ -1,8 +1,8 @@ import { Tooltip } from 'react-tooltip'; import styled from '@emotion/styled'; -import { useCompleteTask } from '@/activities/hooks/useCompleteTask'; import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer'; +import { useCompleteTask } from '@/activities/tasks/hooks/useCompleteTask'; import { IconNotes } from '@/ui/icon'; import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; diff --git a/front/src/modules/activities/types/NoteForList.ts b/front/src/modules/activities/types/NoteForList.ts new file mode 100644 index 0000000000..f441115c13 --- /dev/null +++ b/front/src/modules/activities/types/NoteForList.ts @@ -0,0 +1,3 @@ +import { GetActivitiesQuery } from '~/generated/graphql'; + +export type NoteForList = GetActivitiesQuery['findManyActivities'][0]; diff --git a/front/src/modules/ui/dropdown/components/DropdownButton.tsx b/front/src/modules/ui/dropdown/components/DropdownButton.tsx index 81cfb94526..79d2a83866 100644 --- a/front/src/modules/ui/dropdown/components/DropdownButton.tsx +++ b/front/src/modules/ui/dropdown/components/DropdownButton.tsx @@ -80,7 +80,7 @@ export function DropdownButton({ dropdownHotkeyScope, dropdownButtonCustomHotkeyScope, ]); - + console.log(dropdownComponents, isDropdownButtonOpen); return ( {hotkey && ( diff --git a/front/src/modules/ui/layout/components/WithTopBarContainer.tsx b/front/src/modules/ui/layout/components/WithTopBarContainer.tsx index 52f6d526d4..23e790b339 100644 --- a/front/src/modules/ui/layout/components/WithTopBarContainer.tsx +++ b/front/src/modules/ui/layout/components/WithTopBarContainer.tsx @@ -14,6 +14,7 @@ type OwnProps = { icon: ReactNode; onAddButtonClick?: () => void; onFavoriteButtonClick?: () => void; + extraButtons?: ReactNode[]; }; const StyledContainer = styled.div` @@ -30,6 +31,7 @@ export function WithTopBarContainer({ icon, onAddButtonClick, onFavoriteButtonClick, + extraButtons, }: OwnProps) { return ( @@ -41,6 +43,7 @@ export function WithTopBarContainer({ icon={icon} onAddButtonClick={onAddButtonClick} onFavoriteButtonClick={onFavoriteButtonClick} + extraButtons={extraButtons} /> {children} diff --git a/front/src/modules/ui/layout/page-bar/components/PageBar.tsx b/front/src/modules/ui/layout/page-bar/components/PageBar.tsx index f9e878f74b..19d909d16b 100644 --- a/front/src/modules/ui/layout/page-bar/components/PageBar.tsx +++ b/front/src/modules/ui/layout/page-bar/components/PageBar.tsx @@ -4,8 +4,10 @@ import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; import { IconButton } from '@/ui/button/components/IconButton'; +import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext'; import { IconChevronLeft, IconHeart, IconPlus } from '@/ui/icon/index'; import NavCollapseButton from '@/ui/navbar/components/NavCollapseButton'; +import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { navbarIconSize } from '../../../navbar/constants'; @@ -70,6 +72,7 @@ type OwnProps = { icon: ReactNode; onAddButtonClick?: () => void; onFavoriteButtonClick?: () => void; + extraButtons?: ReactNode[]; }; export function PageBar({ @@ -79,6 +82,7 @@ export function PageBar({ icon, onAddButtonClick, onFavoriteButtonClick, + extraButtons, }: OwnProps) { const navigate = useNavigate(); const navigateBack = useCallback(() => navigate(-1), [navigate]); @@ -113,28 +117,31 @@ export function PageBar({ - - {onFavoriteButtonClick && ( - } - size="large" - data-testid="add-button" - textColor={isFavorite ? 'danger' : 'secondary'} - onClick={onFavoriteButtonClick} - variant="border" - /> - )} - {onAddButtonClick && ( - } - size="large" - data-testid="add-button" - textColor="secondary" - onClick={onAddButtonClick} - variant="border" - /> - )} - + + + {onFavoriteButtonClick && ( + } + size="large" + data-testid="add-button" + textColor={isFavorite ? 'danger' : 'secondary'} + onClick={onFavoriteButtonClick} + variant="border" + /> + )} + {onAddButtonClick && ( + } + size="large" + data-testid="add-button" + textColor="secondary" + onClick={onAddButtonClick} + variant="border" + /> + )} + {extraButtons} + + ); diff --git a/front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx b/front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx new file mode 100644 index 0000000000..45cbe0be5a --- /dev/null +++ b/front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx @@ -0,0 +1,77 @@ +import styled from '@emotion/styled'; + +import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer'; +import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity'; +import { IconButton } from '@/ui/button/components/IconButton'; +import { DropdownButton } from '@/ui/dropdown/components/DropdownButton'; +import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; +import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'; +import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; +import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton'; +import { IconCheckbox, IconNotes, IconPlus } from '@/ui/icon/index'; +import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; +import { ActivityType } from '~/generated/graphql'; + +const StyledContainer = styled.div` + z-index: 1; +`; + +export function ShowPageAddButton({ + entity, +}: { + entity: ActivityTargetableEntity; +}) { + const { closeDropdownButton, toggleDropdownButton } = useDropdownButton({ + key: 'add-show-page', + }); + const openCreateActivity = useOpenCreateActivityDrawer(); + + function handleSelect(type: ActivityType) { + console.log(type, entity); + openCreateActivity(type, [entity]); + closeDropdownButton(); + } + + return ( + + } + size="large" + data-testid="add-showpage-button" + textColor={'secondary'} + variant="border" + onClick={toggleDropdownButton} + /> + } + dropdownComponents={ + + e.stopPropagation()} + > + handleSelect(ActivityType.Note)} + accent="regular" + > + + Note + + handleSelect(ActivityType.Note)} + accent="regular" + > + + Task + + + + } + dropdownHotkeyScope={{ + scope: RelationPickerHotkeyScope.RelationPicker, + }} + /> + + ); +} diff --git a/front/src/modules/ui/layout/show-page/components/ShowPageRightContainer.tsx b/front/src/modules/ui/layout/show-page/components/ShowPageRightContainer.tsx index 934c6a6d74..d32b451e8e 100644 --- a/front/src/modules/ui/layout/show-page/components/ShowPageRightContainer.tsx +++ b/front/src/modules/ui/layout/show-page/components/ShowPageRightContainer.tsx @@ -1,9 +1,24 @@ -import { ReactElement } from 'react'; +import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import { Notes } from '@/activities/notes/components/Notes'; +import { EntityTasks } from '@/activities/tasks/components/EntityTasks'; +import { Timeline } from '@/activities/timeline/components/Timeline'; +import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity'; +import { + IconCheckbox, + IconMail, + IconNotes, + IconTimelineEvent, +} from '@/ui/icon'; +import { TabList } from '@/ui/tab/components/TabList'; +import { activeTabIdScopedState } from '@/ui/tab/states/activeTabIdScopedState'; +import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; -export const StyledShowPageRightContainer = styled.div` +import { ShowPageRecoilScopeContext } from '../../states/ShowPageRecoilScopeContext'; + +const StyledShowPageRightContainer = styled.div` display: flex; flex: 1 0 0; flex-direction: column; @@ -12,14 +27,75 @@ export const StyledShowPageRightContainer = styled.div` width: calc(100% + 4px); `; -export type ShowPageRightContainerProps = { - children: ReactElement; +const StyledTabListContainer = styled.div` + align-items: end; + border-bottom: ${({ theme }) => `1px solid ${theme.border.color.light}`}; + box-sizing: border-box; + display: flex; + gap: ${({ theme }) => theme.spacing(1)}; + height: 40px; + padding-left: ${({ theme }) => theme.spacing(1)}; + padding-right: ${({ theme }) => theme.spacing(1)}; +`; + +type OwnProps = { + entity: ActivityTargetableEntity; + timeline?: boolean; + tasks?: boolean; + notes?: boolean; + emails?: boolean; }; export function ShowPageRightContainer({ - children, -}: ShowPageRightContainerProps) { + entity, + timeline, + tasks, + notes, + emails, +}: OwnProps) { + const theme = useTheme(); + + const TASK_TABS = [ + { + id: 'timeline', + title: 'Timeline', + icon: , + hide: !timeline, + }, + { + id: 'tasks', + title: 'Tasks', + icon: , + hide: !tasks, + }, + { + id: 'notes', + title: 'Notes', + icon: , + hide: !notes, + }, + { + id: 'emails', + title: 'Emails', + icon: , + hide: !emails, + disabled: true, + }, + ]; + + const [activeTabId] = useRecoilScopedState( + activeTabIdScopedState, + ShowPageRecoilScopeContext, + ); + return ( - {children} + + + + + {activeTabId === 'timeline' && } + {activeTabId === 'tasks' && } + {activeTabId === 'notes' && } + ); } diff --git a/front/src/modules/ui/layout/states/ShowPageRecoilScopeContext.ts b/front/src/modules/ui/layout/states/ShowPageRecoilScopeContext.ts new file mode 100644 index 0000000000..37ea925c06 --- /dev/null +++ b/front/src/modules/ui/layout/states/ShowPageRecoilScopeContext.ts @@ -0,0 +1,3 @@ +import { createContext } from 'react'; + +export const ShowPageRecoilScopeContext = createContext(null); diff --git a/front/src/modules/ui/tab/components/Tab.tsx b/front/src/modules/ui/tab/components/Tab.tsx index f176cb9f08..44e9c19c53 100644 --- a/front/src/modules/ui/tab/components/Tab.tsx +++ b/front/src/modules/ui/tab/components/Tab.tsx @@ -2,26 +2,33 @@ import * as React from 'react'; import styled from '@emotion/styled'; type OwnProps = { + id: string; title: string; icon?: React.ReactNode; active?: boolean; className?: string; onClick?: () => void; + disabled?: boolean; }; -const StyledTab = styled.div<{ active?: boolean }>` +const StyledTab = styled.div<{ active?: boolean; disabled?: boolean }>` align-items: center; border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; border-color: ${({ theme, active }) => active ? theme.border.color.inverted : 'transparent'}; - color: ${({ theme, active }) => - active ? theme.font.color.primary : theme.font.color.secondary}; + color: ${({ theme, active, disabled }) => + active + ? theme.font.color.primary + : disabled + ? theme.font.color.light + : theme.font.color.secondary}; cursor: pointer; display: flex; gap: ${({ theme }) => theme.spacing(1)}; justify-content: center; padding: ${({ theme }) => theme.spacing(2) + ' ' + theme.spacing(2)}; + pointer-events: ${({ disabled }) => (disabled ? 'none' : '')}; `; const StyledHover = styled.span` @@ -38,14 +45,22 @@ const StyledHover = styled.span` `; export function Tab({ + id, title, icon, active = false, onClick, className, + disabled, }: OwnProps) { return ( - + {icon} {title} diff --git a/front/src/modules/ui/tab/components/TabList.tsx b/front/src/modules/ui/tab/components/TabList.tsx index ebfcd26514..0da1a98f93 100644 --- a/front/src/modules/ui/tab/components/TabList.tsx +++ b/front/src/modules/ui/tab/components/TabList.tsx @@ -10,6 +10,8 @@ type SingleTabProps = { title: string; icon?: React.ReactNode; id: string; + hide?: boolean; + disabled?: boolean; }; type OwnProps = { @@ -31,17 +33,21 @@ export function TabList({ tabs, context }: OwnProps) { return ( <> - {tabs.map((tab) => ( - { - setActiveTabId(tab.id); - }} - /> - ))} + {tabs + .filter((tab) => !tab.hide) + .map((tab) => ( + { + setActiveTabId(tab.id); + }} + disabled={tab.disabled} + /> + ))} ); } diff --git a/front/src/modules/ui/tab/components/__stories__/Tab.stories.tsx b/front/src/modules/ui/tab/components/__stories__/Tab.stories.tsx index 08a879ed74..5ed38f72e1 100644 --- a/front/src/modules/ui/tab/components/__stories__/Tab.stories.tsx +++ b/front/src/modules/ui/tab/components/__stories__/Tab.stories.tsx @@ -26,6 +26,7 @@ export const Catalog: Story = { args: { title: 'Tab title', icon: }, argTypes: { active: { control: false }, + disabled: { control: false }, onClick: { control: false }, }, parameters: { @@ -43,6 +44,11 @@ export const Catalog: Story = { values: ['true', 'false'], props: (active: string) => ({ active: active === 'true' }), }, + { + name: 'Disabled', + values: ['true', 'false'], + props: (disabled: string) => ({ disabled: disabled === 'true' }), + }, ], }, }, diff --git a/front/src/modules/ui/tab/components/__stories__/Tablist.stories.tsx b/front/src/modules/ui/tab/components/__stories__/Tablist.stories.tsx new file mode 100644 index 0000000000..cfd8b25b21 --- /dev/null +++ b/front/src/modules/ui/tab/components/__stories__/Tablist.stories.tsx @@ -0,0 +1,69 @@ +import { expect } from '@storybook/jest'; +import type { Meta, StoryObj } from '@storybook/react'; +import { within } from '@storybook/testing-library'; +import { IconCheckbox } from '@tabler/icons-react'; + +import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; +import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; + +import { TabList } from '../TabList'; + +const tabs = [ + { + id: '1', + title: 'Tab1', + icon: , + hide: true, + }, + { + id: '2', + title: 'Tab2', + icon: , + hide: false, + }, + { + id: '3', + title: 'Tab3', + icon: , + hide: false, + disabled: true, + }, + { + id: '4', + title: 'Tab4', + icon: , + hide: false, + disabled: false, + }, +]; + +const meta: Meta = { + title: 'UI/Tab/TabList', + component: TabList, + args: { + tabs: tabs, + }, + decorators: [ + (Story) => ( + + + + ), + ComponentDecorator, + ], +}; + +export default meta; + +type Story = StoryObj; + +export const TabListDisplay: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const submitButton = canvas.queryByText('Tab1'); + expect(submitButton).toBeNull(); + expect(await canvas.findByText('Tab2')).toBeInTheDocument(); + expect(await canvas.findByText('Tab3')).toBeInTheDocument(); + expect(await canvas.findByText('Tab4')).toBeInTheDocument(); + }, +}; diff --git a/front/src/pages/companies/CompanyShow.tsx b/front/src/pages/companies/CompanyShow.tsx index d5346f4317..f6f096d8ef 100644 --- a/front/src/pages/companies/CompanyShow.tsx +++ b/front/src/pages/companies/CompanyShow.tsx @@ -1,7 +1,6 @@ import { useParams } from 'react-router-dom'; import { useTheme } from '@emotion/react'; -import { Timeline } from '@/activities/timeline/components/Timeline'; import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity'; import { CompanyTeam } from '@/companies/components/CompanyTeam'; import { useCompanyQuery } from '@/companies/hooks/useCompanyQuery'; @@ -13,9 +12,12 @@ import { EditableFieldMutationContext } from '@/ui/editable-field/contexts/Edita import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox'; import { IconBuildingSkyscraper } from '@/ui/icon'; import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer'; +import { ShowPageAddButton } from '@/ui/layout/show-page/components/ShowPageAddButton'; import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer'; import { ShowPageRightContainer } from '@/ui/layout/show-page/components/ShowPageRightContainer'; import { ShowPageSummaryCard } from '@/ui/layout/show-page/components/ShowPageSummaryCard'; +import { ShowPageRecoilScopeContext } from '@/ui/layout/states/ShowPageRecoilScopeContext'; +import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { useUpdateOneCompanyMutation } from '~/generated/graphql'; import { getLogoUrlFromDomainName } from '~/utils'; @@ -49,47 +51,60 @@ export function CompanyShow() { isFavorite={isFavorite} icon={} onFavoriteButtonClick={handleFavoriteButtonClick} + extraButtons={[ + , + ]} > - - - ( - - )} - /> - - - - {companyShowFieldDefinition.map((fieldDefinition) => { - return ( - - - - ); - })} - - - - - - - + + + ( + + )} + /> + + + + {companyShowFieldDefinition.map((fieldDefinition) => { + return ( + + + + ); + })} + + + + + + - - + + ); } diff --git a/front/src/pages/companies/__stories__/Company.stories.tsx b/front/src/pages/companies/__stories__/Company.stories.tsx index 497242bb99..67bdf2f2e6 100644 --- a/front/src/pages/companies/__stories__/Company.stories.tsx +++ b/front/src/pages/companies/__stories__/Company.stories.tsx @@ -15,7 +15,7 @@ import { type PageDecoratorArgs, } from '~/testing/decorators/PageDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { mockedActivities } from '~/testing/mock-data/activities'; +import { mockedActivities, mockedTasks } from '~/testing/mock-data/activities'; import { mockedCompaniesData } from '~/testing/mock-data/companies'; import { CompanyShow } from '../CompanyShow'; @@ -37,7 +37,10 @@ const meta: Meta = { (req, res, ctx) => { return res( ctx.data({ - findManyActivities: mockedActivities, + findManyActivities: + req?.variables?.where?.type?.equals === 'Task' + ? mockedTasks + : mockedActivities, }), ); }, @@ -59,7 +62,7 @@ export type Story = StoryObj; export const Default: Story = {}; -export const EditNote: Story = { +export const EditNoteByAddButton: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const firstNoteTitle = await canvas.findByText('My very first note'); @@ -74,6 +77,9 @@ export const EditNote: Story = { expect(await canvas.queryByDisplayValue('My very first note')).toBeNull(); + const addDropdown = await canvas.findByTestId('add-showpage-button'); + await addDropdown.click(); + const noteButton = await canvas.findByText('Note'); await noteButton.click(); @@ -114,3 +120,113 @@ export const EditNote: Story = { ], }, }; + +export const NoteTab: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const noteTab = await canvas.findByTestId('tab-notes'); + await noteTab.click(); + + expect(await canvas.findByText('My very first note')).toBeInTheDocument(); + + const workspaceName = await canvas.findByText('Twenty'); + await fireEvent.click(workspaceName); + + expect(await canvas.queryByDisplayValue('My very first note')).toBeNull(); + + const addButton = await canvas.findByText('Add note'); + await addButton.click(); + + const noteButton = await canvas.findByText('Note'); + await noteButton.click(); + + expect(await canvas.findByText('My very first note')).toBeInTheDocument(); + }, + parameters: { + msw: [ + ...meta.parameters?.msw, + graphql.mutation( + getOperationName(CREATE_ACTIVITY_WITH_COMMENT) ?? '', + (req, res, ctx) => { + return res( + ctx.data({ + createOneActivity: mockedActivities[0], + }), + ); + }, + ), + graphql.query(getOperationName(GET_ACTIVITY) ?? '', (req, res, ctx) => { + return res( + ctx.data({ + findManyActivities: [mockedActivities[0]], + }), + ); + }), + graphql.mutation( + getOperationName(UPDATE_ONE_COMPANY) ?? '', + (req, res, ctx) => { + return res( + ctx.data({ + updateOneCompany: [mockedCompaniesData[0]], + }), + ); + }, + ), + ], + }, +}; + +export const TaskTab: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const taskTab = await canvas.findByTestId('tab-tasks'); + await taskTab.click(); + + expect(await canvas.findByText('My very first task')).toBeInTheDocument(); + + const workspaceName = await canvas.findByText('Twenty'); + await fireEvent.click(workspaceName); + + expect(await canvas.queryByDisplayValue('My very first task')).toBeNull(); + + const addButton = await canvas.findByText('Add task'); + await addButton.click(); + + const taskButton = await canvas.findByText('Task'); + await taskButton.click(); + + expect(await canvas.findByText('My very first task')).toBeInTheDocument(); + }, + parameters: { + msw: [ + ...meta.parameters?.msw, + graphql.mutation( + getOperationName(CREATE_ACTIVITY_WITH_COMMENT) ?? '', + (req, res, ctx) => { + return res( + ctx.data({ + createOneActivity: mockedTasks[0], + }), + ); + }, + ), + graphql.query(getOperationName(GET_ACTIVITY) ?? '', (req, res, ctx) => { + return res( + ctx.data({ + findManyActivities: [mockedTasks[0]], + }), + ); + }), + graphql.mutation( + getOperationName(UPDATE_ONE_COMPANY) ?? '', + (req, res, ctx) => { + return res( + ctx.data({ + updateOneCompany: [mockedCompaniesData[0]], + }), + ); + }, + ), + ], + }, +}; diff --git a/front/src/pages/people/PersonShow.tsx b/front/src/pages/people/PersonShow.tsx index 5babac9395..c1bb0e6872 100644 --- a/front/src/pages/people/PersonShow.tsx +++ b/front/src/pages/people/PersonShow.tsx @@ -2,7 +2,6 @@ import { useParams } from 'react-router-dom'; import { getOperationName } from '@apollo/client/utilities'; import { useTheme } from '@emotion/react'; -import { Timeline } from '@/activities/timeline/components/Timeline'; import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity'; import { useFavorites } from '@/favorites/hooks/useFavorites'; import { GET_PERSON } from '@/people/graphql/queries/getPerson'; @@ -14,9 +13,12 @@ import { EditableFieldMutationContext } from '@/ui/editable-field/contexts/Edita import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox'; import { IconUser } from '@/ui/icon'; import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer'; +import { ShowPageAddButton } from '@/ui/layout/show-page/components/ShowPageAddButton'; import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer'; import { ShowPageRightContainer } from '@/ui/layout/show-page/components/ShowPageRightContainer'; import { ShowPageSummaryCard } from '@/ui/layout/show-page/components/ShowPageSummaryCard'; +import { ShowPageRecoilScopeContext } from '@/ui/layout/states/ShowPageRecoilScopeContext'; +import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { useUpdateOnePersonMutation, useUploadPersonPictureMutation, @@ -67,47 +69,60 @@ export function PersonShow() { hasBackButton isFavorite={isFavorite} onFavoriteButtonClick={handleFavoriteButtonClick} + extraButtons={[ + , + ]} > - - - - person ? : <> - } - onUploadPicture={onUploadPicture} - /> - - - - {personShowFieldDefinition.map((fieldDefinition) => { - return ( - - - - ); - })} - - - - - - + + + + person ? : <> + } + onUploadPicture={onUploadPicture} + /> + + + + {personShowFieldDefinition.map((fieldDefinition) => { + return ( + + + + ); + })} + + + + + - - + + ); } diff --git a/front/src/pages/tasks/Tasks.tsx b/front/src/pages/tasks/Tasks.tsx index 94127c7bf8..90f114662f 100644 --- a/front/src/pages/tasks/Tasks.tsx +++ b/front/src/pages/tasks/Tasks.tsx @@ -1,9 +1,9 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { TaskGroups } from '@/activities/components/TaskGroups'; import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer'; import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext'; +import { TaskGroups } from '@/activities/tasks/components/TaskGroups'; import { FilterDropdownButton } from '@/ui/filter-n-sort/components/FilterDropdownButton'; import { FiltersHotkeyScope } from '@/ui/filter-n-sort/types/FiltersHotkeyScope'; import { IconArchive, IconCheck, IconCheckbox } from '@/ui/icon/index'; diff --git a/front/src/testing/graphqlMocks.ts b/front/src/testing/graphqlMocks.ts index 9b1bc7e7d1..07fc180ce7 100644 --- a/front/src/testing/graphqlMocks.ts +++ b/front/src/testing/graphqlMocks.ts @@ -222,7 +222,10 @@ export const graphqlMocks = [ graphql.query(getOperationName(GET_ACTIVITIES) ?? '', (req, res, ctx) => { return res( ctx.data({ - findManyActivities: mockedActivities, + findManyActivities: + req?.variables?.where?.type?.equals === 'Task' + ? mockedTasks + : mockedActivities, }), ); }),