From d7217320dd3c964183e56e5aed25ef32b8703feb Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 26 Sep 2024 19:55:39 +0200 Subject: [PATCH 01/13] In tab 'Tomorrow' button 'Edit Plan' should be active even if warnings are not resolved --- .../add-task-estimation-hours-modal.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx index 1413d496d..6165df59a 100644 --- a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx +++ b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx @@ -22,6 +22,7 @@ import { ScrollArea, ScrollBar } from '@components/ui/scroll-bar'; import { Cross2Icon } from '@radix-ui/react-icons'; import { checkPastDate } from 'lib/utils'; import { UnplanActiveTaskModal } from './unplan-active-task-modal'; +import moment from 'moment'; /** * A modal that allows user to add task estimation / planned work time, etc. @@ -81,6 +82,12 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa draft: false, saved: false }); + const isTomorrowPlan = useMemo( + () => + new Date(moment().add(1, 'days').toDate()).toLocaleDateString('en') == + new Date(plan.date).toLocaleDateString('en'), + [plan.date] + ); const canStartWorking = useMemo(() => { const isTodayPlan = @@ -243,12 +250,18 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa const StartWorkingButton = ( @@ -210,63 +227,9 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) closeModal={handleCloseModal} /> ) : ( - <> - {showSearchInput ? ( - - ) : ( -
- - {t('timer.todayPlanSettings.WORK_TIME_PLANNED')}{' '} - * - -
- { - !isNaN(parseInt(e.target.value)) - ? setWorkTimePlanned(parseInt(e.target.value)) - : setWorkTimePlanned(0); - }} - required - noWrapper - min={0} - value={ - !isNaN(workTimePlanned) && - workTimePlanned.toString() !== '0' - ? workTimePlanned.toString().replace(/^0+/, '') - : isWorkingTimeInputFocused - ? '' - : 0 - } - onFocus={() => setWorkingTimeInputFocused(true)} - onBlur={() => setWorkingTimeInputFocused(false)} - defaultValue={0} - /> - -
-
- )} -
- } text="Plan not found " /> -
- +
+ } text="Plan not found " /> +
)} )} @@ -295,7 +258,7 @@ interface ICalendarProps { * * @param {Object} props - The props object * @param {Dispatch>} props.setSelectedFuturePlan - A function that set the selected plan - * @param {IDailyPlan} props.selectedFuturePlan - The selected plan + * @param {IDailyPlan} props.selectedPlan - The selected plan * @param {IDailyPlan[]} props.plans - Available plans * @param {IDailyPlan[]} props.pastPlans - Past plans * From e76fd64a3bc1c4f02df051e5c36f721a182291dc Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 30 Sep 2024 22:53:58 +0200 Subject: [PATCH 04/13] feat: user can add tasks for today / tomorrow plan from 'See plan' modal --- .../add-task-estimation-hours-modal.tsx | 262 ++++++++++-------- .../features/daily-plan/all-plans-modal.tsx | 82 +++--- 2 files changed, 198 insertions(+), 146 deletions(-) diff --git a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx index d67a373fa..e4099dc63 100644 --- a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx +++ b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx @@ -33,19 +33,21 @@ import moment from 'moment'; * @param {IDailyPlan} props.plan - The selected plan * @param {ITeamTask[]} props.tasks - The list of planned tasks * @param {boolean} props.isRenderedInSoftFlow - If true use the soft flow logic. + * @param {Date} props.selectedDate - A date on which the user can create the plan * * @returns {JSX.Element} The modal element */ interface IAddTasksEstimationHoursModalProps { closeModal: () => void; isOpen: boolean; - plan: IDailyPlan; + plan?: IDailyPlan; tasks: ITeamTask[]; isRenderedInSoftFlow?: boolean; + selectedDate?: Date; } export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModalProps) { - const { isOpen, closeModal, plan, tasks, isRenderedInSoftFlow = true } = props; + const { isOpen, closeModal, plan, tasks, isRenderedInSoftFlow = true, selectedDate } = props; const { isOpen: isActiveTaskHandlerModalOpen, closeModal: closeActiveTaskHandlerModal, @@ -57,25 +59,30 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa const { startTimer, timerStatus } = useTimerView(); const { activeTeam, activeTeamTask, setActiveTask } = useTeamTasks(); const [showSearchInput, setShowSearchInput] = useState(false); - const [workTimePlanned, setWorkTimePlanned] = useState(plan.workTimePlanned); + const [workTimePlanned, setWorkTimePlanned] = useState(plan?.workTimePlanned ?? 0); const currentDate = useMemo(() => new Date().toISOString().split('T')[0], []); const requirePlan = useMemo(() => activeTeam?.requirePlanToTrack, [activeTeam?.requirePlanToTrack]); - const tasksEstimationTimes = useMemo(() => estimatedTotalTime(plan.tasks).timesEstimated / 3600, [plan.tasks]); + const tasksEstimationTimes = useMemo( + () => (plan && plan.tasks ? estimatedTotalTime(plan.tasks).timesEstimated / 3600 : 0), + [plan] + ); const totalWorkedTime = useMemo( () => - plan.tasks?.reduce((acc, cur) => { - const totalWorkedTime = cur.totalWorkedTime ?? 0; + plan && plan.tasks + ? plan.tasks?.reduce((acc, cur) => { + const totalWorkedTime = cur.totalWorkedTime ?? 0; - return acc + totalWorkedTime; - }, 0), + return acc + totalWorkedTime; + }, 0) + : 0, [plan] ); const [warning, setWarning] = useState(''); const [loading, setLoading] = useState(false); const [defaultTask, setDefaultTask] = useState(null); const isActiveTaskPlanned = useMemo( - () => plan.tasks?.some((task) => task.id == activeTeamTask?.id), - [activeTeamTask?.id, plan.tasks] + () => plan && plan.tasks && plan.tasks?.some((task) => task.id == activeTeamTask?.id), + [activeTeamTask?.id, plan] ); const [isWorkingTimeInputFocused, setWorkingTimeInputFocused] = useState(false); const [planEditState, setPlanEditState] = useState<{ draft: boolean; saved: boolean }>({ @@ -84,18 +91,19 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa }); const isTomorrowPlan = useMemo( () => + plan && new Date(moment().add(1, 'days').toDate()).toLocaleDateString('en') == - new Date(plan.date).toLocaleDateString('en'), - [plan.date] + new Date(plan.date).toLocaleDateString('en'), + [plan] ); const canStartWorking = useMemo(() => { const isTodayPlan = - new Date(Date.now()).toLocaleDateString('en') == new Date(plan.date).toLocaleDateString('en'); + plan && new Date(Date.now()).toLocaleDateString('en') == new Date(plan.date).toLocaleDateString('en'); return isTodayPlan; // Can add others conditions - }, [plan.date]); + }, [plan]); const handleCloseModal = useCallback(() => { if (canStartWorking) { @@ -149,7 +157,9 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa setLoading(true); // Update the plan work time only if the user changed it - plan.workTimePlanned !== workTimePlanned && (await updateDailyPlan({ workTimePlanned }, plan.id ?? '')); + plan && + plan.workTimePlanned !== workTimePlanned && + (await updateDailyPlan({ workTimePlanned }, plan.id ?? '')); setPlanEditState({ draft: false, saved: true }); @@ -166,8 +176,7 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa setLoading(false); } }, [ - plan.workTimePlanned, - plan.id, + plan, workTimePlanned, updateDailyPlan, canStartWorking, @@ -198,7 +207,7 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa useEffect(() => { if (!workTimePlanned || workTimePlanned <= 0) { setWarning(t('dailyPlan.planned_tasks_popup.warning.PLANNED_TIME')); - } else if (plan.tasks?.find((task) => !task.estimate)) { + } else if (plan?.tasks?.find((task) => !task.estimate)) { setWarning(t('dailyPlan.planned_tasks_popup.warning.TASKS_ESTIMATION')); } else if (Math.abs(workTimePlanned - tasksEstimationTimes) > 1) { checkPlannedAndEstimateTimeDiff(); @@ -206,7 +215,7 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa setWarning(''); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [workTimePlanned, tasksEstimationTimes, plan.tasks, myDailyPlans]); + }, [workTimePlanned, tasksEstimationTimes, plan?.tasks, myDailyPlans]); // Put tasks without estimates at the top of the list const sortedTasks = useMemo( @@ -245,8 +254,8 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa // Update the working planned time useEffect(() => { - setWorkTimePlanned(plan.workTimePlanned); - }, [plan.id, plan.workTimePlanned]); + setWorkTimePlanned(plan?.workTimePlanned ?? 0); + }, [plan?.id, plan?.workTimePlanned]); const StartWorkingButton = ( + {timerStatus?.running && !planEditState.draft ? ( + + {StartWorkingButton} + + ) : ( +
{StartWorkingButton}
)} - - -
- - {timerStatus?.running ? ( - - {StartWorkingButton} - - ) : ( -
{StartWorkingButton}
- )} -
+ + ) : null} ); @@ -478,10 +497,11 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa */ interface ISearchTaskInputProps { - selectedPlan: IDailyPlan; + selectedPlan?: IDailyPlan; setShowSearchInput: Dispatch>; setDefaultTask: Dispatch>; defaultTask: ITeamTask | null; + selectedDate?: Date; } /** @@ -492,11 +512,12 @@ interface ISearchTaskInputProps { * @param {Dispatch>} props.setShowSearchInput - A setter for (showing / hiding) the input * @param {Dispatch>} props.setDefaultTask - A function that sets default planned task * @param {ITeamTask} props.defaultTask - The default planned task + * @param {Date} props.selectedDate - A date on which the user can create the plan * * @returns The Search input component */ export function SearchTaskInput(props: ISearchTaskInputProps) { - const { selectedPlan, setShowSearchInput, defaultTask, setDefaultTask } = props; + const { selectedPlan, setShowSearchInput, defaultTask, setDefaultTask, selectedDate } = props; const { tasks: teamTasks, createTask } = useTeamTasks(); const { taskStatus } = useTaskStatus(); const [taskName, setTaskName] = useState(''); @@ -510,9 +531,9 @@ export function SearchTaskInput(props: ISearchTaskInputProps) { const isTaskPlanned = useCallback( (taskId: string) => { - return selectedPlan?.tasks?.some((task) => task.id == taskId); + return selectedPlan && selectedPlan?.tasks?.some((task) => task.id == taskId); }, - [selectedPlan.tasks] + [selectedPlan] ); useEffect(() => { @@ -530,7 +551,7 @@ export function SearchTaskInput(props: ISearchTaskInputProps) { } }) ); - }, [isTaskPlanned, selectedPlan.tasks, taskName, teamTasks]); + }, [isTaskPlanned, selectedPlan?.tasks, taskName, teamTasks]); const handleCreateTask = useCallback(async () => { try { @@ -605,6 +626,7 @@ export function SearchTaskInput(props: ISearchTaskInputProps) { plan={selectedPlan} setDefaultTask={setDefaultTask} isDefaultTask={task.id == defaultTask?.id} + selectedDate={selectedDate} /> ))} @@ -638,18 +660,19 @@ interface ITaskCardProps { task: ITeamTask; setDefaultTask: Dispatch>; isDefaultTask: boolean; - plan: IDailyPlan; + plan?: IDailyPlan; viewListMode?: 'planned' | 'searched'; + selectedDate?: Date; } function TaskCard(props: ITaskCardProps) { - const { task, plan, viewListMode = 'planned', isDefaultTask, setDefaultTask } = props; + const { task, plan, viewListMode = 'planned', isDefaultTask, setDefaultTask, selectedDate } = props; const { getTaskById } = useTeamTasks(); const { addTaskToPlan, createDailyPlan } = useDailyPlan(); const { user } = useAuthenticateUser(); const [addToPlanLoading, setAddToPlanLoading] = useState(false); const isTaskRenderedInTodayPlan = - new Date(Date.now()).toLocaleDateString('en') == new Date(plan.date).toLocaleDateString('en'); + plan && new Date(Date.now()).toLocaleDateString('en') == new Date(plan.date).toLocaleDateString('en'); const { isOpen: isTaskDetailsModalOpen, closeModal: closeTaskDetailsModal, @@ -678,20 +701,30 @@ function TaskCard(props: ITaskCardProps) { try { setAddToPlanLoading(true); - console.log(plan); - - if (plan.id) { + if (plan && plan.id) { await addTaskToPlan({ taskId: task.id }, plan.id); } else { - await createDailyPlan({ - workTimePlanned: 0, - taskId: task.id, - date: moment(plan.date).toDate(), - status: DailyPlanStatusEnum.OPEN, - tenantId: user?.tenantId ?? '', - employeeId: user?.employee.id, - organizationId: user?.employee.organizationId - }); + if (plan) { + await createDailyPlan({ + workTimePlanned: 0, + taskId: task.id, + date: new Date(moment(plan.date).format('YYYY-MM-DD')), + status: DailyPlanStatusEnum.OPEN, + tenantId: user?.tenantId ?? '', + employeeId: user?.employee.id, + organizationId: user?.employee.organizationId + }); + } else if (selectedDate) { + await createDailyPlan({ + workTimePlanned: 0, + taskId: task.id, + date: new Date(moment(selectedDate).format('YYYY-MM-DD')), + status: DailyPlanStatusEnum.OPEN, + tenantId: user?.tenantId ?? '', + employeeId: user?.employee.id, + organizationId: user?.employee.organizationId + }); + } } } catch (error) { console.log(error); @@ -702,6 +735,7 @@ function TaskCard(props: ITaskCardProps) { addTaskToPlan, createDailyPlan, plan, + selectedDate, task.id, user?.employee.id, user?.employee.organizationId, @@ -733,7 +767,7 @@ function TaskCard(props: ITaskCardProps) { - ) : ( + ) : plan ? ( <>
{checkPastDate(plan.date) ? ( @@ -761,10 +795,10 @@ function TaskCard(props: ITaskCardProps) { /> - )} + ) : null}
- {activeTeamTask && ( + {plan && activeTeamTask && ( (); + const [customDate, setCustomDate] = useState(moment().toDate()); const { myDailyPlans, pastPlans } = useDailyPlan(); // 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( @@ -55,8 +53,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 @@ -72,6 +74,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); @@ -97,27 +104,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.utc().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, @@ -173,7 +177,7 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) -
+
{selectedTab === 'Calendar' && showCalendar ? (
@@ -194,7 +198,7 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal)
) : ( <> - {plan ? ( + {selectedPlan ? ( + + ) : customDate ? ( ) : (
@@ -247,7 +263,7 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) */ interface ICalendarProps { - setSelectedPlan: Dispatch>; + setSelectedPlan: Dispatch>; selectedPlan: Date | undefined; plans: IDailyPlan[]; pastPlans: IDailyPlan[]; @@ -257,7 +273,7 @@ interface ICalendarProps { * The component that handles the selection of a plan * * @param {Object} props - The props object - * @param {Dispatch>} props.setSelectedFuturePlan - A function that set the selected plan + * @param {Dispatch>} 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 @@ -285,10 +301,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] ); @@ -297,10 +315,10 @@ const FuturePlansCalendar = memo(function FuturePlansCalendar(props: ICalendarPr { if (date) { - setSelectedPlan(date); + setSelectedPlan(moment(date).toDate()); } }} initialFocus @@ -308,8 +326,8 @@ const FuturePlansCalendar = memo(function FuturePlansCalendar(props: ICalendarPr 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( From 366136e8b94515695b7a303de736f51bfec8e496 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Tue, 1 Oct 2024 01:52:37 +0200 Subject: [PATCH 05/13] use i18 --- .../add-task-estimation-hours-modal.tsx | 14 ++++---- .../features/daily-plan/all-plans-modal.tsx | 35 ++++++++++++------- .../team/user-team-card/task-skeleton.tsx | 2 +- .../user-team-card/user-team-card-menu.tsx | 2 +- apps/web/locales/ar.json | 19 +++++++++- apps/web/locales/bg.json | 19 +++++++++- apps/web/locales/de.json | 19 +++++++++- apps/web/locales/en.json | 19 +++++++++- apps/web/locales/es.json | 19 +++++++++- apps/web/locales/fr.json | 19 +++++++++- apps/web/locales/he.json | 19 +++++++++- apps/web/locales/it.json | 19 +++++++++- apps/web/locales/nl.json | 19 +++++++++- apps/web/locales/pl.json | 19 +++++++++- apps/web/locales/pt.json | 19 +++++++++- apps/web/locales/ru.json | 19 +++++++++- apps/web/locales/zh.json | 19 +++++++++- 17 files changed, 265 insertions(+), 35 deletions(-) diff --git a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx index e4099dc63..8914ae272 100644 --- a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx +++ b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx @@ -69,7 +69,7 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa const totalWorkedTime = useMemo( () => plan && plan.tasks - ? plan.tasks?.reduce((acc, cur) => { + ? plan.tasks.reduce((acc, cur) => { const totalWorkedTime = cur.totalWorkedTime ?? 0; return acc + totalWorkedTime; @@ -81,7 +81,7 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa const [loading, setLoading] = useState(false); const [defaultTask, setDefaultTask] = useState(null); const isActiveTaskPlanned = useMemo( - () => plan && plan.tasks && plan.tasks?.some((task) => task.id == activeTeamTask?.id), + () => plan && plan.tasks && plan.tasks.some((task) => task.id == activeTeamTask?.id), [activeTeamTask?.id, plan] ); const [isWorkingTimeInputFocused, setWorkingTimeInputFocused] = useState(false); @@ -278,12 +278,12 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa ) : canStartWorking ? ( timerStatus?.running && planEditState.draft ? ( - 'Save Changes' + t('common.SAVE_CHANGES') ) : ( t('timer.todayPlanSettings.START_WORKING_BUTTON') ) ) : ( - 'Edit plan' + t('common.plan.EDIT_PLAN') )} ); @@ -381,7 +381,7 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
{checkPastDate(plan?.date ?? selectedDate) && (
- Tracked time + {t('common.plan.TRACKED_TIME')}
{formatIntegerToHour(totalWorkedTime ?? 0)}
@@ -449,11 +449,11 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa {isRenderedInSoftFlow ? t('common.SKIP_ADD_LATER') : t('common.CANCEL')} {timerStatus?.running && !planEditState.draft ? ( - + {StartWorkingButton} ) : ( -
{StartWorkingButton}
+
{StartWorkingButton}
)}
diff --git a/apps/web/lib/features/daily-plan/all-plans-modal.tsx b/apps/web/lib/features/daily-plan/all-plans-modal.tsx index 43888f3b8..1550263c3 100644 --- a/apps/web/lib/features/daily-plan/all-plans-modal.tsx +++ b/apps/web/lib/features/daily-plan/all-plans-modal.tsx @@ -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; @@ -35,6 +36,7 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) const [showCustomPlan, setShowCustomPlan] = useState(false); const [customDate, setCustomDate] = useState(moment().toDate()); const { myDailyPlans, pastPlans } = useDailyPlan(); + const t = useTranslations(); // Utility function for checking if two dates are the same const isSameDate = useCallback((date1: Date | number | string, date2: Date | number | string) => { @@ -106,7 +108,7 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) if (customDate) { if (isSameDate(customDate, moment().startOf('day').toDate())) { setSelectedTab('Today'); - setCustomDate(moment.utc().toDate()); + setCustomDate(moment().toDate()); } else if (isSameDate(customDate, moment().add(1, 'days').startOf('day').toDate())) { setSelectedTab('Tomorrow'); setCustomDate(moment().add(1, 'days').toDate()); @@ -149,7 +151,7 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) - Back + {t('common.BACK')} )} @@ -157,9 +159,11 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) {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') : ''}`}
@@ -170,7 +174,13 @@ 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)} > - {tab} + + {tab === 'Today' + ? t('common.TODAY') + : tab === 'Tomorrow' + ? t('common.TOMORROW') + : t('common.CALENDAR')} + {index + 1 < tabs.length && } ))} @@ -182,7 +192,7 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal)
-

Select a date to be able to see a plan

+

{t('common.plan.CHOOSE_DATE')}

- Cancel + {t('common.CANCEL')}
@@ -235,8 +245,7 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) /> ) : customDate ? ( ) : (
- } text="Plan not found " /> + } text={t('common.plan.PLAN_NOT_FOUND')} />
)} diff --git a/apps/web/lib/features/team/user-team-card/task-skeleton.tsx b/apps/web/lib/features/team/user-team-card/task-skeleton.tsx index e23538c55..7212cee4e 100644 --- a/apps/web/lib/features/team/user-team-card/task-skeleton.tsx +++ b/apps/web/lib/features/team/user-team-card/task-skeleton.tsx @@ -59,7 +59,7 @@ export function UserTeamCardHeader() {
-
+
{t('dailyPlan.TASK_TIME')} diff --git a/apps/web/lib/features/team/user-team-card/user-team-card-menu.tsx b/apps/web/lib/features/team/user-team-card/user-team-card-menu.tsx index 0e331efcd..ff074df9b 100644 --- a/apps/web/lib/features/team/user-team-card/user-team-card-menu.tsx +++ b/apps/web/lib/features/team/user-team-card/user-team-card-menu.tsx @@ -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')} diff --git a/apps/web/locales/ar.json b/apps/web/locales/ar.json index 366a8a383..20d3f0635 100644 --- a/apps/web/locales/ar.json +++ b/apps/web/locales/ar.json @@ -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": "مساعدة", @@ -537,6 +553,7 @@ "WORK_TIME_PLANNED_PLACEHOLDER": "وقت العمل المخطط له اليوم", "TASKS_WITH_NO_ESTIMATIONS": "تاكس مع عدم وجود تقديرات زمنية", "START_WORKING_BUTTON": "ابدأ العمل", + "TIMER_RUNNING": "المؤقت قيد التشغيل بالفعل", "WARNING_PLAN_ESTIMATION": "رجى تصحيح ساعات العمل المخطط لها أو إعادة تقدير المهمة (المهام)" } }, diff --git a/apps/web/locales/bg.json b/apps/web/locales/bg.json index 578d64818..22a7213a7 100644 --- a/apps/web/locales/bg.json +++ b/apps/web/locales/bg.json @@ -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": "Помощ", @@ -537,6 +553,7 @@ "WORK_TIME_PLANNED_PLACEHOLDER": "Планирано работно време за днес", "TASKS_WITH_NO_ESTIMATIONS": "Такси без оценки на времето", "START_WORKING_BUTTON": "Започнете работа", + "TIMER_RUNNING": "Таймерът вече работи", "WARNING_PLAN_ESTIMATION": "Моля, коригирайте планираните работни часове или преоценете задачата(ите) " } }, diff --git a/apps/web/locales/de.json b/apps/web/locales/de.json index 43e1088b2..2a991ad2c 100644 --- a/apps/web/locales/de.json +++ b/apps/web/locales/de.json @@ -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", @@ -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." } }, diff --git a/apps/web/locales/en.json b/apps/web/locales/en.json index 2febfb13e..1d899b517 100644 --- a/apps/web/locales/en.json +++ b/apps/web/locales/en.json @@ -202,7 +202,23 @@ "SKIP_ADD_LATER": "Add later", "DAILYPLAN": "Planned", "INVITATION_SENT": "Invitation Sent Successfully", - "INVITATION_SENT_TO_USER": "A team invitation has been sent to {email}." + "INVITATION_SENT_TO_USER": "A team invitation has been sent to {email}.", + "CALENDAR": "Calendar", + "SELECT": "Select", + "SAVE_CHANGES": "Save changes", + "plan": { + "SINGULAR": "Plan", + "PLURAL": "Plans", + "CHOOSE_DATE": "Select a date to be able to see a plan", + "PLAN_NOT_FOUND": "Plan not found", + "FOR_DATE": "PLAN FOR {date}", + "FOR_TODAY": "Today's plan", + "FOR_TOMORROW": "Tomorrow's plan", + "EDIT_PLAN": "Edit Plan", + "TRACKED_TIME": "Tracked time", + "SEE_PLANS": "See plans", + "ADD_PLAN": "Add Plan" + } }, "hotkeys": { "HELP": "Help", @@ -537,6 +553,7 @@ "WORK_TIME_PLANNED_PLACEHOLDER": "Working time planned for today", "TASKS_WITH_NO_ESTIMATIONS": "Tasks with no time estimations", "START_WORKING_BUTTON": "Start working", + "TIMER_RUNNING": "The timer is already running", "WARNING_PLAN_ESTIMATION": "Please correct planned work hours or re-estimate task(s)" } }, diff --git a/apps/web/locales/es.json b/apps/web/locales/es.json index fa97289f2..180217121 100644 --- a/apps/web/locales/es.json +++ b/apps/web/locales/es.json @@ -202,7 +202,23 @@ "SKIP_ADD_LATER": "Agregar más tarde", "DAILYPLAN": "Planificado", "INVITATION_SENT": "Invitación enviada con éxito", - "INVITATION_SENT_TO_USER": "Se ha enviado una invitación de equipo a {email}." + "INVITATION_SENT_TO_USER": "Se ha enviado una invitación de equipo a {email}.", + "CALENDAR": "Calendario", + "SELECT": "Seleccionar", + "SAVE_CHANGES": "Guardar cambios", + "plan": { + "SINGULAR": "Plan", + "PLURAL": "Planes", + "CHOOSE_DATE": "Seleccione una fecha para poder ver un plan", + "PLAN_NOT_FOUND": "Plan no encontrado", + "FOR_DATE": "PLAN PARA {date}", + "FOR_TODAY": "Plan de hoy", + "EDIT_PLAN": "Editar Plan", + "ADD_PLAN": "Agregar Plan", + "TRACKED_TIME": "Tiempo registrado", + "SEE_PLANS": "Ver planes", + "FOR_TOMORROW": "Plan de mañana" + } }, "hotkeys": { "HELP": "Ayuda", @@ -537,6 +553,7 @@ "WORK_TIME_PLANNED_PLACEHOLDER": "Tiempo de trabajo previsto para hoy", "TASKS_WITH_NO_ESTIMATIONS": "Tarea sin estimación de tiempo", "START_WORKING_BUTTON": "Empezar a trabajar", + "TIMER_RUNNING": "El temporizador ya está en ejecución", "WARNING_PLAN_ESTIMATION": "Por favor, corrija las horas de trabajo previstas o reevalúe la(s) tarea(s)" } }, diff --git a/apps/web/locales/fr.json b/apps/web/locales/fr.json index b422827f3..810c18895 100644 --- a/apps/web/locales/fr.json +++ b/apps/web/locales/fr.json @@ -202,7 +202,23 @@ "SKIP_ADD_LATER": "Ajouter plus tard", "DAILYPLAN": "Planifié", "INVITATION_SENT": "Invitation envoyée avec succès", - "INVITATION_SENT_TO_USER": "Une invitation a été envoyée à {email}." + "INVITATION_SENT_TO_USER": "Une invitation a été envoyée à {email}.", + "CALENDAR": "Calendrier", + "SELECT": "Sélectionner", + "SAVE_CHANGES": "Enregistrer les modifications", + "plan": { + "SINGULAR": "Plan", + "PLURAL": "Plans", + "CHOOSE_DATE": "Sélectionnez une date pour pouvoir voir un plan", + "PLAN_NOT_FOUND": "Plan non trouvé", + "FOR_DATE": "PLAN POUR {date}", + "FOR_TODAY": "Le plan d'aujourd'hui", + "FOR_TOMORROW": "Le plan de demain", + "EDIT_PLAN": "Modifier le plan", + "TRACKED_TIME": "Temps suivi", + "SEE_PLANS": "Voir les plans", + "ADD_PLAN": "Ajouter un plan" + } }, "hotkeys": { "HELP": "Aide", @@ -520,6 +536,7 @@ "WORK_TIME_PLANNED_PLACEHOLDER": "Temps de travail prévu pour aujourd'hui", "TASKS_WITH_NO_ESTIMATIONS": "Tâches sans estimations de temps", "START_WORKING_BUTTON": "Commencer à travailler", + "TIMER_RUNNING": "Le minuteur fonctionne déjà", "WARNING_PLAN_ESTIMATION": "Veuillez corriger les heures de travail prévues ou réévaluer la/les tâche(s)" }, "TIME_ACTIVITY": "Activité", diff --git a/apps/web/locales/he.json b/apps/web/locales/he.json index 36c0a130c..9da22459d 100644 --- a/apps/web/locales/he.json +++ b/apps/web/locales/he.json @@ -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": "עזרה", @@ -537,6 +553,7 @@ "WORK_TIME_PLANNED_PLACEHOLDER": "זמן עבודה מתוכנן להיום", "TASKS_WITH_NO_ESTIMATIONS": "משימות בלי הערכת זמן", "START_WORKING_BUTTON": "התחל לעבוד", + "TIMER_RUNNING": "הטיימר כבר פועל", "WARNING_PLAN_ESTIMATION": "נא לתקן את שעות העבודה המתוכננות או להעריך מחדש את המשימות" } }, diff --git a/apps/web/locales/it.json b/apps/web/locales/it.json index 09de582a1..02c5dc5a8 100644 --- a/apps/web/locales/it.json +++ b/apps/web/locales/it.json @@ -202,7 +202,23 @@ "SKIP_ADD_LATER": "Aggiungi dopo", "DAILYPLAN": "Pianificato", "INVITATION_SENT": "Invito inviato con successo", - "INVITATION_SENT_TO_USER": "Un invito alla squadra è stato inviato a {email}." + "INVITATION_SENT_TO_USER": "Un invito alla squadra è stato inviato a {email}.", + "CALENDAR": "Calendario", + "SELECT": "Seleziona", + "SAVE_CHANGES": "Salva modifiche", + "plan": { + "SINGULAR": "Piano", + "PLURAL": "Piani", + "CHOOSE_DATE": "Seleziona una data per poter vedere un piano", + "PLAN_NOT_FOUND": "Piano non trovato", + "FOR_DATE": "PIANO PER {date}", + "FOR_TODAY": "Piano di oggi", + "FOR_TOMORROW": "Piano di domani", + "EDIT_PLAN": "Modifica Piano", + "TRACKED_TIME": "Tempo tracciato", + "SEE_PLANS": "Vedi piani", + "ADD_PLAN": "Aggiungi Piano" + } }, "hotkeys": { "HELP": "Aiuto", @@ -520,6 +536,7 @@ "WORK_TIME_PLANNED_PLACEHOLDER": "Tempo di lavoro pianificato per oggi", "TASKS_WITH_NO_ESTIMATIONS": "Compiti senza stime di tempo", "START_WORKING_BUTTON": "Inizia a lavorare", + "TIMER_RUNNING": "Il timer è già in esecuzione", "WARNING_PLAN_ESTIMATION": "Correggere le ore di lavoro previste o rivalutare il/i compito/i." }, "TIME_ACTIVITY": "Attività", diff --git a/apps/web/locales/nl.json b/apps/web/locales/nl.json index d529bc994..0b7680e1b 100644 --- a/apps/web/locales/nl.json +++ b/apps/web/locales/nl.json @@ -202,7 +202,23 @@ "SKIP_ADD_LATER": "Later toevoegen", "DAILYPLAN": "Gepland", "INVITATION_SENT": "Uitnodiging verzonden", - "INVITATION_SENT_TO_USER": "Een teamuitnodiging is verzonden naar {email}." + "INVITATION_SENT_TO_USER": "Een teamuitnodiging is verzonden naar {email}.", + "CALENDAR": "Kalender", + "SELECT": "Selecteren", + "SAVE_CHANGES": "Wijzigingen opslaan", + "plan": { + "SINGULAR": "Plan", + "PLURAL": "Plannen", + "CHOOSE_DATE": "Selecteer een datum om een plan te kunnen zien", + "PLAN_NOT_FOUND": "Plan niet gevonden", + "FOR_DATE": "PLAN VOOR {date}", + "FOR_TODAY": "Vandaag's plan", + "FOR_TOMORROW": "Morgen's plan", + "EDIT_PLAN": "Plan bewerken", + "TRACKED_TIME": "Gegeneraliseerde tijd", + "SEE_PLANS": "Plannen bekijken", + "ADD_PLAN": "Plan toevoegen" + } }, "hotkeys": { "HELP": "Help", @@ -537,6 +553,7 @@ "WORK_TIME_PLANNED_PLACEHOLDER": "Geplande werktijd voor vandaag", "TASKS_WITH_NO_ESTIMATIONS": "Taken zonder tijdschattingen", "START_WORKING_BUTTON": "Begin met werken", + "TIMER_RUNNING": "De timer loopt al", "WARNING_PLAN_ESTIMATION": "Corrigeer de geplande werkuren of schat de taak(en) opnieuw in" } }, diff --git a/apps/web/locales/pl.json b/apps/web/locales/pl.json index b3c753449..f22554646 100644 --- a/apps/web/locales/pl.json +++ b/apps/web/locales/pl.json @@ -202,7 +202,23 @@ "SKIP_ADD_LATER": "Dodaj później", "DAILYPLAN": "Planowane", "INVITATION_SENT": "Zaproszenie wysłane", - "INVITATION_SENT_TO_USER": "Zaproszenie zespołu zostało wysłane do {email}." + "INVITATION_SENT_TO_USER": "Zaproszenie zespołu zostało wysłane do {email}.", + "CALENDAR": "Kalendarz", + "SELECT": "Wybierz", + "SAVE_CHANGES": "Zapisz zmiany", + "plan": { + "SINGULAR": "Plan", + "PLURAL": "Plany", + "CHOOSE_DATE": "Wybierz datę, aby móc zobaczyć plan", + "PLAN_NOT_FOUND": "Plan nie znaleziony", + "FOR_DATE": "PLAN NA {date}", + "FOR_TODAY": "Dzienny plan", + "FOR_TOMORROW": "Jutro plan", + "EDIT_PLAN": "Edytuj plan", + "TRACKED_TIME": "Śledzony czas", + "SEE_PLANS": "Zobacz plany", + "ADD_PLAN": "Dodaj plan" + } }, "hotkeys": { "HELP": "Pomoc", @@ -520,6 +536,7 @@ "WORK_TIME_PLANNED_PLACEHOLDER": "Zaplanowany czas pracy na dziś", "TASKS_WITH_NO_ESTIMATIONS": "Zadania bez oszacowania czasu", "START_WORKING_BUTTON": "Rozpocznij pracę", + "TIMER_RUNNING": "Timer już działa", "WARNING_PLAN_ESTIMATION": "Popraw planowane godziny pracy lub ponownie oszacuj zadania" }, "TIME_ACTIVITY": "Aktywność", diff --git a/apps/web/locales/pt.json b/apps/web/locales/pt.json index efcbf1bc8..906719445 100644 --- a/apps/web/locales/pt.json +++ b/apps/web/locales/pt.json @@ -202,7 +202,23 @@ "SKIP_ADD_LATER": "Adicionar depois", "DAILYPLAN": "Planeado", "INVITATION_SENT": "Convite enviado com sucesso", - "INVITATION_SENT_TO_USER": "Um convite de equipe foi enviado para {email}." + "INVITATION_SENT_TO_USER": "Um convite de equipe foi enviado para {email}.", + "CALENDAR": "Calendário", + "SELECT": "Selecionar", + "SAVE_CHANGES": "Salvar alterações", + "plan": { + "SINGULAR": "Plano", + "PLURAL": "Planos", + "CHOOSE_DATE": "Selecione uma data para poder ver um plano", + "PLAN_NOT_FOUND": "Plano não encontrado", + "FOR_DATE": "PLANO PARA {date}", + "FOR_TODAY": "Plano de hoje", + "FOR_TOMORROW": "Plano de amanhã", + "EDIT_PLAN": "Editar Plano", + "TRACKED_TIME": "Tempo rastreado", + "SEE_PLANS": "Ver planos", + "ADD_PLAN": "Adicionar Plano" + } }, "hotkeys": { "HELP": "Ajuda", @@ -520,6 +536,7 @@ "WORK_TIME_PLANNED_PLACEHOLDER": "Tempo de trabalho planejado para hoje", "TASKS_WITH_NO_ESTIMATIONS": "Tarefas sem estimativas de tempo", "START_WORKING_BUTTON": "Começar a trabalhar", + "TIMER_RUNNING": "O temporizador já está em execução", "WARNING_PLAN_ESTIMATION": "Corrigir as horas de trabalho planeadas ou fazer uma nova estimativa da(s) tarefa(s)" }, "TIME_ACTIVITY": "Atividade", diff --git a/apps/web/locales/ru.json b/apps/web/locales/ru.json index defbb101c..dc9e61b3d 100644 --- a/apps/web/locales/ru.json +++ b/apps/web/locales/ru.json @@ -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": "Agregar Plan" + } }, "hotkeys": { "HELP": "Помощь", @@ -520,6 +536,7 @@ "WORK_TIME_PLANNED_PLACEHOLDER": "Запланированное время работы на сегодня", "TASKS_WITH_NO_ESTIMATIONS": "Задачи без оценки времени", "START_WORKING_BUTTON": "Начать работу", + "TIMER_RUNNING": "Таймер уже запущен", "WARNING_PLAN_ESTIMATION": "Пожалуйста, исправьте запланированные часы работы или пересчитайте задание(я)" }, "TIME_ACTIVITY": "Активность", diff --git a/apps/web/locales/zh.json b/apps/web/locales/zh.json index d60241854..1c0d0153c 100644 --- a/apps/web/locales/zh.json +++ b/apps/web/locales/zh.json @@ -202,7 +202,23 @@ "SKIP_ADD_LATER": "稍后添加", "DAILYPLAN": "計劃中", "INVITATION_SENT": "邀请已发送", - "INVITATION_SENT_TO_USER": "团队邀请已发送给 {email}。" + "INVITATION_SENT_TO_USER": "团队邀请已发送给 {email}。", + "CALENDAR": "日历", + "SELECT": "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": "帮助", @@ -537,6 +553,7 @@ "WORK_TIME_PLANNED_PLACEHOLDER": "今天计划的工作时间", "TASKS_WITH_NO_ESTIMATIONS": "没有时间估算的任务", "START_WORKING_BUTTON": "开始工作", + "TIMER_RUNNING": "计时器已经在运行", "WARNING_PLAN_ESTIMATION": "请更正计划工时或重新估算任务工时" } }, From 8c4eba2705f84500ba49e6cc40524e3cf58f1653 Mon Sep 17 00:00:00 2001 From: "Thierry CH." Date: Tue, 1 Oct 2024 21:55:48 +0200 Subject: [PATCH 06/13] refactor: display notifications one time per day (#3065) * refactor: display notifications one time per day * use ISO format for dates --- apps/web/app/constants.ts | 2 +- .../team/team-outstanding-notifications.tsx | 105 +++++++++--------- apps/web/lib/features/user-profile-plans.tsx | 8 +- 3 files changed, 61 insertions(+), 54 deletions(-) diff --git a/apps/web/app/constants.ts b/apps/web/app/constants.ts index 1b7659de6..47a35fd06 100644 --- a/apps/web/app/constants.ts +++ b/apps/web/app/constants.ts @@ -285,7 +285,7 @@ export const TASKS_ESTIMATE_HOURS_MODAL_DATE = 'tasks-estimate-hours-modal-date' export const DAILY_PLAN_ESTIMATE_HOURS_MODAL_DATE = 'daily-plan-estimate-hours-modal'; export const DEFAULT_PLANNED_TASK_ID = 'default-planned-task-id'; export const LAST_OPTION__CREATE_DAILY_PLAN_MODAL = 'last-option--create-daily-plan-modal'; -export const HAS_VISITED_OUTSTANDING_TAB = 'has-visited-outstanding-tab'; +export const HAS_VISITED_OUTSTANDING_TASKS = 'has-visited-outstanding-tasks'; // OAuth provider's keys diff --git a/apps/web/lib/features/team/team-outstanding-notifications.tsx b/apps/web/lib/features/team/team-outstanding-notifications.tsx index 622d84cd4..d86c24d98 100644 --- a/apps/web/lib/features/team/team-outstanding-notifications.tsx +++ b/apps/web/lib/features/team/team-outstanding-notifications.tsx @@ -5,9 +5,10 @@ import { Cross2Icon, EyeOpenIcon } from '@radix-ui/react-icons'; import { Tooltip } from 'lib/components'; import { useTranslations } from 'next-intl'; import Link from 'next/link'; -import { useEffect, useState } from 'react'; +import { memo, useEffect, useState } from 'react'; import { estimatedTotalTime } from '../task/daily-plan'; -import { HAS_VISITED_OUTSTANDING_TAB } from '@app/constants'; +import { HAS_VISITED_OUTSTANDING_TASKS } from '@app/constants'; +import moment from 'moment'; interface IEmployeeWithOutstanding { employeeId: string | undefined; @@ -37,45 +38,48 @@ export function TeamOutstandingNotifications() { ); } -function UserOutstandingNotification({ outstandingPlans, user }: { outstandingPlans: IDailyPlan[]; user?: IUser }) { +const UserOutstandingNotification = memo(function UserOutstandingNotification({ + outstandingPlans, + user +}: { + outstandingPlans: IDailyPlan[]; + user?: IUser; +}) { const t = useTranslations(); - // Notification will be displayed 6 hours after the user closed it - const REAPPEAR_INTERVAL = 6 * 60 * 60 * 1000; // 6 hours in milliseconds; - const DISMISSAL_TIMESTAMP_KEY = 'user-saw-notif'; + // Notification will be displayed by next day const name = user?.name || user?.firstName || user?.lastName || user?.username; const [visible, setVisible] = useState(false); + const outStandingTasksCount = estimatedTotalTime( outstandingPlans.map((plan) => plan.tasks?.map((task) => task)) ).totalTasks; + const lastVisited = window?.localStorage.getItem(HAS_VISITED_OUTSTANDING_TASKS); + useEffect(() => { - const checkNotification = () => { - const alreadySeen = window && parseInt(window?.localStorage.getItem(DISMISSAL_TIMESTAMP_KEY) || '0', 10); - const hasVisitedOutstandingTab = - window && JSON.parse(window?.localStorage.getItem(HAS_VISITED_OUTSTANDING_TAB) as string); - const currentTime = new Date().getTime(); - - if (hasVisitedOutstandingTab) { - setVisible(false); - } else if (!alreadySeen || currentTime - alreadySeen > REAPPEAR_INTERVAL) { - setVisible(true); + if (lastVisited == new Date(moment().format('YYYY-MM-DD')).toISOString().split('T')[0]) { + setVisible(false); + } else { + setVisible(true); + if (!lastVisited) { + window?.localStorage.setItem( + HAS_VISITED_OUTSTANDING_TASKS, + new Date(moment().subtract(1, 'days').format('YYYY-MM-DD')).toISOString().split('T')[0] + ); } - }; - - checkNotification(); - const intervalId = setInterval(function () { - window && window?.localStorage.setItem(HAS_VISITED_OUTSTANDING_TAB, JSON.stringify(false)); - checkNotification(); - }, REAPPEAR_INTERVAL); - return () => clearInterval(intervalId); + } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const onClose = () => { - window && window?.localStorage.setItem(DISMISSAL_TIMESTAMP_KEY, new Date().getTime().toString()); + window.localStorage.setItem( + HAS_VISITED_OUTSTANDING_TASKS, + new Date(moment().format('YYYY-MM-DD')).toISOString().split('T')[0] + ); setVisible(false); }; @@ -113,15 +117,17 @@ function UserOutstandingNotification({ outstandingPlans, user }: { outstandingPl )} ); -} +}); -function ManagerOutstandingUsersNotification({ outstandingTasks }: { outstandingTasks: IDailyPlan[] }) { +const ManagerOutstandingUsersNotification = memo(function ManagerOutstandingUsersNotification({ + outstandingTasks +}: { + outstandingTasks: IDailyPlan[]; +}) { const { user } = useAuthenticateUser(); const t = useTranslations(); - // Notification will be displayed 6 hours after the user closed it - const REAPPEAR_INTERVAL = 6 * 60 * 60 * 1000; // 6 hours in milliseconds; - const MANAGER_DISMISSAL_TIMESTAMP_KEY = 'manager-saw-outstanding-notif'; + // Notification will be displayed by next day const [visible, setVisible] = useState(false); @@ -158,32 +164,29 @@ function ManagerOutstandingUsersNotification({ outstandingTasks }: { outstanding [] ); + const lastVisited = window?.localStorage.getItem(HAS_VISITED_OUTSTANDING_TASKS); + useEffect(() => { - const checkNotification = () => { - const alreadySeen = - window && parseInt(window?.localStorage.getItem(MANAGER_DISMISSAL_TIMESTAMP_KEY) || '0', 10); - const hasVisitedOutstandingTab = - window && JSON.parse(window?.localStorage.getItem(HAS_VISITED_OUTSTANDING_TAB) as string); - const currentTime = new Date().getTime(); - - if (hasVisitedOutstandingTab) { - setVisible(false); - } else if (!alreadySeen || currentTime - alreadySeen > REAPPEAR_INTERVAL) { - setVisible(true); + if (lastVisited == new Date(moment().format('YYYY-MM-DD')).toISOString().split('T')[0]) { + setVisible(false); + } else { + setVisible(true); + if (!lastVisited) { + window?.localStorage.setItem( + HAS_VISITED_OUTSTANDING_TASKS, + new Date(moment().subtract(1, 'days').format('YYYY-MM-DD')).toISOString().split('T')[0] + ); } - }; - - checkNotification(); - const intervalId = setInterval(function () { - window && window?.localStorage.setItem(HAS_VISITED_OUTSTANDING_TAB, JSON.stringify(false)); - checkNotification(); - }, REAPPEAR_INTERVAL); - return () => clearInterval(intervalId); + } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const onClose = () => { - window && window?.localStorage.setItem(MANAGER_DISMISSAL_TIMESTAMP_KEY, new Date().getTime().toString()); + window.localStorage.setItem( + HAS_VISITED_OUTSTANDING_TASKS, + new Date(moment().format('YYYY-MM-DD')).toISOString().split('T')[0] + ); setVisible(false); }; return ( @@ -214,4 +217,4 @@ function ManagerOutstandingUsersNotification({ outstandingTasks }: { outstanding )} ); -} +}); diff --git a/apps/web/lib/features/user-profile-plans.tsx b/apps/web/lib/features/user-profile-plans.tsx index f7a3655ac..e02acb305 100644 --- a/apps/web/lib/features/user-profile-plans.tsx +++ b/apps/web/lib/features/user-profile-plans.tsx @@ -14,7 +14,7 @@ import { useAuthenticateUser, useCanSeeActivityScreen, useDailyPlan, useUserProf import { useDateRange } from '@app/hooks/useDateRange'; import { filterDailyPlan } from '@app/hooks/useFilterDateRange'; import { useLocalStorageState } from '@app/hooks/useLocalStorageState'; -import { HAS_VISITED_OUTSTANDING_TAB } from '@app/constants'; +import { HAS_VISITED_OUTSTANDING_TASKS } from '@app/constants'; import { IDailyPlan, ITeamTask } from '@app/interfaces'; import { dataDailyPlanState } from '@app/stores'; import { fullWidthState } from '@app/stores/fullWidth'; @@ -37,6 +37,7 @@ import { FutureTasks } from './task/daily-plan/future-tasks'; import ViewsHeaderTabs from './task/daily-plan/views-header-tabs'; import TaskBlockCard from './task/task-block-card'; import { TaskCard } from './task/task-card'; +import moment from 'moment'; export type FilterTabs = 'Today Tasks' | 'Future Tasks' | 'Past Tasks' | 'All Tasks' | 'Outstanding'; type FilterOutstanding = 'ALL' | 'DATE'; @@ -92,7 +93,10 @@ export function UserProfilePlans() { setCurrentDataDailyPlan(futurePlans); setFilterFuturePlanData(filterDailyPlan(date as any, futurePlans)); } else if (currentTab === 'Outstanding') { - window.localStorage.setItem(HAS_VISITED_OUTSTANDING_TAB, JSON.stringify(true)); + window.localStorage.setItem( + HAS_VISITED_OUTSTANDING_TASKS, + new Date(moment().format('YYYY-MM-DD')).toISOString().split('T')[0] + ); } }, [currentTab, setCurrentDataDailyPlan, date, currentDataDailyPlan, futurePlans, pastPlans, sortedPlans]); From 50ac79785156c1739f781b6756e6773b117512de Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Tue, 1 Oct 2024 22:58:10 +0200 Subject: [PATCH 07/13] improve past plans view --- .../add-task-estimation-hours-modal.tsx | 61 ++++++++++++++++--- apps/web/locales/ar.json | 4 ++ apps/web/locales/bg.json | 4 ++ apps/web/locales/de.json | 4 ++ apps/web/locales/en.json | 4 ++ apps/web/locales/es.json | 4 ++ apps/web/locales/fr.json | 4 ++ apps/web/locales/he.json | 4 ++ apps/web/locales/it.json | 4 ++ apps/web/locales/nl.json | 4 ++ apps/web/locales/pl.json | 4 ++ apps/web/locales/pt.json | 4 ++ apps/web/locales/ru.json | 4 ++ apps/web/locales/zh.json | 4 ++ 14 files changed, 103 insertions(+), 10 deletions(-) diff --git a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx index 8914ae272..5029708f6 100644 --- a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx +++ b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx @@ -260,7 +260,7 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa const StartWorkingButton = ( ); + // TODO: Add onclick handler + const TimeSheetsButton = ( + + ); + const content = (
@@ -324,7 +331,11 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
{checkPastDate(plan?.date ?? selectedDate) ? (
- {formatIntegerToHour(tasksEstimationTimes)} + {/**Create a space between hours and minutes for past plans view */} + {formatIntegerToHour(plan?.workTimePlanned ?? 0).replace( + /(\d+h)(\d+m)/, + '$1 $2' + )}
) : ( {t('common.plan.TRACKED_TIME')}
- {formatIntegerToHour(totalWorkedTime ?? 0)} + {formatIntegerToHour(totalWorkedTime ?? 0).replace(/(\d+h)(\d+m)/, '$1 $2')}
)} @@ -402,13 +413,23 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
{checkPastDate(plan.date) ? ( - {t('dailyPlan.ESTIMATED')} : + <> + {t('dailyPlan.ESTIMATED')} : + + {formatIntegerToHour(tasksEstimationTimes).replace( + /(\d+h)(\d+m)/, + '$1 $2' + )} + + ) : ( - {t('dailyPlan.TOTAL_ESTIMATED')} : + <> + {t('dailyPlan.TOTAL_ESTIMATED')} : + + {formatIntegerToHour(tasksEstimationTimes)} + + )} - - {formatIntegerToHour(tasksEstimationTimes)} -
@@ -458,6 +479,26 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
) : null} +
+ + {timerStatus?.running ? ( + + {StartWorkingButton} + + ) : ( +
+ {checkPastDate(plan?.date) ? TimeSheetsButton : StartWorkingButton} +
+ )} +
); @@ -769,7 +810,7 @@ function TaskCard(props: ITaskCardProps) { ) : plan ? ( <> -
+
{checkPastDate(plan.date) ? ( Date: Wed, 2 Oct 2024 20:25:06 +0200 Subject: [PATCH 08/13] fix dropdowns in 'See plan' modal --- apps/web/lib/components/modal.tsx | 7 +++- .../add-task-estimation-hours-modal.tsx | 41 +++++++++---------- .../features/daily-plan/all-plans-modal.tsx | 7 +++- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/apps/web/lib/components/modal.tsx b/apps/web/lib/components/modal.tsx index 33ea9eed9..84985fc01 100644 --- a/apps/web/lib/components/modal.tsx +++ b/apps/web/lib/components/modal.tsx @@ -11,6 +11,7 @@ type Props = { description?: string; isOpen: boolean; closeModal: () => void; + customCloseModal?: () => void; className?: string; alignCloseIcon?: boolean; showCloseIcon?: boolean; @@ -19,6 +20,7 @@ type Props = { export function Modal({ isOpen, closeModal, + customCloseModal, children, title, titleClass, @@ -54,7 +56,10 @@ export function Modal({ {description && {description}} {showCloseIcon && (
{ + closeModal(); + customCloseModal?.(); + }} className={`absolute ${ alignCloseIcon ? 'right-2 top-3' : 'right-3 top-3' } md:right-2 md:top-3 cursor-pointer z-50`} diff --git a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx index 8914ae272..69a8b0184 100644 --- a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx +++ b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx @@ -89,13 +89,6 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa draft: false, saved: false }); - const isTomorrowPlan = useMemo( - () => - plan && - new Date(moment().add(1, 'days').toDate()).toLocaleDateString('en') == - new Date(plan.date).toLocaleDateString('en'), - [plan] - ); const canStartWorking = useMemo(() => { const isTodayPlan = @@ -260,8 +253,8 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa const StartWorkingButton = ( - {timerStatus?.running && !planEditState.draft ? ( + {canStartWorking && timerStatus?.running && !planEditState.draft ? ( {StartWorkingButton} ) : ( -
{StartWorkingButton}
+
+ {checkPastDate(plan.date) ? TimeSheetsButton : StartWorkingButton} +
)}
) : null} -
- - {timerStatus?.running ? ( - - {StartWorkingButton} - - ) : ( -
- {checkPastDate(plan?.date) ? TimeSheetsButton : StartWorkingButton} -
- )} -
); From 641d4191b113faeda30dc8e1e55a405c257a4fe6 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 3 Oct 2024 00:36:54 +0200 Subject: [PATCH 10/13] user is able to double-click on specific dates and see a Plan --- .../features/daily-plan/all-plans-modal.tsx | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/apps/web/lib/features/daily-plan/all-plans-modal.tsx b/apps/web/lib/features/daily-plan/all-plans-modal.tsx index 1550263c3..7c1430fe5 100644 --- a/apps/web/lib/features/daily-plan/all-plans-modal.tsx +++ b/apps/web/lib/features/daily-plan/all-plans-modal.tsx @@ -1,5 +1,5 @@ import { Card, Modal, NoData, SpinnerLoader, Tooltip, VerticalSeparator } from 'lib/components'; -import { Dispatch, memo, SetStateAction, useCallback, useMemo, useState } from 'react'; +import { Dispatch, memo, SetStateAction, useCallback, useMemo, useRef, useState } from 'react'; import { clsxm } from '@app/utils'; import { Text } from 'lib/components'; import { ChevronRightIcon } from 'assets/svg'; @@ -12,6 +12,7 @@ import moment from 'moment'; import { ValueNoneIcon } from '@radix-ui/react-icons'; import { checkPastDate } from 'lib/utils'; import { useTranslations } from 'next-intl'; +import { ActiveModifiers } from 'react-day-picker'; interface IAllPlansModal { closeModal: () => void; @@ -199,6 +200,8 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) setSelectedPlan={setCustomDate} plans={myDailyPlans.items} pastPlans={pastPlans} + handleCalendarSelect={handleCalendarSelect} + createEmptyPlan={createEmptyPlan} />
@@ -276,6 +279,8 @@ interface ICalendarProps { selectedPlan: Date | undefined; plans: IDailyPlan[]; pastPlans: IDailyPlan[]; + handleCalendarSelect: () => void; + createEmptyPlan: () => Promise; } /** @@ -286,11 +291,15 @@ interface ICalendarProps { * @param {IDailyPlan} props.selectedPlan - The selected plan * @param {IDailyPlan[]} props.plans - Available plans * @param {IDailyPlan[]} props.pastPlans - Past plans + * @param {() => void} props.handleCalendarSelect - Handle plan selection + * @param {() => Promise} props.createEmptyPlan - Create empty plan * * @returns {JSX.Element} The Calendar component. */ const FuturePlansCalendar = memo(function FuturePlansCalendar(props: ICalendarProps) { - const { selectedPlan, setSelectedPlan, plans, pastPlans } = props; + const { selectedPlan, setSelectedPlan, plans, pastPlans, createEmptyPlan, handleCalendarSelect } = props; + const clickTimeoutRef = useRef(null); + const clickCountRef = useRef(0); const sortedPlans = useMemo( () => @@ -320,9 +329,46 @@ const FuturePlansCalendar = memo(function FuturePlansCalendar(props: ICalendarPr [plans] ); + /** + * onDayClick handler - A function that handles clicks on a day (date) + * + * @param {Date} day - The clicked date + * @param {ActiveModifiers} activeModifiers - The active modifiers + * @param {React.MouseEvent} e - The event + * + * @returns {void} Nothing + */ + const handleDayClick = useCallback( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + (day: Date, activeModifiers: ActiveModifiers, e: React.MouseEvent) => { + if (activeModifiers.disabled) return; + + clickCountRef.current += 1; + + if (clickCountRef.current === 1) { + clickTimeoutRef.current = setTimeout(() => { + // Single click + clickCountRef.current = 0; + }, 300); + } else if (clickCountRef.current === 2) { + if (clickTimeoutRef.current) clearTimeout(clickTimeoutRef.current); + // Double click + if (selectedPlan) { + handleCalendarSelect(); + } else { + setSelectedPlan(moment(day).toDate()); + createEmptyPlan(); + } + clickCountRef.current = 0; + } + }, + [createEmptyPlan, handleCalendarSelect, selectedPlan, setSelectedPlan] + ); + return ( { From 4674f006e219582625ce15ec14edc6f9a13fa7a7 Mon Sep 17 00:00:00 2001 From: land-bit Date: Thu, 3 Oct 2024 07:07:10 +0200 Subject: [PATCH 11/13] [Fix] Settings | Icon direction --- apps/web/lib/components/accordian.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/lib/components/accordian.tsx b/apps/web/lib/components/accordian.tsx index 47602ee1f..9cd7275dc 100644 --- a/apps/web/lib/components/accordian.tsx +++ b/apps/web/lib/components/accordian.tsx @@ -30,7 +30,7 @@ export const Accordian = ({ children, title, className, isDanger, id, defaultOpe From 3d6b72cf933d180d7e6e537de2f298b670704f17 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 3 Oct 2024 16:04:42 +0200 Subject: [PATCH 12/13] feat: impreove the warnings order --- .../add-task-estimation-hours-modal.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx index 8914ae272..d9bfb04f7 100644 --- a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx +++ b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx @@ -205,13 +205,20 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa // Handle warning messages useEffect(() => { - if (!workTimePlanned || workTimePlanned <= 0) { - setWarning(t('dailyPlan.planned_tasks_popup.warning.PLANNED_TIME')); - } else if (plan?.tasks?.find((task) => !task.estimate)) { + // First, check if there are tasks without estimates and show the corresponding warning + if (plan?.tasks?.find((task) => !task.estimate)) { setWarning(t('dailyPlan.planned_tasks_popup.warning.TASKS_ESTIMATION')); - } else if (Math.abs(workTimePlanned - tasksEstimationTimes) > 1) { + } + // Next, check if no work time is planned or if planned time is invalid + else if (!workTimePlanned || workTimePlanned <= 0) { + setWarning(t('dailyPlan.planned_tasks_popup.warning.PLANNED_TIME')); + } + // If the difference between planned and estimated times is significant, check further + else if (Math.abs(workTimePlanned - tasksEstimationTimes) > 1) { checkPlannedAndEstimateTimeDiff(); - } else { + } + // If all checks pass, clear the warning + else { setWarning(''); } // eslint-disable-next-line react-hooks/exhaustive-deps From 0da0f0fca15be1397505de1f9353fe4a07483123 Mon Sep 17 00:00:00 2001 From: "northflank-cloud-build-run[bot]" <66723851+northflank-cloud-build-run[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2024 19:28:17 +0000 Subject: [PATCH 13/13] [NORTHFLANK] Updated template file contents --- northflank-template.json | 143 +++++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 58 deletions(-) diff --git a/northflank-template.json b/northflank-template.json index 1caa83873..06769f1dc 100644 --- a/northflank-template.json +++ b/northflank-template.json @@ -1,59 +1,86 @@ { - "apiVersion": "v1", - "spec": { - "kind": "Workflow", - "spec": { - "type": "sequential", - "steps": [ - { - "kind": "DeploymentService", - "spec": { - "deployment": { - "instances": 1, - "docker": { - "configType": "default" - }, - "external": { - "imagePath": "ghcr.io/ever-co/ever-teams-webapp:latest" - } - }, - "runtimeEnvironment": {}, - "runtimeFiles": {}, - "billing": { - "deploymentPlan": "nf-compute-10" - }, - "name": "ever-teams-web", - "ports": [ - { - "internalPort": 3030, - "protocol": "HTTP", - "public": true, - "name": "p01", - "domains": [], - "security": { - "policies": [], - "credentials": [] - }, - "disableNfDomain": false - } - ] - } - } - ] - } - }, - "name": "ever-teams-template", - "description": "Open Work and Project Management Platform.", - "project": { - "spec": { - "name": "Ever Teams Template", - "region": "europe-west", - "description": "Open Work and Project Management Platform.", - "color": "#3826A6", - "networking": { - "allowedIngressProjects": [] - } - } - }, - "$schema": "https://api.northflank.com/v1/schemas/template" -} + "apiVersion": "v1.2", + "spec": { + "kind": "Workflow", + "spec": { + "type": "sequential", + "steps": [ + { + "kind": "Project", + "ref": "project", + "spec": { + "name": "Ever Teams Template", + "region": "europe-west", + "description": "Open Work and Project Management Platform.", + "color": "#3826A6", + "networking": { + "allowedIngressProjects": [] + } + } + }, + { + "kind": "Workflow", + "spec": { + "type": "sequential", + "steps": [ + { + "kind": "DeploymentService", + "spec": { + "deployment": { + "instances": 1, + "docker": { + "configType": "default" + }, + "external": { + "imagePath": "ghcr.io/ever-co/ever-teams-webapp:latest" + } + }, + "runtimeEnvironment": {}, + "runtimeFiles": {}, + "billing": { + "deploymentPlan": "nf-compute-10" + }, + "name": "ever-teams-web", + "ports": [ + { + "internalPort": 3030, + "protocol": "HTTP", + "public": true, + "name": "p01", + "domains": [], + "security": { + "policies": [], + "credentials": [] + }, + "disableNfDomain": false + } + ] + } + } + ], + "context": { + "projectId": "${refs.project.id}" + } + } + } + ] + } + }, + "name": "ever-teams-template", + "description": "Open Work and Project Management Platform.", + "options": { + "autorun": false, + "concurrencyPolicy": "allow" + }, + "gitops": { + "repoUrl": "https://github.com/ever-co/ever-teams", + "vcsService": "github", + "vcsLinkId": "6561e62d6069fdc2fe81dffc", + "installationId": 44402628, + "branch": "develop", + "filePath": "/northflank-template.json", + "templateSha": "8b67d68a7666047ccd64928875f0a88fb762035b", + "templateSHA": "12e71fb141d67d0a4d61dec6962c3d1ec57f98fc" + }, + "$schema": "https://api.northflank.com/v1/schemas/template" +} \ No newline at end of file