From 8c5f359748600a3b5d5acb6dea68d1ce3bfdd0b9 Mon Sep 17 00:00:00 2001 From: AKILIMAILI CIZUNGU Innocent <51681130+Innocent-Akim@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:12:08 +0200 Subject: [PATCH 1/3] [Feat] Display all data if no range date (#2763) * feat: display all data if no range date * fix: Select Date Range close By Itself on select one date * fix:DeepScan errors * feat: fix: display data by date range * fix: display data by date range --------- Co-authored-by: Cedric Karungu --- apps/web/app/hooks/features/useDailyPlan.ts | 5 +- apps/web/app/hooks/useDateRange.ts | 11 +++-- apps/web/app/hooks/useFilterDateRange.ts | 18 ++++++- apps/web/app/interfaces/IDailyPlan.ts | 4 +- apps/web/app/stores/daily-plan.ts | 48 ++++++++++++++---- .../features/task/daily-plan/future-tasks.tsx | 14 ++++-- .../features/task/daily-plan/past-tasks.tsx | 12 +++-- .../web/lib/features/task/task-date-range.tsx | 26 +++++++--- apps/web/lib/features/task/task-filters.tsx | 18 +++---- apps/web/lib/features/task/task-status.tsx | 30 ++++++------ apps/web/lib/features/user-profile-plans.tsx | 49 +++++++++++++++---- 11 files changed, 165 insertions(+), 70 deletions(-) diff --git a/apps/web/app/hooks/features/useDailyPlan.ts b/apps/web/app/hooks/features/useDailyPlan.ts index f0bf1de9d..434da0f59 100644 --- a/apps/web/app/hooks/features/useDailyPlan.ts +++ b/apps/web/app/hooks/features/useDailyPlan.ts @@ -33,6 +33,7 @@ type TodayPlanNotificationParams = { canBeSeen: boolean; alreadySeen: boolean; }; +export type FilterTabs = 'Today Tasks' | 'Future Tasks' | 'Past Tasks' | 'All Tasks' | 'Outstanding'; export function useDailyPlan() { const [addTodayPlanTrigger, setAddTodayPlanTrigger] = useState({ @@ -65,6 +66,7 @@ export function useDailyPlan() { const [dailyPlanFetching, setDailyPlanFetching] = useRecoilState(dailyPlanFetchingState); const { firstLoadData: firstLoadDailyPlanData, firstLoad } = useFirstLoad(); + useEffect(() => { if (firstLoad) { setDailyPlanFetching(loading); @@ -76,8 +78,10 @@ export function useDailyPlan() { if (response.data.items.length) { const { items, total } = response.data; setDailyPlan({ items, total }); + } }); + }, [getAllQueryCall, setDailyPlan]); const getMyDailyPlans = useCallback(() => { @@ -294,7 +298,6 @@ export function useDailyPlan() { [...profileDailyPlans.items].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); const currentUser = activeTeam?.members?.find((member) => member.employee.userId === user?.id); - useEffect(() => { getMyDailyPlans(); }, [getMyDailyPlans]); diff --git a/apps/web/app/hooks/useDateRange.ts b/apps/web/app/hooks/useDateRange.ts index 38e3cec61..4c8cfd08e 100644 --- a/apps/web/app/hooks/useDateRange.ts +++ b/apps/web/app/hooks/useDateRange.ts @@ -1,17 +1,18 @@ -import { dateRangeAllPlanState, dateRangeFuturePlanState, dateRangePastPlanState } from "@app/stores"; -import { useRecoilState } from "recoil"; +import { dateRangeAllPlanState, dateRangeFuturePlanState, dateRangePastPlanState, getFirstAndLastDateState } from "@app/stores"; +import { useRecoilState, useRecoilValue } from "recoil"; export const useDateRange = (tab: string | any) => { + const itemsDate = useRecoilValue(getFirstAndLastDateState); const [dateFuture, setDateFuture] = useRecoilState(dateRangeFuturePlanState); const [dateAllPlan, setDateAllPlan] = useRecoilState(dateRangeAllPlanState); const [datePastPlan, setDatePastPlan] = useRecoilState(dateRangePastPlanState); switch (tab) { case 'Future Tasks': - return { date: dateFuture, setDate: setDateFuture }; + return { date: dateFuture, setDate: setDateFuture, data: itemsDate }; case 'Past Tasks': - return { date: datePastPlan, setDate: setDatePastPlan }; + return { date: datePastPlan, setDate: setDatePastPlan, data: itemsDate }; case 'All Tasks': default: - return { date: dateAllPlan, setDate: setDateAllPlan }; + return { date: dateAllPlan, setDate: setDateAllPlan, data: itemsDate }; } } diff --git a/apps/web/app/hooks/useFilterDateRange.ts b/apps/web/app/hooks/useFilterDateRange.ts index 869d201bf..53828b897 100644 --- a/apps/web/app/hooks/useFilterDateRange.ts +++ b/apps/web/app/hooks/useFilterDateRange.ts @@ -1,8 +1,10 @@ 'use client'; +import { isTestDateRange } from '@app/helpers'; import { IDailyPlan } from '@app/interfaces' import { dateRangeAllPlanState, dateRangeFuturePlanState, dateRangePastPlanState, filteredAllPlanDataState, filteredFuturePlanDataState, filteredPastPlanDataState, originalAllPlanState, originalFuturePlanState, originalPastPlanDataState } from '@app/stores'; import { useEffect, useMemo } from 'react'; +import { DateRange } from 'react-day-picker'; import { useRecoilState, useRecoilValue } from 'recoil'; /** *custom filter the data with date range @@ -37,7 +39,6 @@ export function useFilterDateRange(itemsDailyPlan: IDailyPlan[], typeItems?: 'fu // setOriginalAllPlanState(itemsDailyPlan); // } // }, [itemsDailyPlan, dateFuture, datePastPlan, dateAllPlan, typeItems, setOriginalAllPlanState, setOriginalFuturePlanData, setOriginalAllPlanState]); - const updateOriginalPlanData = useMemo(() => (data: IDailyPlan[]) => { switch (typeItems) { case 'future': @@ -58,7 +59,7 @@ export function useFilterDateRange(itemsDailyPlan: IDailyPlan[], typeItems?: 'fu if (!itemsDailyPlan) return; updateOriginalPlanData(itemsDailyPlan); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [updateOriginalPlanData, dateAllPlan, datePastPlan, dateFuture]); + }, [updateOriginalPlanData, dateAllPlan, datePastPlan, dateFuture, setDateAllPlan, setDatePastPlan, setDateFuture]); return { filteredAllPlanData, @@ -72,3 +73,16 @@ export function useFilterDateRange(itemsDailyPlan: IDailyPlan[], typeItems?: 'fu setDatePastPlan, } } + + +export const filterDailyPlan = (date: DateRange, data: IDailyPlan[]) => { + if (!date || !data.length) return data; + const { from, to } = date; + if (!from && !to) { + return data + } + return data.filter((plan) => { + const itemDate = new Date(plan.date); + return isTestDateRange(itemDate, from, to); + }); +} diff --git a/apps/web/app/interfaces/IDailyPlan.ts b/apps/web/app/interfaces/IDailyPlan.ts index 5d91cd89a..2af0ea650 100644 --- a/apps/web/app/interfaces/IDailyPlan.ts +++ b/apps/web/app/interfaces/IDailyPlan.ts @@ -23,11 +23,11 @@ export interface ICreateDailyPlan extends IDailyPlanBase, IRelationnalEmployee { taskId?: ITeamTask['id']; } -export interface IUpdateDailyPlan extends Partial, Pick {} +export interface IUpdateDailyPlan extends Partial, Pick { } export interface IDailyPlanTasksUpdate extends Pick, - IBasePerTenantAndOrganizationEntity {} + IBasePerTenantAndOrganizationEntity { } export enum DailyPlanStatusEnum { OPEN = 'open', diff --git a/apps/web/app/stores/daily-plan.ts b/apps/web/app/stores/daily-plan.ts index e28f6e1df..af57cd9da 100644 --- a/apps/web/app/stores/daily-plan.ts +++ b/apps/web/app/stores/daily-plan.ts @@ -1,12 +1,8 @@ import { atom, RecoilState, selector } from 'recoil'; import { IDailyPlan, PaginationResponse } from '@app/interfaces'; -import { addDays } from 'date-fns'; import { DateRange } from 'react-day-picker'; import { isTestDateRange } from '@app/helpers'; -const today = new Date(); -const oneWeekAgo = new Date(); -oneWeekAgo.setDate(today.getDate() - 7); export const dailyPlanListState = atom>({ key: 'dailyPlanListState', @@ -65,11 +61,20 @@ const createDailyPlanAtom = (key: string | any) => atom({ const createDateRangeAtom = (key: string | any) => atom({ key, - default: { - from: oneWeekAgo, - to: addDays(today, 3), - }, + default: { from: undefined, to: undefined } +}); + +export const dataDailyPlanState = createDailyPlanAtom('originalPlanState'); +const getFirstAndLastDateSelector = (key: string | any) => selector({ + key, + get: ({ get }) => { + const itemsData = get(dataDailyPlanState); + if (!itemsData?.length) return { from: null, to: null }; + const sortedData = itemsData?.slice().sort((a, b) => new Date(a.date)?.getTime() - new Date(b?.date).getTime()); + return { from: new Date(sortedData[0]?.date), to: new Date(sortedData[sortedData.length - 1]?.date) }; + } }); +export const dateRangeDailyPlanState = createDateRangeAtom('dateRangeDailyPlanState') const createFilteredDailyPlanDataSelector = (key: string | any, dateRangeState: RecoilState, originalDataState: RecoilState) => selector({ key, @@ -78,6 +83,28 @@ const createFilteredDailyPlanDataSelector = (key: string | any, dateRangeState: const data = get(originalDataState); if (!dateRange || !data.length) return data; const { from, to } = dateRange; + if (!from && !to) { + return data + } + return data.filter((plan) => { + const itemDate = new Date(plan.date); + return isTestDateRange(itemDate, from, to); + }); + }, +}); +export const dataDailyPlanAllFilterState = createDailyPlanAtom('dataDailyPlanAllFilterState'); +export const dateRangeAllPlanState = createDateRangeAtom('dateRangeAllPlanState'); + +export const setCreateFilteredDailyPlanDataSelector = () => selector({ + key: 'dataDailyPlanAllFilter', + get: ({ get }) => { + const dateRange = get(dateRangeAllPlanState); + const data = get(dataDailyPlanAllFilterState); + if (!dateRange || !data.length) return data; + const { from, to } = dateRange; + if (!from && !to) { + return data + } return data.filter((plan) => { const itemDate = new Date(plan.date); return isTestDateRange(itemDate, from, to); @@ -87,8 +114,9 @@ const createFilteredDailyPlanDataSelector = (key: string | any, dateRangeState: export const dataDailyPlanCountFilterState = createDailyPlanCountFilterAtom('dataDailyPlanCountFilterState'); export const dateRangePastPlanState = createDateRangeAtom('dateRangePastPlanState'); + export const dateRangeFuturePlanState = createDateRangeAtom('dateRangeFuturePlanState'); -export const dateRangeAllPlanState = createDateRangeAtom('dateRangeAllPlanState'); +export const dateRangeLimitState = createDateRangeAtom('startAndEndDateRange'); export const originalFuturePlanState = createDailyPlanAtom('originalFuturePlanState'); export const originalAllPlanState = createDailyPlanAtom('originalAllPlanState'); @@ -111,3 +139,5 @@ export const filteredAllPlanDataState = createFilteredDailyPlanDataSelector( dateRangeAllPlanState, originalAllPlanState ); + +export const getFirstAndLastDateState = getFirstAndLastDateSelector('getFirstAndLastDateState'); diff --git a/apps/web/lib/features/task/daily-plan/future-tasks.tsx b/apps/web/lib/features/task/daily-plan/future-tasks.tsx index d235ca163..ec453f87f 100644 --- a/apps/web/lib/features/task/daily-plan/future-tasks.tsx +++ b/apps/web/lib/features/task/daily-plan/future-tasks.tsx @@ -10,20 +10,24 @@ import { dailyPlanViewHeaderTabs } from '@app/stores/header-tabs'; import TaskBlockCard from '../task-block-card'; import { clsxm } from '@app/utils'; import { HorizontalSeparator } from 'lib/components'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { AlertPopup } from 'lib/components'; -import { useFilterDateRange } from '@app/hooks/useFilterDateRange'; +import { filterDailyPlan } from '@app/hooks/useFilterDateRange'; import { IDailyPlan } from '@app/interfaces'; import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; +import { useDateRange } from '@app/hooks/useDateRange'; export function FutureTasks({ profile }: { profile: any }) { const { deleteDailyPlan, deleteDailyPlanLoading, futurePlans } = useDailyPlan(); const canSeeActivity = useCanSeeActivityScreen(); const [popupOpen, setPopupOpen] = useState(false); - const { filteredFuturePlanData: filteredFuturePlanData } = useFilterDateRange(futurePlans, 'future'); - const [currentDeleteIndex, setCurrentDeleteIndex] = useState(0); - const [futureDailyPlanTasks, setFutureDailyPlanTasks] = useState(filteredFuturePlanData); + const [currentDeleteIndex, setCurrentDeleteIndex] = useState(0); + const { setDate, date } = useDateRange(window.localStorage.getItem('daily-plan-tab')); + const [futureDailyPlanTasks, setFutureDailyPlanTasks] = useState(futurePlans); + useEffect(() => { + setFutureDailyPlanTasks(filterDailyPlan(date as any, futurePlans)) + }, [date, setDate]) const view = useRecoilValue(dailyPlanViewHeaderTabs); return ( diff --git a/apps/web/lib/features/task/daily-plan/past-tasks.tsx b/apps/web/lib/features/task/daily-plan/past-tasks.tsx index 412ef7d05..6fc9a3983 100644 --- a/apps/web/lib/features/task/daily-plan/past-tasks.tsx +++ b/apps/web/lib/features/task/daily-plan/past-tasks.tsx @@ -8,18 +8,22 @@ import { dailyPlanViewHeaderTabs } from '@app/stores/header-tabs'; import { HorizontalSeparator } from 'lib/components'; import { clsxm } from '@app/utils'; import TaskBlockCard from '../task-block-card'; -import { useFilterDateRange } from '@app/hooks/useFilterDateRange'; -import { useState } from 'react'; +import { filterDailyPlan } from '@app/hooks/useFilterDateRange'; +import { useEffect, useState } from 'react'; import { IDailyPlan } from '@app/interfaces'; import { DragDropContext, Draggable, Droppable, DroppableProvided, DroppableStateSnapshot } from 'react-beautiful-dnd'; +import { useDateRange } from '@app/hooks/useDateRange'; export function PastTasks({ profile, currentTab = 'Past Tasks' }: { profile: any; currentTab?: FilterTabs }) { const { pastPlans } = useDailyPlan(); const view = useRecoilValue(dailyPlanViewHeaderTabs); - const { filteredPastPlanData: filteredPastPlanData } = useFilterDateRange(pastPlans, 'past'); - const [pastTasks, setPastTasks] = useState(filteredPastPlanData); + const [pastTasks, setPastTasks] = useState(pastPlans); + const { setDate, date } = useDateRange(window.localStorage.getItem('daily-plan-tab')); + useEffect(() => { + setPastTasks(filterDailyPlan(date as any, pastPlans)) + }, [date, setDate]) return (
{pastTasks?.length > 0 ? ( diff --git a/apps/web/lib/features/task/task-date-range.tsx b/apps/web/lib/features/task/task-date-range.tsx index 1f5c5d243..336816e29 100644 --- a/apps/web/lib/features/task/task-date-range.tsx +++ b/apps/web/lib/features/task/task-date-range.tsx @@ -2,7 +2,7 @@ import React from "react" import { format } from "date-fns" -import { Calendar as CalendarIcon } from "lucide-react" +import { CalendarDays } from "lucide-react" import { cn } from "lib/utils" import { Button } from "@components/ui/button" import { Calendar } from "@components/ui/calendar" @@ -13,34 +13,42 @@ import { } from "@components/ui/popover" import { DateRange } from "react-day-picker" import { SetterOrUpdater } from "recoil" +import moment from "moment" interface ITaskDatePickerWithRange { className?: React.HTMLAttributes, date?: DateRange; onSelect?: SetterOrUpdater; - label?: string + label?: string, + data?: any, } - export function TaskDatePickerWithRange({ className, date, onSelect, - label - + label, + data, }: ITaskDatePickerWithRange) { + const isDateDisabled = (dateToCheck: any) => { + const { from, to }: any = data; + const fromDate = new Date(moment(from)?.format('YYYY-MM-DD')); + const toDate = new Date(moment(to)?.format('YYYY-MM-DD')); + return dateToCheck < fromDate || dateToCheck > toDate; + }; + return (
- + + +
+
+ ); +}; + +export default SortTasksStatusSettings; diff --git a/apps/web/lib/components/modal.tsx b/apps/web/lib/components/modal.tsx index 592e7bed1..c1af3fc71 100644 --- a/apps/web/lib/components/modal.tsx +++ b/apps/web/lib/components/modal.tsx @@ -45,15 +45,16 @@ export function Modal({ className="fixed inset-0 backdrop-brightness-90 backdrop-blur-sm z-[9999] w-full h-full" >
- {title && {title}} {description && {description}}
{children} -
+
diff --git a/apps/web/lib/settings/task-statuses-form.tsx b/apps/web/lib/settings/task-statuses-form.tsx index d94dfabcc..415fceee0 100644 --- a/apps/web/lib/settings/task-statuses-form.tsx +++ b/apps/web/lib/settings/task-statuses-form.tsx @@ -1,11 +1,11 @@ /* eslint-disable no-mixed-spaces-and-tabs */ -import { useRefetchData, useTaskStatus } from '@app/hooks'; +import { useModal, useRefetchData, useTaskStatus } from '@app/hooks'; import { IIcon, ITaskStatusItemList } from '@app/interfaces'; import { userState } from '@app/stores'; import { clsxm } from '@app/utils'; import { Spinner } from '@components/ui/loaders/spinner'; import { PlusIcon } from '@heroicons/react/20/solid'; -import { Button, ColorPicker, InputField, Text } from 'lib/components'; +import { Button, ColorPicker, InputField, Modal, Text } from 'lib/components'; import { useCallback, useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslations } from 'next-intl'; @@ -13,6 +13,7 @@ import { useRecoilState } from 'recoil'; import { generateIconList } from './icon-items'; import IconPopover from './icon-popover'; import { StatusesListCard } from './list-card'; +import SortTasksStatusSettings from '@components/pages/kanban/sort-tasks-status-settings'; type StatusForm = { formOnly?: boolean; @@ -114,9 +115,18 @@ export const TaskStatusesForm = ({ formOnly = false, onCreated }: StatusForm) => }, [edit, createNew, formOnly, editTaskStatus, onCreated, user, reset, createTaskStatus, refetch] ); + const updateArray = taskStatus.slice(); + const sortedArray = + Array.isArray(updateArray) && updateArray.length > 0 + ? updateArray.sort((a: any, b: any) => a.order - b.order) + : []; + const { isOpen, closeModal, openModal } = useModal(); return ( <> + + +
@@ -127,20 +137,24 @@ export const TaskStatusesForm = ({ formOnly = false, onCreated }: StatusForm) => )}
- {!createNew && !edit && ( - + )} + - )} - +
{(createNew || edit) && ( <> @@ -202,15 +216,15 @@ export const TaskStatusesForm = ({ formOnly = false, onCreated }: StatusForm) => )} - {!formOnly && taskStatus?.length > 0 && ( + {!formOnly && taskStatus.length > 0 && ( <> {t('pages.settingsTeam.LIST_OF_STATUSES')}
- {loading && !taskStatus?.length && } - {taskStatus && taskStatus?.length ? ( - taskStatus.map((status) => ( + {loading && } + {!loading && sortedArray.length ? ( + sortedArray.map((status) => ( Date: Fri, 19 Jul 2024 10:14:40 +0200 Subject: [PATCH 3/3] fix: remove unecessary paddings / margings on home and team page (#2759) * fix: remove unecessary paddings / margings on home and team page * fix: fix broken views --- apps/web/app/[locale]/page-component.tsx | 109 ++++++++---------- .../app/[locale]/profile/[memberId]/page.tsx | 13 +-- apps/web/lib/layout/main-layout.tsx | 4 +- 3 files changed, 55 insertions(+), 71 deletions(-) diff --git a/apps/web/app/[locale]/page-component.tsx b/apps/web/app/[locale]/page-component.tsx index e1b331bf5..dc4e771ad 100644 --- a/apps/web/app/[locale]/page-component.tsx +++ b/apps/web/app/[locale]/page-component.tsx @@ -7,9 +7,9 @@ import { useOrganizationTeams } from '@app/hooks'; import { clsxm } from '@app/utils'; import NoTeam from '@components/pages/main/no-team'; import { withAuthentication } from 'lib/app/authenticator'; -import { Breadcrumb, Card, Divider } from 'lib/components'; +import { Breadcrumb, Card } from 'lib/components'; import { AuthUserTaskInput, TeamInvitations, TeamMembers, Timer, UnverifiedEmail } from 'lib/features'; -import { Footer, MainLayout } from 'lib/layout'; +import { MainLayout } from 'lib/layout'; import { IssuesView } from '@app/constants'; import { useNetworkState } from '@uidotdev/usehooks'; import Offline from '@components/pages/offline'; @@ -65,69 +65,56 @@ function MainPage() { return ( <>
-
- - -
- - {/* */} - setHeaderSize(size)} - > -
-
-
-
- - -
-
- -
+ {/*
*/} + + +
+ + {/* */} + setHeaderSize(size)} + > +
+
+
+
+ +
-
- - - - {isTeamMember ? ( - - ) : null} +
+
-
+
+ + + + {isTeamMember ? ( + + ) : null} +
+
- - - - {/* */} - -
{isTeamMember ? : }
-
- -
- -
-
- -
-
+
+ + + {/* */} + +
{isTeamMember ? : }
+
+ +
+
diff --git a/apps/web/app/[locale]/profile/[memberId]/page.tsx b/apps/web/app/[locale]/profile/[memberId]/page.tsx index c44a3d619..e0afccf3c 100644 --- a/apps/web/app/[locale]/profile/[memberId]/page.tsx +++ b/apps/web/app/[locale]/profile/[memberId]/page.tsx @@ -13,7 +13,7 @@ import { TaskFilter, Timer, TimerStatus, UserProfileTask, getTimerStatusValue, u import { MainHeader, MainLayout } from 'lib/layout'; import Link from 'next/link'; import React, { useCallback, useMemo, useState } from 'react'; -import { useTranslations } from 'next-intl' +import { useTranslations } from 'next-intl'; import stc from 'string-to-color'; import { useRecoilValue, useSetRecoilState } from 'recoil'; @@ -105,15 +105,12 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId setHeaderSize(size)} > - + {/* Breadcrumb */}
@@ -141,7 +138,7 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId - + {hook.tab == 'worked' && canSeeActivity && (
diff --git a/apps/web/lib/layout/main-layout.tsx b/apps/web/lib/layout/main-layout.tsx index 6f5ec8c2f..26aba60d5 100644 --- a/apps/web/lib/layout/main-layout.tsx +++ b/apps/web/lib/layout/main-layout.tsx @@ -30,7 +30,7 @@ export function MainLayout({ }: Props) { const fullWidth = useRecoilValue(fullWidthState); return ( -
+