diff --git a/front/src/AppNavbar.tsx b/front/src/AppNavbar.tsx
index 0bdf5e2db..d89ffa8a6 100644
--- a/front/src/AppNavbar.tsx
+++ b/front/src/AppNavbar.tsx
@@ -1,5 +1,6 @@
import { useLocation, useNavigate } from 'react-router-dom';
+import { useCurrentUserTaskCount } from '@/activities/tasks/hooks/useCurrentUserDueTaskCount';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { Favorites } from '@/favorites/components/Favorites';
import { SettingsNavbar } from '@/settings/components/SettingsNavbar';
@@ -26,6 +27,7 @@ export function AppNavbar() {
const navigate = useNavigate();
const isInSubMenu = useIsSubMenuNavbarDisplayed();
+ const { currentUserDueTaskCount } = useCurrentUserTaskCount();
return (
<>
@@ -54,6 +56,7 @@ export function AppNavbar() {
to="/tasks"
active={currentPath === '/tasks'}
Icon={IconCheckbox}
+ count={currentUserDueTaskCount}
/>
diff --git a/front/src/modules/activities/tasks/components/TaskGroups.tsx b/front/src/modules/activities/tasks/components/TaskGroups.tsx
index fcdd37b59..ca010fdf5 100644
--- a/front/src/modules/activities/tasks/components/TaskGroups.tsx
+++ b/front/src/modules/activities/tasks/components/TaskGroups.tsx
@@ -68,7 +68,7 @@ export function TaskGroups({ entity, showAddButton }: OwnProps) {
);
if (
- (activeTabId === 'to-do' &&
+ (activeTabId !== 'done' &&
todayOrPreviousTasks?.length === 0 &&
upcomingTasks?.length === 0 &&
unscheduledTasks?.length === 0) ||
diff --git a/front/src/modules/activities/tasks/hooks/useCurrentUserDueTaskCount.ts b/front/src/modules/activities/tasks/hooks/useCurrentUserDueTaskCount.ts
new file mode 100644
index 000000000..d9d4dcf71
--- /dev/null
+++ b/front/src/modules/activities/tasks/hooks/useCurrentUserDueTaskCount.ts
@@ -0,0 +1,44 @@
+import { DateTime } from 'luxon';
+import { useRecoilState } from 'recoil';
+
+import { currentUserState } from '@/auth/states/currentUserState';
+import { FilterOperand } from '@/ui/view-bar/types/FilterOperand';
+import { turnFilterIntoWhereClause } from '@/ui/view-bar/utils/turnFilterIntoWhereClause';
+import { ActivityType, useGetActivitiesQuery } from '~/generated/graphql';
+import { parseDate } from '~/utils/date-utils';
+
+export function useCurrentUserTaskCount() {
+ const [currentUser] = useRecoilState(currentUserState);
+
+ const { data } = useGetActivitiesQuery({
+ variables: {
+ where: {
+ type: { equals: ActivityType.Task },
+ completedAt: { equals: null },
+ ...(currentUser
+ ? turnFilterIntoWhereClause({
+ key: 'assigneeId',
+ type: 'entity',
+ value: currentUser.id,
+ operand: FilterOperand.Is,
+ displayValue: currentUser.displayName,
+ displayAvatarUrl: currentUser.avatarUrl ?? undefined,
+ })
+ : {}),
+ },
+ },
+ });
+
+ const currentUserDueTaskCount = data?.findManyActivities.filter((task) => {
+ if (!task.dueAt) {
+ return false;
+ }
+ const dueDate = parseDate(task.dueAt).toJSDate();
+ const today = DateTime.now().endOf('day').toJSDate();
+ return dueDate <= today;
+ }).length;
+
+ return {
+ currentUserDueTaskCount,
+ };
+}
diff --git a/front/src/modules/activities/tasks/hooks/useInitializeTasksFilters.ts b/front/src/modules/activities/tasks/hooks/useInitializeTasksFilters.ts
deleted file mode 100644
index 323d25cd1..000000000
--- a/front/src/modules/activities/tasks/hooks/useInitializeTasksFilters.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { useEffect } from 'react';
-
-import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
-import { availableFiltersScopedState } from '@/ui/view-bar/states/availableFiltersScopedState';
-import { FilterDefinition } from '@/ui/view-bar/types/FilterDefinition';
-
-import { TasksRecoilScopeContext } from '../../states/recoil-scope-contexts/TasksRecoilScopeContext';
-
-export function useInitializeTasksFilters({
- availableFilters,
-}: {
- availableFilters: FilterDefinition[];
-}) {
- const [, setAvailableFilters] = useRecoilScopedState(
- availableFiltersScopedState,
- TasksRecoilScopeContext,
- );
-
- useEffect(() => {
- setAvailableFilters(availableFilters);
- }, [setAvailableFilters, availableFilters]);
-}
diff --git a/front/src/modules/activities/tasks/hooks/useTasks.ts b/front/src/modules/activities/tasks/hooks/useTasks.ts
index b3d4f2b12..ac7b43d1b 100644
--- a/front/src/modules/activities/tasks/hooks/useTasks.ts
+++ b/front/src/modules/activities/tasks/hooks/useTasks.ts
@@ -1,47 +1,19 @@
-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 { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
-import { FilterOperand } from '@/ui/view-bar/types/FilterOperand';
import { turnFilterIntoWhereClause } from '@/ui/view-bar/utils/turnFilterIntoWhereClause';
import { ActivityType, useGetActivitiesQuery } from '~/generated/graphql';
-import { tasksFilters } from '~/pages/tasks/tasks-filters';
import { parseDate } from '~/utils/date-utils';
export function useTasks(entity?: ActivityTargetableEntity) {
- useInitializeTasksFilters({
- availableFilters: tasksFilters,
- });
-
- const [filters, setFilters] = useRecoilScopedState(
+ const [filters] = useRecoilScopedState(
filtersScopedState,
TasksRecoilScopeContext,
);
- // If there is no filter, we set the default filter to the current user
- const [currentUser] = useRecoilState(currentUserState);
-
- useEffect(() => {
- if (currentUser && !filters.length && !entity) {
- setFilters([
- {
- key: 'assigneeId',
- type: 'entity',
- value: currentUser.id,
- operand: FilterOperand.Is,
- displayValue: currentUser.displayName,
- displayAvatarUrl: currentUser.avatarUrl ?? undefined,
- },
- ]);
- }
- }, [currentUser, filters, setFilters, entity]);
-
const whereFilters = entity
? {
activityTargets: {
@@ -68,6 +40,7 @@ export function useTasks(entity?: ActivityTargetableEntity) {
...whereFilters,
},
},
+ skip: !entity && filters.length === 0,
});
const { data: incompleteTaskData } = useGetActivitiesQuery({
@@ -78,6 +51,7 @@ export function useTasks(entity?: ActivityTargetableEntity) {
...whereFilters,
},
},
+ skip: !entity && filters.length === 0,
});
const todayOrPreviousTasks = incompleteTaskData?.findManyActivities.filter(
@@ -111,9 +85,9 @@ export function useTasks(entity?: ActivityTargetableEntity) {
const completedTasks = completeTasksData?.findManyActivities;
return {
- todayOrPreviousTasks,
- upcomingTasks,
- unscheduledTasks,
- completedTasks,
+ todayOrPreviousTasks: todayOrPreviousTasks ?? [],
+ upcomingTasks: upcomingTasks ?? [],
+ unscheduledTasks: unscheduledTasks ?? [],
+ completedTasks: completedTasks ?? [],
};
}
diff --git a/front/src/modules/companies/table/components/companies-mock-data.ts b/front/src/modules/companies/table/components/companies-mock-data.ts
index 534f80056..9a1857e33 100644
--- a/front/src/modules/companies/table/components/companies-mock-data.ts
+++ b/front/src/modules/companies/table/components/companies-mock-data.ts
@@ -1,4 +1,4 @@
-import { Company, User } from '../../../../generated/graphql';
+import { Company, Favorite, User } from '../../../../generated/graphql';
type MockedCompany = Pick<
Company,
@@ -25,7 +25,7 @@ type MockedCompany = Pick<
| 'firstName'
| 'lastName'
> | null;
-};
+} & { Favorite: Pick | null };
export const mockedCompaniesData: Array = [
{
@@ -39,6 +39,7 @@ export const mockedCompaniesData: Array = [
createdAt: '2023-04-26T10:08:54.724515+00:00',
address: 'San Francisco, CA',
employees: 5000,
+ Favorite: null,
_activityCount: 0,
accountOwner: {
email: 'charles@test.com',
@@ -62,6 +63,7 @@ export const mockedCompaniesData: Array = [
createdAt: '2023-04-26T10:12:42.33625+00:00',
address: 'Paris, France',
employees: 800,
+ Favorite: null,
_activityCount: 0,
accountOwner: null,
__typename: 'Company',
@@ -77,6 +79,7 @@ export const mockedCompaniesData: Array = [
createdAt: '2023-04-26T10:10:32.530184+00:00',
address: 'San Francisco, CA',
employees: 8000,
+ Favorite: null,
_activityCount: 0,
accountOwner: null,
__typename: 'Company',
@@ -92,6 +95,7 @@ export const mockedCompaniesData: Array = [
createdAt: '2023-03-21T06:30:25.39474+00:00',
address: 'San Francisco, CA',
employees: 800,
+ Favorite: null,
_activityCount: 0,
accountOwner: null,
__typename: 'Company',
@@ -107,6 +111,7 @@ export const mockedCompaniesData: Array = [
createdAt: '2023-04-26T10:13:29.712485+00:00',
address: 'San Francisco, CA',
employees: 400,
+ Favorite: null,
_activityCount: 0,
accountOwner: null,
__typename: 'Company',
diff --git a/front/src/modules/ui/navbar/components/NavItem.tsx b/front/src/modules/ui/navbar/components/NavItem.tsx
index 7c1706965..3159e2fc1 100644
--- a/front/src/modules/ui/navbar/components/NavItem.tsx
+++ b/front/src/modules/ui/navbar/components/NavItem.tsx
@@ -17,6 +17,7 @@ type NavItemProps = {
active?: boolean;
danger?: boolean;
soon?: boolean;
+ count?: number;
};
type StyledItemProps = {
@@ -82,6 +83,21 @@ const StyledSoonPill = styled.div`
padding-right: ${({ theme }) => theme.spacing(2)};
`;
+const StyledItemCount = styled.div`
+ align-items: center;
+ background-color: ${({ theme }) => theme.color.blue};
+ border-radius: ${({ theme }) => theme.border.radius.rounded};
+ color: ${({ theme }) => theme.grayScale.gray0};
+ display: flex;
+ font-size: ${({ theme }) => theme.font.size.xs};
+ font-weight: ${({ theme }) => theme.font.weight.semiBold};
+
+ height: 16px;
+ justify-content: center;
+ margin-left: auto;
+ width: 16px;
+`;
+
function NavItem({
label,
Icon,
@@ -90,6 +106,7 @@ function NavItem({
active,
danger,
soon,
+ count,
}: NavItemProps) {
const theme = useTheme();
const navigate = useNavigate();
@@ -120,6 +137,7 @@ function NavItem({
{Icon && }
{label}
{soon && Soon}
+ {!!count && {count}}
);
}
diff --git a/front/src/pages/tasks/Tasks.tsx b/front/src/pages/tasks/Tasks.tsx
index 78ae9c6c0..103f9fe6b 100644
--- a/front/src/pages/tasks/Tasks.tsx
+++ b/front/src/pages/tasks/Tasks.tsx
@@ -14,6 +14,8 @@ import { TopBar } from '@/ui/top-bar/TopBar';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { FilterDropdownButton } from '@/ui/view-bar/components/FilterDropdownButton';
+import { TasksEffect } from './TasksEffect';
+
const StyledTasksContainer = styled.div`
display: flex;
flex: 1;
@@ -47,6 +49,7 @@ export function Tasks() {
+
diff --git a/front/src/pages/tasks/TasksEffect.tsx b/front/src/pages/tasks/TasksEffect.tsx
new file mode 100644
index 000000000..113c25537
--- /dev/null
+++ b/front/src/pages/tasks/TasksEffect.tsx
@@ -0,0 +1,44 @@
+import { useEffect } from 'react';
+import { useRecoilState } from 'recoil';
+
+import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext';
+import { currentUserState } from '@/auth/states/currentUserState';
+import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
+import { availableFiltersScopedState } from '@/ui/view-bar/states/availableFiltersScopedState';
+import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
+import { FilterOperand } from '@/ui/view-bar/types/FilterOperand';
+
+import { tasksFilters } from './tasks-filters';
+
+export function TasksEffect() {
+ const [currentUser] = useRecoilState(currentUserState);
+ const [, setFilters] = useRecoilScopedState(
+ filtersScopedState,
+ TasksRecoilScopeContext,
+ );
+
+ const [, setAvailableFilters] = useRecoilScopedState(
+ availableFiltersScopedState,
+ TasksRecoilScopeContext,
+ );
+
+ useEffect(() => {
+ setAvailableFilters(tasksFilters);
+ }, [setAvailableFilters]);
+
+ useEffect(() => {
+ if (currentUser) {
+ setFilters([
+ {
+ key: 'assigneeId',
+ type: 'entity',
+ value: currentUser.id,
+ operand: FilterOperand.Is,
+ displayValue: currentUser.displayName,
+ displayAvatarUrl: currentUser.avatarUrl ?? undefined,
+ },
+ ]);
+ }
+ }, [currentUser, setFilters]);
+ return <>>;
+}
diff --git a/front/src/testing/mock-data/activities.ts b/front/src/testing/mock-data/activities.ts
index 867c268ee..b332722cb 100644
--- a/front/src/testing/mock-data/activities.ts
+++ b/front/src/testing/mock-data/activities.ts
@@ -21,20 +21,14 @@ type MockedActivity = Pick<
| 'dueAt'
| 'completedAt'
> & {
- author: {
- __typename?: 'User' | undefined;
- id: string;
- firstName: string;
- lastName: string;
- displayName: string;
- };
- assignee: {
- __typename?: 'User' | undefined;
- id: string;
- firstName: string;
- lastName: string;
- displayName: string;
- };
+ author: Pick<
+ User,
+ 'id' | 'firstName' | 'lastName' | 'displayName' | 'avatarUrl'
+ >;
+ assignee: Pick<
+ User,
+ 'id' | 'firstName' | 'lastName' | 'displayName' | 'avatarUrl'
+ >;
comments: Array<
Pick & {
author: Pick;
@@ -52,8 +46,8 @@ type MockedActivity = Pick<
| 'companyId'
> & {
activity: Pick;
- person?: Pick;
- company?: Pick;
+ person?: Pick | null;
+ company?: Pick | null;
}
>;
};
@@ -73,12 +67,14 @@ export const mockedTasks: Array = [
firstName: 'Charles',
lastName: 'Test',
displayName: 'Charles Test',
+ avatarUrl: '',
},
assignee: {
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
firstName: 'Charles',
lastName: 'Test',
displayName: 'Charles Test',
+ avatarUrl: '',
},
authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
comments: [],
@@ -102,12 +98,14 @@ export const mockedActivities: Array = [
firstName: 'Charles',
lastName: 'Test',
displayName: 'Charles Test',
+ avatarUrl: '',
},
assignee: {
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
firstName: 'Charles',
lastName: 'Test',
displayName: 'Charles Test',
+ avatarUrl: '',
},
authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
comments: [],
@@ -117,12 +115,13 @@ export const mockedActivities: Array = [
createdAt: '2023-04-26T10:12:42.33625+00:00',
updatedAt: '2023-04-26T10:23:42.33625+00:00',
personId: null,
- companyId: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
+ companyId: '89bb825c-171e-4bcc-9cf7-43448d6fb280',
company: {
- id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
+ id: '89bb825c-171e-4bcc-9cf7-43448d6fb280',
name: 'Airbnb',
domainName: 'airbnb.com',
},
+ person: null,
activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb230',
activity: {
id: '89bb825c-171e-4bcc-9cf7-43448d6fb230',
@@ -142,7 +141,8 @@ export const mockedActivities: Array = [
name: 'Aircall',
domainName: 'aircall.io',
},
- activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb231',
+ person: null,
+ activityId: 'b396e6b9-dc5c-4643-bcff-61b6cf7523ae',
activity: {
id: '89bb825c-171e-4bcc-9cf7-43448d6fb231',
createdAt: new Date().toISOString(),
@@ -154,7 +154,7 @@ export const mockedActivities: Array = [
__typename: 'Activity',
},
{
- id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
+ id: '89bb825c-171e-4bcc-9cf7-43448d6fb278a',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
title: 'Another note',
@@ -167,43 +167,53 @@ export const mockedActivities: Array = [
firstName: 'Charles',
lastName: 'Test',
displayName: 'Charles Test',
+ avatarUrl: '',
},
assignee: {
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
firstName: 'Charles',
lastName: 'Test',
displayName: 'Charles Test',
+ avatarUrl: '',
},
authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
comments: [],
activityTargets: [
{
- id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
+ id: '89bb825c-171e-4bcc-9cf7-43448d6fb278t',
createdAt: '2023-04-26T10:12:42.33625+00:00',
updatedAt: '2023-04-26T10:23:42.33625+00:00',
personId: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b', // Alexandre
person: {
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
displayName: 'Alexandre Test',
+ avatarUrl: '',
},
+ company: null,
companyId: null,
- activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
+ activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb278a',
activity: {
- id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
+ id: '89bb825c-171e-4bcc-9cf7-43448d6fb278a',
createdAt: '2023-04-26T10:12:42.33625+00:00',
updatedAt: '2023-04-26T10:23:42.33625+00:00',
},
__typename: 'ActivityTarget',
},
{
- id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
+ id: '89bb825c-171e-4bcc-9cf7-43448d6fb279t',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
personId: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6d', // Jean d'Eau
companyId: null,
- activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
+ company: null,
+ person: {
+ id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6d',
+ displayName: "Jean d'Eau",
+ avatarUrl: '',
+ },
+ activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb278a',
activity: {
- id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
+ id: '89bb825c-171e-4bcc-9cf7-43448d6fb278a',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
diff --git a/front/src/testing/mock-data/companies.ts b/front/src/testing/mock-data/companies.ts
index 4981fc301..a95384d9b 100644
--- a/front/src/testing/mock-data/companies.ts
+++ b/front/src/testing/mock-data/companies.ts
@@ -1,5 +1,12 @@
import { companiesAvailableColumnDefinitions } from '@/companies/constants/companiesAvailableColumnDefinitions';
-import { Company, User, View, ViewField, ViewType } from '~/generated/graphql';
+import {
+ Company,
+ Favorite,
+ User,
+ View,
+ ViewField,
+ ViewType,
+} from '~/generated/graphql';
type MockedCompany = Pick<
Company,
@@ -26,7 +33,7 @@ type MockedCompany = Pick<
| 'firstName'
| 'lastName'
> | null;
-};
+} & { Favorite: Pick | null };
export const mockedCompaniesData: Array = [
{
@@ -41,6 +48,7 @@ export const mockedCompaniesData: Array = [
annualRecurringRevenue: 500000,
idealCustomerProfile: true,
_activityCount: 1,
+ Favorite: null,
accountOwner: {
email: 'charles@test.com',
displayName: 'Charles Test',
@@ -65,6 +73,7 @@ export const mockedCompaniesData: Array = [
idealCustomerProfile: false,
_activityCount: 1,
accountOwner: null,
+ Favorite: null,
__typename: 'Company',
},
{
@@ -80,6 +89,7 @@ export const mockedCompaniesData: Array = [
idealCustomerProfile: true,
_activityCount: 1,
accountOwner: null,
+ Favorite: null,
__typename: 'Company',
},
{
@@ -95,6 +105,7 @@ export const mockedCompaniesData: Array = [
idealCustomerProfile: false,
_activityCount: 0,
accountOwner: null,
+ Favorite: null,
__typename: 'Company',
},
{
@@ -110,6 +121,7 @@ export const mockedCompaniesData: Array = [
idealCustomerProfile: false,
_activityCount: 2,
accountOwner: null,
+ Favorite: null,
__typename: 'Company',
},
{
@@ -125,6 +137,7 @@ export const mockedCompaniesData: Array = [
idealCustomerProfile: true,
_activityCount: 13,
accountOwner: null,
+ Favorite: null,
__typename: 'Company',
},
{
@@ -140,6 +153,7 @@ export const mockedCompaniesData: Array = [
idealCustomerProfile: true,
_activityCount: 1,
accountOwner: null,
+ Favorite: null,
__typename: 'Company',
},
];
diff --git a/play.md b/play.md
new file mode 100644
index 000000000..7decace75
--- /dev/null
+++ b/play.md
@@ -0,0 +1,8 @@
+What does this mean? And what does `WorkspaceMember` look like?
+
+```prisma
+model User {
+ /// @TypeGraphQL.omit(input: true)
+ workspaceMember WorkspaceMember?
+}
+```
\ No newline at end of file