diff --git a/apps/web/app/stores/header-tabs.ts b/apps/web/app/stores/header-tabs.ts index 3b5d13f08..b5bfd189d 100644 --- a/apps/web/app/stores/header-tabs.ts +++ b/apps/web/app/stores/header-tabs.ts @@ -10,3 +10,8 @@ export const allTeamsHeaderTabs = atom({ key: 'allTeamsHeaderTabs', default: IssuesView.CARDS }); + +export const dailyPlanViewHeaderTabs = atom({ + key: 'dailyPlanViewHeaderTabs', + default: IssuesView.CARDS +}); diff --git a/apps/web/lib/components/image-overlapper.tsx b/apps/web/lib/components/image-overlapper.tsx index ddd510b97..7f5223b7b 100644 --- a/apps/web/lib/components/image-overlapper.tsx +++ b/apps/web/lib/components/image-overlapper.tsx @@ -6,7 +6,7 @@ import { ITeamTask, ITimerStatus } from '@app/interfaces'; import Skeleton from 'react-loading-skeleton'; import { Tooltip } from './tooltip'; import { ScrollArea } from '@components/ui/scroll-bar'; -import { RiUserFill, RiUserAddFill } from "react-icons/ri"; +import { RiUserFill, RiUserAddFill } from 'react-icons/ri'; import { useModal } from '@app/hooks'; import { Modal, Divider } from 'lib/components'; import { useOrganizationTeams } from '@app/hooks'; @@ -14,7 +14,7 @@ import { useTranslations } from 'next-intl'; import { TaskAssignButton } from '../../lib/features/task/task-assign-button'; import { clsxm } from '@app/utils'; import { TaskAvatars } from 'lib/features'; -import { FaCheck } from "react-icons/fa6"; +import { FaCheck } from 'react-icons/fa6'; import TeamMember from 'lib/components/team-member'; import { IEmployee } from '@app/interfaces'; @@ -42,7 +42,7 @@ export default function ImageOverlapper({ arrowData = null, hasActiveMembers = false, assignTaskButtonCall = false, - hasInfo = '', + hasInfo = '' }: { images: ImageOverlapperProps[]; radius?: number; @@ -56,11 +56,11 @@ export default function ImageOverlapper({ hasInfo?: string; }) { // Split the array into two arrays based on the display number - const firstArray = images.slice(0, displayImageCount); - const widthCalculate = images.slice(0, 5); - const secondArray = images.slice(displayImageCount); - const isMoreThanDisplay = images.length > displayImageCount; - const imageLength = images.length; + const firstArray = images?.slice(0, displayImageCount); + const widthCalculate = images?.slice(0, 5); + const secondArray = images?.slice(displayImageCount); + const isMoreThanDisplay = images?.length > displayImageCount; + const imageLength = images?.length; const { isOpen, openModal, closeModal } = useModal(); const { activeTeam } = useOrganizationTeams(); const allMembers = activeTeam?.members || []; @@ -82,13 +82,12 @@ export default function ImageOverlapper({ const updatedUnassign = unassignedMembers.filter((el: IEmployee) => el.id != member.id); setUnassignedMembers(updatedUnassign); } - - } + }; const onCLickValidate = () => { setValidate(!validate); closeModal(); - } + }; const hasMembers = item?.members?.length > 0; const membersList = { assignedMembers, unassignedMembers }; @@ -100,59 +99,56 @@ export default function ImageOverlapper({ if ((!hasMembers && item) || hasActiveMembers || assignTaskButtonCall) { return (
- {hasInfo.length > 0 && showInfo && - (
+ {hasInfo.length > 0 && showInfo && ( +
{hasInfo}
- )} - { - iconType ? ( - - - ) : ( - <> - { - !hasMembers ? - ( -
- setShowInfo(true)} - onMouseOut={() => setShowInfo(false)} - /> -
- ) - : - ( -
- setShowInfo(true)} - onMouseOut={() => setShowInfo(false)} - /> -
- ) - } - - ) - } + )} + {iconType ? ( + + ) : ( + <> + {!hasMembers ? ( +
+ setShowInfo(true)} + onMouseOut={() => setShowInfo(false)} + /> +
+ ) : ( +
+ setShowInfo(true)} + onMouseOut={() => setShowInfo(false)} + /> +
+ )} + + )}
-
+
diff --git a/apps/web/lib/components/kanban-card.tsx b/apps/web/lib/components/kanban-card.tsx index 0410fac13..98b38a2d9 100644 --- a/apps/web/lib/components/kanban-card.tsx +++ b/apps/web/lib/components/kanban-card.tsx @@ -24,7 +24,7 @@ function getStyle(provided: DraggableProvided, style: any) { }; } -function setCommentIconColor(commentType: 'tagged' | 'untagged') { +export function setCommentIconColor(commentType: 'tagged' | 'untagged') { let style; if (commentType === 'tagged') { @@ -77,7 +77,7 @@ export function TagList({ tags }: { tags: Tag[] }) { ); } -function Priority({ level }: { level: ITaskPriority }) { +export function Priority({ level }: { level: ITaskPriority }) { const levelSmallCase = level.toString().toLowerCase(); const levelIntoNumber = levelSmallCase === 'low' ? 1 : levelSmallCase === 'medium' ? 2 : levelSmallCase === 'high' ? 3 : 4; 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 5cb66f6bf..ff4af85e1 100644 --- a/apps/web/lib/features/task/daily-plan/future-tasks.tsx +++ b/apps/web/lib/features/task/daily-plan/future-tasks.tsx @@ -5,13 +5,20 @@ import { TaskCard } from '../task-card'; import { Button } from '@components/ui/button'; import { useCanSeeActivityScreen, useDailyPlan } from '@app/hooks'; import { ReloadIcon } from '@radix-ui/react-icons'; +import { useRecoilValue } from 'recoil'; +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 { AlertPopup } from 'lib/components'; export function FutureTasks({ profile }: { profile: any }) { const { deleteDailyPlan, deleteDailyPlanLoading, futurePlans } = useDailyPlan(); const canSeeActivity = useCanSeeActivityScreen(); - const [popupOpen, setPopupOpen] = useState(false) + const [popupOpen, setPopupOpen] = useState(false); + + const view = useRecoilValue(dailyPlanViewHeaderTabs); return (
@@ -25,11 +32,14 @@ export function FutureTasks({ profile }: { profile: any }) { - -
- {formatDayPlanDate(plan.date.toString())} ({plan.tasks?.length}) + +
+
+ {formatDayPlanDate(plan.date.toString())} ({plan.tasks?.length}) +
+
@@ -37,22 +47,33 @@ export function FutureTasks({ profile }: { profile: any }) { {/* Plan tasks list */} -
    - {plan.tasks?.map((task) => ( - - ))} +
      + {plan.tasks?.map((task) => + view === 'CARDS' ? ( + + ) : ( + + ) + )}
    {/* Delete Plan */} diff --git a/apps/web/lib/features/task/daily-plan/outstanding-all.tsx b/apps/web/lib/features/task/daily-plan/outstanding-all.tsx index b6619791e..c0ca5b92d 100644 --- a/apps/web/lib/features/task/daily-plan/outstanding-all.tsx +++ b/apps/web/lib/features/task/daily-plan/outstanding-all.tsx @@ -1,50 +1,65 @@ import { EmptyPlans } from 'lib/features/user-profile-plans'; import { TaskCard } from '../task-card'; import { useDailyPlan } from '@app/hooks'; -import { TaskEstimatedcount } from '.'; +import { TaskEstimatedCount } from '.'; +import { useRecoilValue } from 'recoil'; +import { dailyPlanViewHeaderTabs } from '@app/stores/header-tabs'; +import TaskBlockCard from '../task-block-card'; +import { clsxm } from '@app/utils'; interface OutstandingAll { - profile: any, + profile: any; } export function OutstandingAll({ profile }: OutstandingAll) { - const { outstandingPlans } = useDailyPlan(); - const displayedTaskId = new Set(); - return ( -
    - - {outstandingPlans?.length > 0 ? ( - <> - {outstandingPlans?.map((plan) => ( - <> - {/* */} -
      - {plan?.tasks?.map((task) => { - //If the task is already displayed, skip it - if (displayedTaskId.has(task.id)) { return null; } - // Add the task to the Set to avoid displaying it again - displayedTaskId.add(task.id); - return - } - )} -
    - - ))} - - - ) : ( - - )} -
    - ); + const { outstandingPlans } = useDailyPlan(); + const view = useRecoilValue(dailyPlanViewHeaderTabs); + const displayedTaskId = new Set(); + return ( +
    + + {outstandingPlans?.length > 0 ? ( + <> + {outstandingPlans?.map((plan) => ( + <> + {/* */} +
      + {plan?.tasks?.map((task) => { + //If the task is already displayed, skip it + if (displayedTaskId.has(task.id)) { + return null; + } + // Add the task to the Set to avoid displaying it again + displayedTaskId.add(task.id); + return view === 'CARDS' ? ( + + ) : ( + + ); + })} +
    + + ))} + + ) : ( + + )} +
    + ); } diff --git a/apps/web/lib/features/task/daily-plan/outstanding-date.tsx b/apps/web/lib/features/task/daily-plan/outstanding-date.tsx index 8ff8e07fd..9eaa232ac 100644 --- a/apps/web/lib/features/task/daily-plan/outstanding-date.tsx +++ b/apps/web/lib/features/task/daily-plan/outstanding-date.tsx @@ -3,12 +3,18 @@ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@c import { EmptyPlans, PlanHeader } from 'lib/features/user-profile-plans'; import { TaskCard } from '../task-card'; import { useDailyPlan } from '@app/hooks'; +import { HorizontalSeparator } from 'lib/components'; +import TaskBlockCard from '../task-block-card'; +import { clsxm } from '@app/utils'; +import { useRecoilValue } from 'recoil'; +import { dailyPlanViewHeaderTabs } from '@app/stores/header-tabs'; interface IOutstandingFilterDate { - profile: any + profile: any; } export function OutstandingFilterDate({ profile }: IOutstandingFilterDate) { const { outstandingPlans } = useDailyPlan(); + const view = useRecoilValue(dailyPlanViewHeaderTabs); return (
    {outstandingPlans?.length > 0 ? ( @@ -21,11 +27,14 @@ export function OutstandingFilterDate({ profile }: IOutstandingFilterDate) { - -
    - {formatDayPlanDate(plan.date.toString())} ({plan.tasks?.length}) + +
    +
    + {formatDayPlanDate(plan.date.toString())} ({plan.tasks?.length}) +
    +
    @@ -33,21 +42,32 @@ export function OutstandingFilterDate({ profile }: IOutstandingFilterDate) { {/* Plan tasks list */} -
      - {plan.tasks?.map((task) => ( - - ))} +
        + {plan.tasks?.map((task) => + view === 'CARDS' ? ( + + ) : ( + + ) + )}
      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 07a358e95..249f3ebdd 100644 --- a/apps/web/lib/features/task/daily-plan/past-tasks.tsx +++ b/apps/web/lib/features/task/daily-plan/past-tasks.tsx @@ -3,9 +3,16 @@ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@c import { EmptyPlans, PlanHeader } from 'lib/features/user-profile-plans'; import { TaskCard } from '../task-card'; import { useDailyPlan } from '@app/hooks'; +import { useRecoilValue } from 'recoil'; +import { dailyPlanViewHeaderTabs } from '@app/stores/header-tabs'; +import { HorizontalSeparator } from 'lib/components'; +import { clsxm } from '@app/utils'; +import TaskBlockCard from '../task-block-card'; export function PastTasks({ profile }: { profile: any }) { const { pastPlans } = useDailyPlan(); + + const view = useRecoilValue(dailyPlanViewHeaderTabs); return (
      {pastPlans?.length > 0 ? ( @@ -18,11 +25,14 @@ export function PastTasks({ profile }: { profile: any }) { - -
      - {formatDayPlanDate(plan.date.toString())} ({plan.tasks?.length}) + +
      +
      + {formatDayPlanDate(plan.date.toString())} ({plan.tasks?.length}) +
      +
      @@ -30,20 +40,31 @@ export function PastTasks({ profile }: { profile: any }) { {/* Plan tasks list */} -
        - {plan.tasks?.map((task) => ( - - ))} +
          + {plan.tasks?.map((task) => + view === 'CARDS' ? ( + + ) : ( + + ) + )}
        diff --git a/apps/web/lib/features/task/daily-plan/task-estimated-count.tsx b/apps/web/lib/features/task/daily-plan/task-estimated-count.tsx index c7b4de1e2..811f7cbf7 100644 --- a/apps/web/lib/features/task/daily-plan/task-estimated-count.tsx +++ b/apps/web/lib/features/task/daily-plan/task-estimated-count.tsx @@ -1,44 +1,44 @@ import { secondsToTime } from '@app/helpers'; import { IDailyPlan } from '@app/interfaces'; -import { VerticalSeparator } from 'lib/components' -import React from 'react' +import { VerticalSeparator } from 'lib/components'; -interface ITaskEstimatedcount { - outstandingPlans: any[] +interface ITaskEstimatedCount { + outstandingPlans: any[]; } -export function TaskEstimatedcount({ outstandingPlans }: ITaskEstimatedcount) { - const element = outstandingPlans?.map((plan: IDailyPlan) => plan.tasks?.map((task) => task)); - const { timesEstemated, totalTasks } = estimatedTotalTime(element || []); - const { h: hour, m: minute } = secondsToTime((timesEstemated || 0)); - return ( -
        -
        - Estimated: - {hour}h{minute}m -
        - -
        - Total tasks: - {totalTasks} -
        -
        - ) +export function TaskEstimatedCount({ outstandingPlans }: ITaskEstimatedCount) { + const element = outstandingPlans?.map((plan: IDailyPlan) => plan.tasks?.map((task) => task)); + const { timesEstimated, totalTasks } = estimatedTotalTime(element || []); + const { h: hour, m: minute } = secondsToTime(timesEstimated || 0); + return ( +
        +
        + Estimated: + + {hour}h{minute}m + +
        + +
        + Total tasks: + {totalTasks} +
        +
        + ); } - export function estimatedTotalTime(data: any) { - // Flatten the data and reduce to calculate the sum of estimates without duplicates - const uniqueTasks = data.flat().reduce((acc: any, task: any) => { - if (!acc[task.id]) { - acc[task.id] = task.estimate; - } - return acc; - }, {}); + // Flatten the data and reduce to calculate the sum of estimates without duplicates + const uniqueTasks = data.flat().reduce((acc: any, task: any) => { + if (!acc[task.id]) { + acc[task.id] = task.estimate; + } + return acc; + }, {}); - // Calculate the total of estimates - const timesEstemated = Object.values(uniqueTasks)?.reduce((total: number, estimate: any) => total + estimate, 0); - // Calculate the total of tasks - const totalTasks = Object.values(uniqueTasks)?.length; + // Calculate the total of estimates + const timesEstimated = Object.values(uniqueTasks)?.reduce((total: number, estimate: any) => total + estimate, 0); + // Calculate the total of tasks + const totalTasks = Object.values(uniqueTasks)?.length; - return { timesEstemated, totalTasks }; + return { timesEstimated, totalTasks }; } diff --git a/apps/web/lib/features/task/daily-plan/views-header-tabs.tsx b/apps/web/lib/features/task/daily-plan/views-header-tabs.tsx new file mode 100644 index 000000000..6f18de4ac --- /dev/null +++ b/apps/web/lib/features/task/daily-plan/views-header-tabs.tsx @@ -0,0 +1,47 @@ +import { IssuesView } from '@app/constants'; +import { dailyPlanViewHeaderTabs } from '@app/stores/header-tabs'; +import { clsxm } from '@app/utils'; +import { QueueListIcon, Squares2X2Icon, TableCellsIcon } from '@heroicons/react/20/solid'; +import { Tooltip } from 'lib/components'; +import { DottedLanguageObjectStringPaths, useTranslations } from 'next-intl'; +import { useRecoilState } from 'recoil'; + +export default function ViewsHeaderTabs() { + const t = useTranslations(); + const options = [ + { label: 'CARDS', icon: QueueListIcon, view: IssuesView.CARDS }, + { label: 'TABLE', icon: TableCellsIcon, view: IssuesView.TABLE }, + { label: 'BLOCKS', icon: Squares2X2Icon, view: IssuesView.BLOCKS } + ]; + + const [view, setView] = useRecoilState(dailyPlanViewHeaderTabs); + + return ( +
        + {options.map(({ label, icon: Icon, view: optionView }) => ( + + + + ))} +
        + ); +} diff --git a/apps/web/lib/features/task/task-block-card.tsx b/apps/web/lib/features/task/task-block-card.tsx new file mode 100644 index 000000000..3b6c7544b --- /dev/null +++ b/apps/web/lib/features/task/task-block-card.tsx @@ -0,0 +1,155 @@ +import { ITeamTask } from '@app/interfaces'; +import { TaskAllStatusTypes } from './task-all-status-type'; +import MenuKanbanCard from '@components/pages/kanban/menu-kanban-card'; +import { TaskInput } from './task-input'; +import { useRecoilState } from 'recoil'; +import { activeTeamTaskId } from '@app/stores'; +import Link from 'next/link'; +import { useAuthenticateUser, useOrganizationTeams, useTaskStatistics, useTeamMemberCard } from '@app/hooks'; +import ImageComponent, { ImageOverlapperProps } from '../../components/image-overlapper'; +import { TaskIssueStatus } from './task-issue'; +import { Priority, setCommentIconColor } from 'lib/components/kanban-card'; +import CircularProgress from '@components/ui/svgs/circular-progress'; +import { HorizontalSeparator } from 'lib/components'; +import { TaskStatus } from '@app/constants'; +import { secondsToTime } from '@app/helpers'; + +interface TaskItemProps { + task: ITeamTask; +} + +export default function TaskBlockCard(props: TaskItemProps) { + const { task } = props; + const [activeTask, setActiveTask] = useRecoilState(activeTeamTaskId); + const { activeTeam } = useOrganizationTeams(); + const { user } = useAuthenticateUser(); + const { getEstimation } = useTaskStatistics(0); + const members = activeTeam?.members || []; + const currentUser = members.find((m) => m.employee.userId === user?.id); + + let totalWorkedTasksTimer = 0; + activeTeam?.members?.forEach((member) => { + const totalWorkedTasks = member?.totalWorkedTasks?.find((i) => i.id === task?.id) || null; + if (totalWorkedTasks) { + totalWorkedTasksTimer += totalWorkedTasks.duration; + } + }); + + const memberInfo = useTeamMemberCard(currentUser); + + const taskAssignee: ImageOverlapperProps[] = task.members?.map((member: any) => { + return { + id: member.user.id, + url: member.user.imageUrl, + alt: member.user.firstName + }; + }); + + const progress = getEstimation(null, task, totalWorkedTasksTimer || 1, task.estimate || 0); + + const currentMember = activeTeam?.members.find((member) => member.id === memberInfo.member?.id || task?.id); + + const { h, m, s } = secondsToTime( + (currentMember?.totalWorkedTasks && + currentMember?.totalWorkedTasks?.length && + currentMember?.totalWorkedTasks + .filter((t) => t.id === task?.id) + .reduce((previousValue, currentValue) => previousValue + currentValue.duration, 0)) || + 0 + ); + + return ( +
        +
        +
        + + + + + + +
        +
        +
        + {activeTask?.id == task.id ? ( + <> +
        + { + // TODO: implement + console.log(e); + }} + onEnterKey={() => { + setActiveTask({ id: '' }); + }} + /> +
        + + ) : ( + +
        + {task.issueType && ( + + + + + + )} + #{task.number} + {task.title} + + {task.priority && } + +
        + + )} +
        + + +
        +
        + +
        +
        +
        + {task.status === TaskStatus.INPROGRESS ? ( +
        + Live: +

        + {h}h : {m}m : {s}s +

        +
        + ) : ( +
        + Worked: +

        + {h}h : {m}m : {s}s +

        +
        + )} +
        + + {task.issueType && ( +
        +
        +
        + )} +
        +
        +
        + ); +} diff --git a/apps/web/lib/features/task/task-card.tsx b/apps/web/lib/features/task/task-card.tsx index 0f093fce5..192c035fd 100644 --- a/apps/web/lib/features/task/task-card.tsx +++ b/apps/web/lib/features/task/task-card.tsx @@ -1,9 +1,10 @@ 'use client'; -import { secondsToTime } from '@app/helpers'; +import { secondsToTime, tomorrowDate } from '@app/helpers'; import { I_TeamMemberCardHook, I_UserProfilePage, + useAuthenticateUser, useCanSeeActivityScreen, useDailyPlan, useModal, @@ -17,6 +18,7 @@ import { } from '@app/hooks'; import ImageComponent, { ImageOverlapperProps } from 'lib/components/image-overlapper'; import { + DailyPlanStatusEnum, IClassName, IDailyPlan, IDailyPlanMode, @@ -39,7 +41,7 @@ import { } from 'lib/components'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; -import { useCallback, useState } from 'react'; +import { useCallback, useState, useTransition } from 'react'; import { SetterOrUpdater, useRecoilValue } from 'recoil'; import { TaskEstimateInfo } from '../team/user-team-card/task-estimate'; import { TimerButton } from '../timer/timer-button'; @@ -53,6 +55,7 @@ import { SixSquareGridIcon, ThreeCircleOutlineVerticalIcon } from 'assets/svg'; import { CreateDailyPlanFormModal } from '../daily-plan/create-daily-plan-form-modal'; import { AddTaskToPlan } from '../daily-plan/add-task-to-plan'; import { AddWorkTimeAndEstimatesToPlan } from '../daily-plan/plans-work-time-and-estimate'; +import { ReloadIcon } from '@radix-ui/react-icons'; type Props = { active?: boolean; @@ -632,7 +635,41 @@ export function PlanTask({ employeeId?: string; chooseMember?: boolean; }) { + const t = useTranslations(); + const [isPending, startTransition] = useTransition(); const { closeModal, isOpen, openModal } = useModal(); + const { createDailyPlan } = useDailyPlan(); + const { user } = useAuthenticateUser(); + + const handleOpenModal = () => { + if (planMode === 'custom') { + openModal(); + } else if (planMode === 'today') { + startTransition(() => { + createDailyPlan({ + workTimePlanned: 0, + taskId, + date: new Date(), + status: DailyPlanStatusEnum.OPEN, + tenantId: user?.tenantId ?? '', + employeeId: employeeId, + organizationId: user?.employee.organizationId + }); + }); + } else { + startTransition(() => { + createDailyPlan({ + workTimePlanned: 0, + taskId, + date: tomorrowDate, + status: DailyPlanStatusEnum.OPEN, + tenantId: user?.tenantId ?? '', + employeeId: employeeId, + organizationId: user?.employee.organizationId + }); + }); + } + }; return ( <> @@ -641,7 +678,7 @@ export function PlanTask({ 'font-normal whitespace-nowrap transition-all', 'hover:font-semibold hover:transition-all cursor-pointer' )} - onClick={openModal} + onClick={handleOpenModal} > - {planMode === 'today' && 'Plan for today'} - {planMode === 'tomorow' && 'Plan for tomorow'} - {planMode === 'custom' && 'Plan for some day'} + {planMode === 'today' && ( + + {isPending ? ( + + ) : ( + t('dailyPlan.PLAN_FOR_TODAY') + )} + + )} + {planMode === 'tomorow' && ( + + {isPending ? ( + + ) : ( + t('dailyPlan.PLAN_FOR_TOMORROW') + )} + + )} + {planMode === 'custom' && t('dailyPlan.PLAN_FOR_SOME_DAY')} ); } export function AddTaskToPlanComponent({ task, employee }: { task: ITeamTask; employee?: OT_Member }) { + const t = useTranslations(); const { closeModal, isOpen, openModal } = useModal(); return ( - Add this task to a plan + {t('dailyPlan.ADD_TASK_TO_PLAN')} ); } export function RemoveTaskFromPlan({ task, plan, member }: { task: ITeamTask; member?: OT_Member; plan?: IDailyPlan }) { + const t = useTranslations(); const { removeTaskFromPlan } = useDailyPlan(); const data: IDailyPlanTasksUpdate = { taskId: task.id, employeeId: member?.employeeId }; const onClick = () => { @@ -689,7 +744,7 @@ export function RemoveTaskFromPlan({ task, plan, member }: { task: ITeamTask; me )} onClick={onClick} > - Remove from this plan + {t('dailyPlan.REMOVE_FROM_THIS_PLAN')} ); } diff --git a/apps/web/lib/features/user-profile-plans.tsx b/apps/web/lib/features/user-profile-plans.tsx index f8063b8c4..9154e1f8a 100644 --- a/apps/web/lib/features/user-profile-plans.tsx +++ b/apps/web/lib/features/user-profile-plans.tsx @@ -5,19 +5,21 @@ import { useRecoilValue } from 'recoil'; import { useCanSeeActivityScreen, useDailyPlan, useUserProfilePage } from '@app/hooks'; import { TaskCard } from './task/task-card'; import { IDailyPlan } from '@app/interfaces'; -import { AlertPopup, Container, NoData, ProgressBar, VerticalSeparator } from 'lib/components'; +import { AlertPopup, Container, HorizontalSeparator, NoData, ProgressBar, VerticalSeparator } from 'lib/components'; import { clsxm } from '@app/utils'; import { fullWidthState } from '@app/stores/fullWidth'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@components/ui/accordion'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@components/ui/select" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@components/ui/select'; import { formatDayPlanDate, formatIntegerToHour } from '@app/helpers'; import { EditPenBoxIcon, CheckCircleTickIcon as TickSaveIcon } from 'assets/svg'; -import { ReaderIcon, ReloadIcon } from '@radix-ui/react-icons'; +import { ReaderIcon, ReloadIcon, StarIcon } from '@radix-ui/react-icons'; import { OutstandingAll, PastTasks, Outstanding, OutstandingFilterDate } from './task/daily-plan'; import { FutureTasks } from './task/daily-plan/future-tasks'; import { Button } from '@components/ui/button'; -import { IoCalendarOutline } from "react-icons/io5"; - +import { IoCalendarOutline } from 'react-icons/io5'; +import ViewsHeaderTabs from './task/daily-plan/views-header-tabs'; +import { dailyPlanViewHeaderTabs } from '@app/stores/header-tabs'; +import TaskBlockCard from './task/task-block-card'; type FilterTabs = 'Today Tasks' | 'Future Tasks' | 'Past Tasks' | 'All Tasks' | 'Outstanding'; type FilterOutstanding = 'ALL' | 'DATE'; @@ -28,7 +30,10 @@ export function UserProfilePlans() { ? (window.localStorage.getItem('daily-plan-tab') as FilterTabs) || null : 'Today Tasks'; - const defaultOutstanding = typeof window !== 'undefined' ? (window.localStorage.getItem('outstanding') as FilterOutstanding) || null : 'ALL'; + const defaultOutstanding = + typeof window !== 'undefined' + ? (window.localStorage.getItem('outstanding') as FilterOutstanding) || null + : 'ALL'; const profile = useUserProfilePage(); const { todayPlan, futurePlans, pastPlans, outstandingPlans, sortedPlans, profileDailyPlans } = useDailyPlan(); @@ -37,11 +42,10 @@ export function UserProfilePlans() { const [currentTab, setCurrentTab] = useState(defaultTab || 'Today Tasks'); const [currentOutstanding, setCurrentOutstanding] = useState(defaultOutstanding || 'ALL'); - const screenOutstanding = { - 'ALL': , - "DATE": - } + ALL: , + DATE: + }; const tabsScreens = { 'Today Tasks': , 'Future Tasks': , @@ -50,11 +54,8 @@ export function UserProfilePlans() { Outstanding: }; - - useEffect(() => { window.localStorage.setItem('daily-plan-tab', currentTab); - }, [currentTab]); useEffect(() => { @@ -67,7 +68,7 @@ export function UserProfilePlans() { <> {profileDailyPlans?.items?.length > 0 ? (
        -
        +
        {Object.keys(tabsScreens).map((filter, i) => (
        @@ -96,25 +97,30 @@ export function UserProfilePlans() {
        ))}
        - {currentTab === 'Outstanding' && ( - - )} +
        + + {currentTab === 'Outstanding' && ( + + )} +
        {tabsScreens[currentTab]}
        @@ -131,13 +137,15 @@ function AllPlans({ profile, currentTab = 'All Tasks' }: { profile: any; current // Filter plans let filteredPlans: IDailyPlan[] = []; const { deleteDailyPlan, deleteDailyPlanLoading, sortedPlans, todayPlan } = useDailyPlan(); - const [popupOpen, setPopupOpen] = useState(false) + const [popupOpen, setPopupOpen] = useState(false); filteredPlans = sortedPlans; if (currentTab === 'Today Tasks') filteredPlans = todayPlan; const canSeeActivity = useCanSeeActivityScreen(); + const view = useRecoilValue(dailyPlanViewHeaderTabs); + return (
        {filteredPlans?.length > 0 ? ( @@ -154,11 +162,14 @@ function AllPlans({ profile, currentTab = 'All Tasks' }: { profile: any; current - -
        - {formatDayPlanDate(plan.date.toString())} ({plan.tasks?.length}) + +
        +
        + {formatDayPlanDate(plan.date.toString())} ({plan.tasks?.length}) +
        +
        @@ -166,22 +177,33 @@ function AllPlans({ profile, currentTab = 'All Tasks' }: { profile: any; current {/* Plan tasks list */} -
          - {plan.tasks?.map((task) => ( - - ))} +
            + {plan.tasks?.map((task) => + view === 'CARDS' ? ( + + ) : ( + + ) + )}
          {/* Delete Plan */} @@ -223,7 +245,6 @@ function AllPlans({ profile, currentTab = 'All Tasks' }: { profile: any; current Cancel -
        ) : ( <> diff --git a/apps/web/locales/ar.json b/apps/web/locales/ar.json index 275746bf7..17e85f377 100644 --- a/apps/web/locales/ar.json +++ b/apps/web/locales/ar.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "يرجى تأكيد رغبتك في إغلاق المهمة" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "خطة اليوم", + "PLAN_FOR_TOMORROW": "خطة الغد", + "PLAN_FOR_SOME_DAY": "خطة ليوم ما", + "ADD_TASK_TO_PLAN": "أضف هذه المهمة إلى الخطة", + "REMOVE_FROM_THIS_PLAN": "إزالة من هذه الخطة" + }, "form": { "NAME_PLACEHOLDER": "أدخل اسمك", "FIRST_NAME_PLACEHOLDER": "الاسم الأول", diff --git a/apps/web/locales/bg.json b/apps/web/locales/bg.json index f15a00a01..3b43adbba 100644 --- a/apps/web/locales/bg.json +++ b/apps/web/locales/bg.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "Моля, потвърдете ако искате да затворите задачата" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "План за днес", + "PLAN_FOR_TOMORROW": "План за утре", + "PLAN_FOR_SOME_DAY": "План за някой ден", + "ADD_TASK_TO_PLAN": "Добави тази задача към плана", + "REMOVE_FROM_THIS_PLAN": "Премахни от този план" + }, "form": { "NAME_PLACEHOLDER": "Въведете името си", "FIRST_NAME_PLACEHOLDER": "Собствено име", diff --git a/apps/web/locales/de.json b/apps/web/locales/de.json index b3d00d4df..1ce0d9d1e 100644 --- a/apps/web/locales/de.json +++ b/apps/web/locales/de.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "Bitte bestätigen Sie, ob Sie die Aufgabe schließen möchten" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "Plan für heute", + "PLAN_FOR_TOMORROW": "Plan für morgen", + "PLAN_FOR_SOME_DAY": "Plan für irgendeinen Tag", + "ADD_TASK_TO_PLAN": "Diese Aufgabe zum Plan hinzufügen", + "REMOVE_FROM_THIS_PLAN": "Aus diesem Plan entfernen" + }, "form": { "NAME_PLACEHOLDER": "Ihren Namen eingeben", "FIRST_NAME_PLACEHOLDER": "Vorname", diff --git a/apps/web/locales/en.json b/apps/web/locales/en.json index 0c4a99a67..1dc28e54d 100644 --- a/apps/web/locales/en.json +++ b/apps/web/locales/en.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "Please confirm if you want to close the task" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "Plan for today", + "PLAN_FOR_TOMORROW": "Plan for tomorrow", + "PLAN_FOR_SOME_DAY": "Plan for some day", + "ADD_TASK_TO_PLAN": "Add this task to a plan", + "REMOVE_FROM_THIS_PLAN": "Remove from this plan" + }, "form": { "NAME_PLACEHOLDER": "Enter your name", "FIRST_NAME_PLACEHOLDER": "First Name", diff --git a/apps/web/locales/es.json b/apps/web/locales/es.json index 7c3770da4..257403ef4 100644 --- a/apps/web/locales/es.json +++ b/apps/web/locales/es.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "Por favor, confirma si deseas cerrar la tarea" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "Plan para hoy", + "PLAN_FOR_TOMORROW": "Plan para mañana", + "PLAN_FOR_SOME_DAY": "Plan para algún día", + "ADD_TASK_TO_PLAN": "Agregar esta tarea a un plan", + "REMOVE_FROM_THIS_PLAN": "Eliminar de este plan" + }, "form": { "NAME_PLACEHOLDER": "Ingresa tu nombre", "FIRST_NAME_PLACEHOLDER": "Nombre", diff --git a/apps/web/locales/fr.json b/apps/web/locales/fr.json index f3d2bef22..27c226ab1 100644 --- a/apps/web/locales/fr.json +++ b/apps/web/locales/fr.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "Veuillez confirmer si vous voulez fermer la tâche" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "Plan pour aujourd'hui", + "PLAN_FOR_TOMORROW": "Plan pour demain", + "PLAN_FOR_SOME_DAY": "Plan pour une date", + "ADD_TASK_TO_PLAN": "Ajouter cette tâche au plan", + "REMOVE_FROM_THIS_PLAN": "Retirer de ce plan" + }, "form": { "NAME_PLACEHOLDER": "Entrez votre nom", "FIRST_NAME_PLACEHOLDER": "Prénom", diff --git a/apps/web/locales/he.json b/apps/web/locales/he.json index d4297fb2c..17de57399 100644 --- a/apps/web/locales/he.json +++ b/apps/web/locales/he.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "אנא אשר אם ברצונך לסגור את המשימה" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "תוכנית להיום", + "PLAN_FOR_TOMORROW": "תוכנית למחר", + "PLAN_FOR_SOME_DAY": "תוכנית ליום כלשהו", + "ADD_TASK_TO_PLAN": "הוסף משימה זו לתוכנית", + "REMOVE_FROM_THIS_PLAN": "הסר מתוכנית זו" + }, "form": { "NAME_PLACEHOLDER": "הכנס את השם שלך", "FIRST_NAME_PLACEHOLDER": "שם פרטי", diff --git a/apps/web/locales/it.json b/apps/web/locales/it.json index 49d89bf20..93262916f 100644 --- a/apps/web/locales/it.json +++ b/apps/web/locales/it.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "Conferma se desideri chiudere il compito" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "Piano per oggi", + "PLAN_FOR_TOMORROW": "Piano per domani", + "PLAN_FOR_SOME_DAY": "Piano per un giorno", + "ADD_TASK_TO_PLAN": "Aggiungi questa attività al piano", + "REMOVE_FROM_THIS_PLAN": "Rimuovi da questo piano" + }, "form": { "NAME_PLACEHOLDER": "Inserisci il tuo nome", "FIRST_NAME_PLACEHOLDER": "Nome", diff --git a/apps/web/locales/nl.json b/apps/web/locales/nl.json index 0a6737d18..fad47a7c3 100644 --- a/apps/web/locales/nl.json +++ b/apps/web/locales/nl.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "Bevestig als u de taak wilt sluiten" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "Plan voor vandaag", + "PLAN_FOR_TOMORROW": "Plan voor morgen", + "PLAN_FOR_SOME_DAY": "Plan voor een dag", + "ADD_TASK_TO_PLAN": "Voeg deze taak toe aan een plan", + "REMOVE_FROM_THIS_PLAN": "Verwijder uit dit plan" + }, "form": { "NAME_PLACEHOLDER": "Voer uw naam in", "FIRST_NAME_PLACEHOLDER": "Voornaam", diff --git a/apps/web/locales/pl.json b/apps/web/locales/pl.json index ce374a6fc..1ec6b4080 100644 --- a/apps/web/locales/pl.json +++ b/apps/web/locales/pl.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "Potwierdź, czy chcesz zamknąć zadanie" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "Plan na dzisiaj", + "PLAN_FOR_TOMORROW": "Plan na jutro", + "PLAN_FOR_SOME_DAY": "Plan na pewien dzień", + "ADD_TASK_TO_PLAN": "Dodaj tę zadanie do planu", + "REMOVE_FROM_THIS_PLAN": "Usuń z tego planu" + }, "form": { "NAME_PLACEHOLDER": "Wprowadź swoje imię", "FIRST_NAME_PLACEHOLDER": "Imię", diff --git a/apps/web/locales/pt.json b/apps/web/locales/pt.json index ae6f71e89..fef612fbe 100644 --- a/apps/web/locales/pt.json +++ b/apps/web/locales/pt.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "Por favor, confirme se deseja fechar a tarefa" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "Plano para hoje", + "PLAN_FOR_TOMORROW": "Plano para amanhã", + "PLAN_FOR_SOME_DAY": "Plano para algum dia", + "ADD_TASK_TO_PLAN": "Adicionar esta tarefa ao plano", + "REMOVE_FROM_THIS_PLAN": "Remover deste plano" + }, "form": { "NAME_PLACEHOLDER": "Digite seu nome", "FIRST_NAME_PLACEHOLDER": "Primeiro Nome", diff --git a/apps/web/locales/ru.json b/apps/web/locales/ru.json index da861c74b..51c727994 100644 --- a/apps/web/locales/ru.json +++ b/apps/web/locales/ru.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "Пожалуйста, подтвердите, что вы хотите закрыть задачу" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "План на сегодня", + "PLAN_FOR_TOMORROW": "План на завтра", + "PLAN_FOR_SOME_DAY": "План на какой-то день", + "ADD_TASK_TO_PLAN": "Добавить эту задачу в план", + "REMOVE_FROM_THIS_PLAN": "Убрать из этого плана" + }, "form": { "NAME_PLACEHOLDER": "Введите ваше имя", "FIRST_NAME_PLACEHOLDER": "Имя", diff --git a/apps/web/locales/zh.json b/apps/web/locales/zh.json index b114c82f9..64d4ea750 100644 --- a/apps/web/locales/zh.json +++ b/apps/web/locales/zh.json @@ -572,6 +572,13 @@ }, "CONFIRM_CLOSE_TASK": "请确认您是否要关闭此任务" }, + "dailyPlan": { + "PLAN_FOR_TODAY": "今日计划", + "PLAN_FOR_TOMORROW": "明日计划", + "PLAN_FOR_SOME_DAY": "未来计划", + "ADD_TASK_TO_PLAN": "添加此任务到计划", + "REMOVE_FROM_THIS_PLAN": "从计划中移除" + }, "form": { "NAME_PLACEHOLDER": "输入您的姓名", "FIRST_NAME_PLACEHOLDER": "名",