Skip to content

Commit

Permalink
[Feat]: Daily Plan Compare Estimated LOGIC (#2778)
Browse files Browse the repository at this point in the history
* feat: daily plan compare estimated logic

* fix: cspell error

* fix: cspell error

* feat: daily plan compare estimated logic

* fix: cspell error
  • Loading branch information
Innocent-Akim authored Jul 20, 2024
1 parent 7db14da commit 4973610
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 16 deletions.
24 changes: 20 additions & 4 deletions apps/web/app/[locale]/page-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { withAuthentication } from 'lib/app/authenticator';
import { Breadcrumb, Card } from 'lib/components';
import { AuthUserTaskInput, TeamInvitations, TeamMembers, Timer, UnverifiedEmail } from 'lib/features';
import { MainLayout } from 'lib/layout';
import { IssuesView } from '@app/constants';
import { DAILY_PLAN_SHOW_MODAL, IssuesView } from '@app/constants';
import { useNetworkState } from '@uidotdev/usehooks';
import Offline from '@components/pages/offline';
import { useTranslations } from 'next-intl';
Expand All @@ -35,10 +35,16 @@ import { DailyPlanCompareEstimatedModal } from 'lib/features/daily-plan';

function MainPage() {
const t = useTranslations();
const [isOpen, setIsOpen] = useState(true)
const [isOpen, setIsOpen] = useState(false)
const { todayPlan } = useDailyPlan();
const profile = useUserProfilePage();
const [headerSize, setHeaderSize] = useState(10);
const plan = todayPlan.find((plan) => plan.date?.toString()?.startsWith(new Date()?.toISOString().split('T')[0]));

const defaultOpenPopup =
typeof window !== 'undefined'
? (window.localStorage.getItem(DAILY_PLAN_SHOW_MODAL)) || null
: new Date().toISOString().split('T')[0];

const { isTeamMember, isTrackingEnabled, activeTeam } = useOrganizationTeams();
const [fullWidth, setFullWidth] = useRecoilState(fullWidthState);
Expand All @@ -57,6 +63,14 @@ function MainPage() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [path, setView]);

useEffect(() => {
window.localStorage.setItem(DAILY_PLAN_SHOW_MODAL, new Date().toISOString().split('T')[0]);
if (defaultOpenPopup !== new Date().toISOString().split('T')[0] || defaultOpenPopup === null) {
setIsOpen(true)
}
}, [defaultOpenPopup, plan])


React.useEffect(() => {
window && window?.localStorage.getItem('conf-fullWidth-mode');
setFullWidth(JSON.parse(window?.localStorage.getItem('conf-fullWidth-mode') || 'true'));
Expand All @@ -65,10 +79,12 @@ function MainPage() {
if (!online) {
return <Offline />;
}

return (
<>
<DailyPlanCompareEstimatedModal open={isOpen} closeModal={() => setIsOpen(prev => prev)} todayPlan={todayPlan} profile={profile} />
<DailyPlanCompareEstimatedModal open={isOpen} closeModal={() => setIsOpen((prev) => {
window.localStorage.setItem(DAILY_PLAN_SHOW_MODAL, new Date().toISOString().split('T')[0]);
return !prev;
})} todayPlan={todayPlan} profile={profile} />
<div className="flex flex-col h-screen justify-between">
{/* <div className="flex-grow "> */}
<MainLayout className="h-full" footerClassName={clsxm('')}>
Expand Down
1 change: 1 addition & 0 deletions apps/web/app/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ export const languagesFlags = [
export const LAST_WORSPACE_AND_TEAM = 'last-workspace-and-team';
export const USER_SAW_OUTSTANDING_NOTIFICATION = 'user-saw-notif';
export const TODAY_PLAN_ALERT_SHOWN_DATE = 'last-today-plan-alert-date';
export const DAILY_PLAN_SHOW_MODAL = 'daily-plan-modal'

// OAuth providers keys

Expand Down
29 changes: 29 additions & 0 deletions apps/web/app/helpers/daily-plan-estimated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client"

import { IDailyPlan } from "@app/interfaces";

export const dailyPlanCompareEstimated = (plans: IDailyPlan[]) => {

const plan = plans.find((plan) => plan.date?.toString()?.startsWith(new Date()?.toISOString().split('T')[0]));

const times = plan?.tasks?.map((task) => task?.estimate).filter((time): time is number => typeof time === 'number') ?? [];
const estimated = plan?.tasks?.map((task) => task.estimate! > 0);

let estimatedTime = 0;
if (times.length > 0) estimatedTime = times.reduce((acc, cur) => acc + cur, 0) ?? 0;

const workedTimes =
plan?.tasks?.map((task) => task.totalWorkedTime).filter((time): time is number => typeof time === 'number') ??
[];

let totalWorkTime = 0;
if (workedTimes?.length > 0) totalWorkTime = workedTimes.reduce((acc, cur) => acc + cur, 0) ?? 0;

const result = estimated?.every(Boolean) ? estimatedTime - totalWorkTime : null;

return {
result,
totalWorkTime,
estimatedTime
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
"use client"

import { Card, Modal, Text, Button } from 'lib/components'
import { PiWarningCircleFill } from "react-icons/pi";
import React from 'react'
import React, { InputHTMLAttributes } from 'react'
import Separator from '@components/ui/separator';
import { IDailyPlan, ITeamTask } from '@app/interfaces';
import { TaskNameInfoDisplay } from '../task/task-displays';
import { clsxm } from '@app/utils';
import { TaskEstimateInput } from '../team/user-team-card/task-estimate';
import { useTeamMemberCard, useTMCardTaskEdit } from '@app/hooks';
import { useTeamMemberCard, useTimer, useTMCardTaskEdit } from '@app/hooks';
import { dailyPlanCompareEstimated } from '@app/helpers/daily-plan-estimated';
import { secondsToTime } from '@app/helpers';


export function DailyPlanCompareEstimatedModal({
Expand All @@ -16,21 +20,29 @@ export function DailyPlanCompareEstimatedModal({
profile
}: { open: boolean, closeModal: () => void, todayPlan?: IDailyPlan[], profile: any }) {

const { estimatedTime } = dailyPlanCompareEstimated(todayPlan!);
const { h: dh, m: dm, s: ds } = secondsToTime(estimatedTime || 0);
const { startTimer } = useTimer()

const onClick = () => {
startTimer();
window.localStorage.setItem('daily-plan-modal', new Date().toISOString().split('T')[0]);
}
return (
<Modal isOpen={open} closeModal={closeModal}>
<div className='w-[98%] md:w-[550px] relative '>
<div className='w-[98%] md:w-[550px] relative'>
<Card className="w-full h-[620px] flex flex-col justify-start bg-gray-50" shadow='custom'>
<div className='flex flex-col items-center justify-between'>
<HeadTitle />
<DailyPlanCompareHeader />
</div>
<div className='flex items-start flex-col justify-start w-full px-2'>
<InputTime />
<DailyPlanWorkTimeInput estimated={`${dh}:${dm}:${ds}`} />
</div>
<div className='flex h-full w-full p-2'>
{todayPlan?.map((plan, i) => {
return <div key={i}>
{plan.tasks?.map((data, index) => {
return <CardDailyPlan
return <DailyPlanTask
key={index}
task={data}
profile={profile}
Expand All @@ -44,14 +56,14 @@ export function DailyPlanCompareEstimatedModal({
<PiWarningCircleFill className='text-[14px]' />
<span>Please correct planned work hours or re-estimate task(s)</span>
</div>
<ButtonAction closeModal={closeModal} />
<DailyPlanCompareActionButton loading={false} closeModal={closeModal} onClick={onClick} />
</div>
</Card>
</div>
</Modal>
)
}
export function CardDailyPlan({ task, profile }: { task?: ITeamTask, profile: any }) {
export function DailyPlanTask({ task, profile }: { task?: ITeamTask, profile: any }) {
const taskEdition = useTMCardTaskEdit(task);
const member = task?.selectedTeam?.members.find((member) => {
return member?.employee?.user?.id === profile?.userProfile?.id
Expand Down Expand Up @@ -83,7 +95,7 @@ export function CardDailyPlan({ task, profile }: { task?: ITeamTask, profile: an
}


export function ButtonAction({ closeModal, onClick }: { closeModal?: () => void, onClick?: () => void }) {
export function DailyPlanCompareActionButton({ closeModal, onClick, loading }: { closeModal?: () => void, onClick?: () => void, loading?: boolean }) {
return (
<div className='flex items-center justify-between'>
<Button
Expand All @@ -92,15 +104,15 @@ export function ButtonAction({ closeModal, onClick }: { closeModal?: () => void,
className='font-normal rounded-sm text-md h-9'>
Cancel
</Button>
<Button onClick={onClick} className='font-normal rounded-sm text-md h-9'>
<Button disabled={loading} loading={loading} onClick={onClick} className='font-normal rounded-sm text-md h-9'>
Start working
</Button>
</div>
)
}


export function HeadTitle() {
export function DailyPlanCompareHeader() {
return (
<>
<div >
Expand All @@ -122,11 +134,12 @@ export function HeadTitle() {
}


export function InputTime({ onChange }: { onChange?: (_: any) => void }) {
export function DailyPlanWorkTimeInput({ onChange, estimated }: { onChange?: (_: InputHTMLAttributes<HTMLInputElement>) => void, estimated?: string }) {
return (
<>
<input
onChange={onChange}
defaultValue={estimated}
className='custom-time-input mb-3 w-full p-1 focus:border-[#1B005D] border rounded-md border-[#D7E1EB] dark:focus:border-[#D7E1EB] bg-white pb-1 font-normal dark:text-white outline-none dark:bg-transparent text-[13px]'
type="time" />
<div className='flex items-center space-x-1 w-auto'>
Expand Down

0 comments on commit 4973610

Please sign in to comment.