Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: user can add tasks for today / tomorrow plan from 'See plan' modal #3087

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 151 additions & 117 deletions apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx

Large diffs are not rendered by default.

111 changes: 69 additions & 42 deletions apps/web/lib/features/daily-plan/all-plans-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { DailyPlanStatusEnum, IDailyPlan } from '@app/interfaces';
import moment from 'moment';
import { ValueNoneIcon } from '@radix-ui/react-icons';
import { checkPastDate } from 'lib/utils';
import { useTranslations } from 'next-intl';

interface IAllPlansModal {
closeModal: () => void;
Expand All @@ -33,15 +34,14 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal)
const { isOpen, closeModal } = props;
const [showCalendar, setShowCalendar] = useState(false);
const [showCustomPlan, setShowCustomPlan] = useState(false);
const [customDate, setCustomDate] = useState<Date>();
const [customDate, setCustomDate] = useState<Date>(moment().toDate());
const { myDailyPlans, pastPlans } = useDailyPlan();
const t = useTranslations();

// Utility function for checking if two dates are the same
const isSameDate = useCallback(
(date1: Date, date2: Date) =>
new Date(date1).toLocaleDateString('en') === new Date(date2).toLocaleDateString('en'),
[]
);
const isSameDate = useCallback((date1: Date | number | string, date2: Date | number | string) => {
return moment(date1).toISOString().split('T')[0] === moment(date2).toISOString().split('T')[0];
}, []);

// Memoize today, tomorrow, and future plans
const todayPlan = useMemo(
Expand All @@ -55,8 +55,12 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal)
);

const selectedPlan = useMemo(
() => customDate && myDailyPlans.items.find((plan) => isSameDate(plan.date, customDate)),
[isSameDate, myDailyPlans.items, customDate]
() =>
customDate &&
myDailyPlans.items.find((plan) => {
return isSameDate(plan.date.toString().split('T')[0], customDate.setHours(0, 0, 0, 0));
}),
[customDate, myDailyPlans.items, isSameDate]
);

// Handle modal close
Expand All @@ -72,6 +76,11 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal)

// Handle tab switching
const handleTabClick = (tab: CalendarTab) => {
if (tab === 'Today') {
setCustomDate(moment().toDate());
} else if (tab === 'Tomorrow') {
setCustomDate(moment().add(1, 'days').toDate());
}
setSelectedTab(tab);
setShowCalendar(tab === 'Calendar');
setShowCustomPlan(false);
Expand All @@ -97,27 +106,24 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal)
// Set the related tab for today and tomorrow dates
const handleCalendarSelect = useCallback(() => {
if (customDate) {
if (
new Date(customDate).toLocaleDateString('en') === new Date(moment().toDate()).toLocaleDateString('en')
) {
if (isSameDate(customDate, moment().startOf('day').toDate())) {
setSelectedTab('Today');
} else if (
new Date(customDate).toLocaleDateString('en') ===
new Date(moment().add(1, 'days').toDate()).toLocaleDateString('en')
) {
setCustomDate(moment().toDate());
} else if (isSameDate(customDate, moment().add(1, 'days').startOf('day').toDate())) {
setSelectedTab('Tomorrow');
setCustomDate(moment().add(1, 'days').toDate());
} else {
setShowCalendar(false);
setShowCustomPlan(true);
}
}
}, [customDate]);
}, [customDate, isSameDate]);

const createEmptyPlan = useCallback(async () => {
try {
await createDailyPlan({
workTimePlanned: 0,
date: moment(customDate).toDate(),
date: new Date(moment(customDate).format('YYYY-MM-DD')),
status: DailyPlanStatusEnum.OPEN,
tenantId: user?.tenantId ?? '',
employeeId: user?.employee.id,
Expand Down Expand Up @@ -145,17 +151,19 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal)
<span className="rotate-180">
<ChevronRightIcon className="w-4 h-4 stroke-[#B1AEBC]" />
</span>
<span>Back</span>
<span>{t('common.BACK')}</span>
</button>
</Tooltip>
)}

<Text.Heading as="h3" className="uppercase text-center">
{selectedTab == 'Calendar'
? showCustomPlan && selectedPlan
? `PLAN FOR ${new Date(selectedPlan.date).toLocaleDateString('en-GB')}`
: `PLANS`
: `${selectedTab}'S PLAN`}
? t('common.plan.FOR_DATE', {
date: new Date(selectedPlan.date).toLocaleDateString('en-GB')
})
: t('common.plan.PLURAL')
: `${selectedTab === 'Today' ? t('common.plan.FOR_TODAY') : selectedTab === 'Tomorrow' ? t('common.plan.FOR_TOMORROW') : ''}`}
</Text.Heading>
</div>
<div className="w-full h-12 flex items-center">
Expand All @@ -166,19 +174,25 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal)
className={`flex justify-center gap-4 items-center hover:text-primary cursor-pointer ${selectedTab === tab ? 'text-primary font-medium' : ''}`}
onClick={() => handleTabClick(tab)}
>
<span>{tab}</span>
<span>
{tab === 'Today'
? t('common.TODAY')
: tab === 'Tomorrow'
? t('common.TOMORROW')
: t('common.CALENDAR')}
</span>
{index + 1 < tabs.length && <VerticalSeparator className="w-full" />}
</li>
))}
</ul>
</div>

<div className="w-full flex flex-col items-center justify-center h-[34rem]">
<div className="w-full flex flex-col items-center h-[34rem]">
{selectedTab === 'Calendar' && showCalendar ? (
<div className="w-full h-full flex-col flex items-center justify-between">
<div className="w-full grow">
<div className="w-full h-full flex flex-col gap-4 items-center justify-center">
<p className=" text-sm font-medium">Select a date to be able to see a plan</p>
<p className=" text-sm font-medium">{t('common.plan.CHOOSE_DATE')}</p>
<div className="p-3 border flex items-center justify-center rounded-md">
<FuturePlansCalendar
selectedPlan={customDate}
Expand All @@ -194,41 +208,52 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal)
<Button
variant="outline"
type="submit"
className="py-3 px-5 rounded-md font-light text-md dark:text-white dark:bg-slate-700 dark:border-slate-600"
className="py-3 px-5 min-w-[8rem] rounded-md font-light text-md dark:text-white dark:bg-slate-700 dark:border-slate-600"
onClick={handleCloseModal}
>
Cancel
{t('common.CANCEL')}
</Button>
<Button
disabled={!customDate || createDailyPlanLoading}
variant="default"
type="submit"
className={clsxm('py-3 px-5 rounded-md font-light text-md dark:text-white')}
className={clsxm(
'py-3 min-w-[8rem] px-5 rounded-md font-light text-md dark:text-white'
)}
onClick={selectedPlan ? handleCalendarSelect : createEmptyPlan}
>
{selectedPlan ? (
'Select'
t('common.SELECT')
) : createDailyPlanLoading ? (
<SpinnerLoader variant="light" size={20} />
) : (
'Add plan'
t('common.plan.ADD_PLAN')
)}
</Button>
</div>
</div>
) : (
<>
{plan ? (
{selectedPlan ? (
<AddTasksEstimationHoursModal
plan={plan}
tasks={plan.tasks ?? []}
tasks={plan?.tasks ?? []}
isRenderedInSoftFlow={false}
isOpen={isOpen}
closeModal={handleCloseModal}
selectedDate={customDate}
/>
) : customDate ? (
<AddTasksEstimationHoursModal
tasks={[]}
isRenderedInSoftFlow={false}
isOpen={isOpen}
closeModal={handleCloseModal}
selectedDate={customDate}
/>
) : (
<div className="flex justify-center items-center h-full">
<NoData component={<ValueNoneIcon />} text="Plan not found " />
<NoData component={<ValueNoneIcon />} text={t('common.plan.PLAN_NOT_FOUND')} />
</div>
)}
</>
Expand All @@ -247,7 +272,7 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal)
*/

interface ICalendarProps {
setSelectedPlan: Dispatch<SetStateAction<Date | undefined>>;
setSelectedPlan: Dispatch<SetStateAction<Date>>;
selectedPlan: Date | undefined;
plans: IDailyPlan[];
pastPlans: IDailyPlan[];
Expand All @@ -257,7 +282,7 @@ interface ICalendarProps {
* The component that handles the selection of a plan
*
* @param {Object} props - The props object
* @param {Dispatch<SetStateAction<IDailyPlan>>} props.setSelectedFuturePlan - A function that set the selected plan
* @param {Dispatch<SetStateAction<IDailyPlan>>} props.setSelectedPlan - A function that set the selected plan
* @param {IDailyPlan} props.selectedPlan - The selected plan
* @param {IDailyPlan[]} props.plans - Available plans
* @param {IDailyPlan[]} props.pastPlans - Past plans
Expand Down Expand Up @@ -285,10 +310,12 @@ const FuturePlansCalendar = memo(function FuturePlansCalendar(props: ICalendarPr
const isDateUnplanned = useCallback(
(dateToCheck: Date) => {
return !plans
.map((plan) => new Date(plan.date))
.some(
(date) => new Date(date).toLocaleDateString('en') == new Date(dateToCheck).toLocaleDateString('en')
);
.map((plan) => {
return moment(plan.date.toString().split('T')[0]).toISOString().split('T')[0];
})
.some((date) => {
return date === moment(dateToCheck).toISOString().split('T')[0];
});
},
[plans]
);
Expand All @@ -297,19 +324,19 @@ const FuturePlansCalendar = memo(function FuturePlansCalendar(props: ICalendarPr
<Calendar
mode="single"
captionLayout="dropdown"
selected={selectedPlan ? new Date(selectedPlan) : undefined}
selected={selectedPlan ? selectedPlan : undefined}
onSelect={(date) => {
if (date) {
setSelectedPlan(date);
setSelectedPlan(moment(date).toDate());
}
}}
initialFocus
disabled={(date) => {
return checkPastDate(date) && isDateUnplanned(date);
}}
modifiers={{
booked: sortedPlans?.map((plan) => new Date(plan.date)),
pastDay: pastPlans?.map((plan) => new Date(plan.date))
booked: sortedPlans?.map((plan) => moment.utc(plan.date.toString().split('T')[0]).toDate()),
pastDay: pastPlans?.map((plan) => moment.utc(plan.date.toString().split('T')[0]).toDate())
}}
modifiersClassNames={{
booked: clsxm(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function UserTeamCardHeader() {
</div>
</div>
<div className="w-4 self-stretch border-l-[0.125rem] border-l-transparent " />
<div className="2xl:w-48 3xl:w-[12rem] w-1/5 lg:px-4 !pl-6 lg:!pl-8 flex flex-col items-center text-center justify-center">
<div className="2xl:w-48 3xl:w-[12rem] capitalize w-1/5 lg:px-4 !pl-6 lg:!pl-8 flex flex-col items-center text-center justify-center">
<Tooltip label={t('task.taskTableHead.WORKED_ON_TASK_HEADER_TOOLTIP')}>
{t('dailyPlan.TASK_TIME')}
</Tooltip>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ function DropdownMenu({ edition, memberInfo }: Props) {
'font-normal whitespace-nowrap text-sm hover:font-semibold hover:transition-all'
)}
>
See Plan
{t('common.plan.SEE_PLANS')}
</button>
</ul>
</ul>
Expand Down
19 changes: 18 additions & 1 deletion apps/web/locales/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,23 @@
"SKIP_ADD_LATER": "أضف لاحقًا",
"DAILYPLAN": "مخطط",
"INVITATION_SENT": "تم إرسال دعوة بنجاح",
"INVITATION_SENT_TO_USER": "تم إرسال دعوة فريق إلى {email}."
"INVITATION_SENT_TO_USER": "تم إرسال دعوة فريق إلى {email}.",
"CALENDAR": "تقويم",
"SELECT": "اختر",
"SAVE_CHANGES": "حفظ التغييرات",
"plan": {
"SINGULAR": "خطة",
"PLURAL": "خطط",
"CHOOSE_DATE": "اختر تاريخًا لتتمكن من رؤية خطة",
"PLAN_NOT_FOUND": "لم يتم العثور على خطة",
"FOR_DATE": "خطة لِـ {date}",
"FOR_TODAY": "خطة اليوم",
"FOR_TOMORROW": "خطة الغد",
"EDIT_PLAN": "تعديل الخطة",
"TRACKED_TIME": "الوقت المتعقب",
"SEE_PLANS": "عرض الخطط",
"ADD_PLAN": "إضافة خطة"
}
},
"hotkeys": {
"HELP": "مساعدة",
Expand Down Expand Up @@ -537,6 +553,7 @@
"WORK_TIME_PLANNED_PLACEHOLDER": "وقت العمل المخطط له اليوم",
"TASKS_WITH_NO_ESTIMATIONS": "تاكس مع عدم وجود تقديرات زمنية",
"START_WORKING_BUTTON": "ابدأ العمل",
"TIMER_RUNNING": "المؤقت قيد التشغيل بالفعل",
"WARNING_PLAN_ESTIMATION": "رجى تصحيح ساعات العمل المخطط لها أو إعادة تقدير المهمة (المهام)"
}
},
Expand Down
19 changes: 18 additions & 1 deletion apps/web/locales/bg.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,23 @@
"SKIP_ADD_LATER": "Добави по-късно",
"DAILYPLAN": "Планирано",
"INVITATION_SENT": "Покана е изпратена успешно",
"INVITATION_SENT_TO_USER": "Покана е изпратена на {email}."
"INVITATION_SENT_TO_USER": "Покана е изпратена на {email}.",
"CALENDAR": "Календар",
"SELECT": "Изберете",
"SAVE_CHANGES": "Запази промените",
"plan": {
"SINGULAR": "План",
"PLURAL": "Планове",
"CHOOSE_DATE": "Изберете дата, за да видите план",
"PLAN_NOT_FOUND": "Не е намерен план",
"FOR_DATE": "ПЛАН ЗА {date}",
"FOR_TODAY": "Днешният план",
"FOR_TOMORROW": "Утрешният план",
"EDIT_PLAN": "Редактиране на план",
"TRACKED_TIME": "Проследено време",
"SEE_PLANS": "Виж планове",
"ADD_PLAN": "Добави план"
}
},
"hotkeys": {
"HELP": "Помощ",
Expand Down Expand Up @@ -537,6 +553,7 @@
"WORK_TIME_PLANNED_PLACEHOLDER": "Планирано работно време за днес",
"TASKS_WITH_NO_ESTIMATIONS": "Такси без оценки на времето",
"START_WORKING_BUTTON": "Започнете работа",
"TIMER_RUNNING": "Таймерът вече работи",
"WARNING_PLAN_ESTIMATION": "Моля, коригирайте планираните работни часове или преоценете задачата(ите) "
}
},
Expand Down
19 changes: 18 additions & 1 deletion apps/web/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,23 @@
"SKIP_ADD_LATER": "Später hinzufügen",
"DAILYPLAN": "Plan dzienny",
"INVITATION_SENT": "Einladung erfolgreich gesendet",
"INVITATION_SENT_TO_USER": "Eine Teameinladung wurde an {email} gesendet."
"INVITATION_SENT_TO_USER": "Eine Teameinladung wurde an {email} gesendet.",
"CALENDAR": "Kalender",
"SELECT": "Auswählen",
"SAVE_CHANGES": "Änderungen speichern",
"plan": {
"SINGULAR": "Plan",
"PLURAL": "Pläne",
"CHOOSE_DATE": "Wählen Sie ein Datum, um einen Plan zu sehen",
"PLAN_NOT_FOUND": "Plan nicht gefunden",
"FOR_DATE": "PLAN FÜR {date}",
"FOR_TODAY": "Heutiger Plan",
"FOR_TOMORROW": "Morgenplan",
"EDIT_PLAN": "Plan bearbeiten",
"TRACKED_TIME": "Verfolgte Zeit",
"SEE_PLANS": "Pläne anzeigen",
"ADD_PLAN": "Plan hinzufügen"
}
},
"hotkeys": {
"HELP": "Hilfe",
Expand Down Expand Up @@ -537,6 +553,7 @@
"WORK_TIME_PLANNED_PLACEHOLDER": "Für heute geplante Arbeitszeit",
"TASKS_WITH_NO_ESTIMATIONS": "Taks ohne Zeitvorgaben",
"START_WORKING_BUTTON": "Mit der Arbeit beginnen",
"TIMER_RUNNING": "Der Timer läuft bereits",
"WARNING_PLAN_ESTIMATION": "Bitte korrigieren Sie die geplanten Arbeitsstunden oder schätzen Sie die Aufgabe(n) neu ein."
}
},
Expand Down
Loading