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]: handle February (28/29 days) in calendar and integrate task label translations, update button logic #3397

Merged
merged 1 commit into from
Dec 8, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import MonthlyTimesheetCalendar from "./MonthlyTimesheetCalendar";
import { useTimelogFilterOptions } from "@/app/hooks";
import WeeklyTimesheetCalendar from "./WeeklyTimesheetCalendar";
interface BaseCalendarDataViewProps {
t: TranslationHooks
data: GroupedTimesheet[];
daysLabels?: string[];
CalendarComponent: typeof MonthlyTimesheetCalendar | typeof WeeklyTimesheetCalendar;
Expand All @@ -35,9 +36,9 @@ export function CalendarView({ data, loading }: { data?: GroupedTimesheet[], loa
data.length > 0 ? (
<>
{timesheetGroupByDays === 'Monthly' ? (
<MonthlyCalendarDataView data={data} daysLabels={defaultDaysLabels} />
<MonthlyCalendarDataView data={data} daysLabels={defaultDaysLabels} t={t} />
) : timesheetGroupByDays === 'Weekly' ? (
<WeeklyCalendarDataView data={data} daysLabels={defaultDaysLabels} />
<WeeklyCalendarDataView data={data} daysLabels={defaultDaysLabels} t={t} />
) : (
<CalendarDataView data={data} t={t} />
)}
Expand Down Expand Up @@ -99,7 +100,7 @@ const CalendarDataView = ({ data, t }: { data?: GroupedTimesheet[], t: Translati
<div className="flex items-center w-full gap-2">
<div className={cn('p-2 rounded', statusColor(status).bg)}></div>
<div className="flex items-center gap-x-1">
<span className="text-base font-normal text-gray-400 uppercase text-[12px]">
<span className="text-base font-normal text-gray-400 uppercase !text-[12px]">
{status === 'DENIED' ? 'REJECTED' : status}
</span>
<span className="text-gray-400 text-[14px]">({rows.length})</span>
Expand Down Expand Up @@ -166,10 +167,11 @@ const CalendarDataView = ({ data, t }: { data?: GroupedTimesheet[], t: Translati
)
}

const BaseCalendarDataView = ({ data, daysLabels, CalendarComponent }: BaseCalendarDataViewProps) => {
const BaseCalendarDataView = ({ data, daysLabels, t, CalendarComponent }: BaseCalendarDataViewProps) => {
const { getStatusTimesheet } = useTimesheet({});
return (
<CalendarComponent
t={t}
data={data}
// locale={ }
daysLabels={daysLabels}
Expand All @@ -185,17 +187,17 @@ const BaseCalendarDataView = ({ data, daysLabels, CalendarComponent }: BaseCalen
<AccordionTrigger
type="button"
className={cn(
'flex flex-row-reverse justify-end items-center w-full !h-[20px] rounded-sm gap-x-2 hover:no-underline',
'flex flex-row-reverse justify-end items-center w-full !h-[16px] rounded-sm gap-x-2 hover:no-underline',
statusColor(status).text
)}>
<div className="flex items-center justify-between space-x-1 w-full">
<div className="flex items-center w-full gap-2">
<div className={cn('p-2 rounded', statusColor(status).bg)}></div>
<div className="flex items-center gap-x-1">
<span className="text-base font-normal text-gray-400 uppercase text-[12px]">
<span className="text-base font-normal text-gray-400 uppercase !text-[13px]">
{status === 'DENIED' ? 'REJECTED' : status}
</span>
<span className="text-gray-400 text-[12px]">({rows.length})</span>
<span className="text-gray-400 text-[13px]">({rows.length})</span>
</div>
</div>
<div className="flex items-center space-x-2">
Expand Down Expand Up @@ -262,10 +264,10 @@ const BaseCalendarDataView = ({ data, daysLabels, CalendarComponent }: BaseCalen
);
};

const MonthlyCalendarDataView = (props: { data: GroupedTimesheet[], daysLabels?: string[] }) => (
const MonthlyCalendarDataView = (props: { data: GroupedTimesheet[], t: TranslationHooks, daysLabels?: string[] }) => (
<BaseCalendarDataView {...props} CalendarComponent={MonthlyTimesheetCalendar} />
);

const WeeklyCalendarDataView = (props: { data: GroupedTimesheet[], daysLabels?: string[] }) => (
const WeeklyCalendarDataView = (props: { data: GroupedTimesheet[], t: TranslationHooks, daysLabels?: string[] }) => (
<BaseCalendarDataView {...props} CalendarComponent={WeeklyTimesheetCalendar} />
);
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { HTMLAttributes } from 'react';
import { Button } from 'lib/components';
import { clsxm } from '@app/utils';
import { TimesheetLog, TimesheetStatus } from '@/app/interfaces';
import { useTranslations } from 'next-intl';

export type FilterStatus = 'All Tasks' | 'Pending' | 'Approved' | 'In review' | 'Draft' | 'Rejected';
export function FilterWithStatus({
Expand All @@ -17,6 +18,7 @@ export function FilterWithStatus({
onToggle: (status: FilterStatus) => void;
className?: HTMLAttributes<HTMLDivElement>;
}>) {
const t = useTranslations();

const statusIcons: Record<FilterStatus, string> = {
'All Tasks': 'icon-all',
Expand All @@ -29,12 +31,12 @@ export function FilterWithStatus({

const buttonData = React.useMemo(() => {
const counts = {
'All Tasks': Object.values(data ?? {}).reduce((total, tasks) => total + (tasks?.length ?? 0), 0),
Pending: data?.PENDING?.length ?? 0,
Approved: data?.APPROVED?.length ?? 0,
'In review': data?.['IN REVIEW']?.length ?? 0,
Draft: data?.DRAFT?.length ?? 0,
Rejected: data?.DENIED?.length ?? 0,
[t('pages.timesheet.ALL_TASKS')]: Object.values(data ?? {}).reduce((total, tasks) => total + (tasks?.length ?? 0), 0),
[t('pages.timesheet.PENDING')]: data?.PENDING?.length ?? 0,
[t('pages.timesheet.APPROVED')]: data?.APPROVED?.length ?? 0,
[t('pages.timesheet.IN_REVIEW')]: data?.['IN REVIEW']?.length ?? 0,
[t('pages.timesheet.DRAFT')]: data?.DRAFT?.length ?? 0,
[t('pages.timesheet.REJECTED')]: data?.DENIED?.length ?? 0,
Innocent-Akim marked this conversation as resolved.
Show resolved Hide resolved
};
return Object.entries(counts).map(([label, count]) => ({
label: label as FilterStatus,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React, { useMemo, useState, useCallback } from "react";
import { format, addMonths, eachDayOfInterval, startOfMonth, endOfMonth, addDays, Locale } from "date-fns";
import { format, addMonths, eachDayOfInterval, startOfMonth, endOfMonth, addDays, Locale, isLeapYear } from "date-fns";
import { GroupedTimesheet } from "@/app/hooks/features/useTimesheet";
import { enGB } from 'date-fns/locale';
import { cn } from "@/lib/utils";
import { TotalDurationByDate } from "@/lib/features";
import { formatDate } from "@/app/helpers";
import { TranslationHooks } from "next-intl";

type MonthlyCalendarDataViewProps = {
t: TranslationHooks
data?: GroupedTimesheet[];
onDateClick?: (date: Date) => void;
renderDayContent?: (date: Date, plan?: GroupedTimesheet) => React.ReactNode;
Expand All @@ -26,7 +28,14 @@ const defaultDaysLabels = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];

const generateFullCalendar = (currentMonth: Date) => {
const monthStart = startOfMonth(currentMonth);
const monthEnd = endOfMonth(currentMonth);
const monthEnd = (() => {
const month = monthStart.getMonth();
if (month === 1) {
const year = monthStart.getFullYear();
return new Date(year, 1, isLeapYear(monthStart) ? 29 : 28);
}
return endOfMonth(monthStart);
})();
const startDate = addDays(monthStart, -monthStart.getDay());
const endDate = addDays(monthEnd, 6 - monthEnd.getDay());
return eachDayOfInterval({ start: startDate, end: endDate });
Expand All @@ -40,7 +49,8 @@ const MonthlyTimesheetCalendar: React.FC<MonthlyCalendarDataViewProps> = ({
locale = enGB,
daysLabels = defaultDaysLabels,
noDataText = "No Data",
classNames = {}
classNames = {},
t
}) => {
const [currentMonth, setCurrentMonth] = useState(new Date());
const calendarDates = useMemo(() => generateFullCalendar(currentMonth), [currentMonth]);
Expand All @@ -60,7 +70,7 @@ const MonthlyTimesheetCalendar: React.FC<MonthlyCalendarDataViewProps> = ({
onClick={handlePreviousMonth}
className="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300 dark:bg-primary-light hover:dark:bg-primary-light"
>
Previous
{t('common.PREV')}
</button>
<h2 className="text-xl font-bold">
{format(currentMonth, "MMMM yyyy", { locale: locale })}
Expand All @@ -69,7 +79,7 @@ const MonthlyTimesheetCalendar: React.FC<MonthlyCalendarDataViewProps> = ({
onClick={handleNextMonth}
className="px-4 py-2 bg-gray-200 dark:bg-primary-light rounded hover:bg-gray-300 hover:dark:bg-primary-light"
>
Next
{t('common.NEXT')}
</button>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { MultiSelect } from 'lib/components/custom-select';
import { Popover, PopoverContent, PopoverTrigger } from '@components/ui/popover';
import { SettingFilterIcon } from '@/assets/svg';
import { useTranslations } from 'next-intl';
import { clsxm } from '@/app/utils';
import { useTimelogFilterOptions } from '@/app/hooks';
import { useTimesheet } from '@/app/hooks/features/useTimesheet';
import { cn } from '@/lib/utils';

export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover() {
const [shouldRemoveItems, setShouldRemoveItems] = React.useState(false);
Expand Down Expand Up @@ -61,7 +61,7 @@ export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover
<label className="flex justify-between mb-1 text-sm text-gray-600">
<span className="text-[12px]">{t('manualTime.EMPLOYEE')}</span>
<span
className={clsxm(
className={cn(
'text-primary/10',
employee?.length > 0 && 'text-primary dark:text-primary-light'
)}
Expand All @@ -84,7 +84,7 @@ export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover
<label className="flex justify-between mb-1 text-sm text-gray-600">
<span className="text-[12px]">{t('sidebar.PROJECTS')}</span>
<span
className={clsxm(
className={cn(
'text-primary/10',
project?.length > 0 && 'text-primary dark:text-primary-light'
)}
Expand All @@ -109,7 +109,7 @@ export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover
<label className="flex justify-between mb-1 text-sm text-gray-600">
<span className="text-[12px]">{t('hotkeys.TASK')}</span>
<span
className={clsxm(
className={cn(
'text-primary/10',
task?.length > 0 && 'text-primary dark:text-primary-light'
)}
Expand All @@ -132,7 +132,7 @@ export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover
<label className="flex justify-between mb-1 text-sm text-gray-600">
<span className="text-[12px]">{t('common.STATUS')}</span>
<span
className={clsxm(
className={cn(
'text-primary/10',
statusState && 'text-primary dark:text-primary-light'
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@

import { formatDate } from '@/app/helpers';
import { DisplayTimeForTimesheet, TaskNameInfoDisplay, TotalDurationByDate, TotalTimeDisplay } from '@/lib/features';
import { clsxm } from '@app/utils';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@components/ui/accordion';
import { Badge } from '@components/ui/badge';
import { ArrowRightIcon } from 'assets/svg';
Expand All @@ -12,6 +11,7 @@ import { EmployeeAvatar } from './CompactTimesheetComponent';
import { useTimesheet } from '@/app/hooks/features/useTimesheet';
import { useTimelogFilterOptions } from '@/app/hooks';
import { TimesheetLog, TimesheetStatus } from '@/app/interfaces';
import { cn } from '@/lib/utils';

interface ITimesheetCard {
title?: string;
Expand Down Expand Up @@ -42,7 +42,7 @@ export function TimesheetCard({ ...props }: ITimesheetCard) {
</div>
<Button
variant='outline'
className={clsxm(
className={cn(
'h-9 px-3 py-2',
'border border-gray-200 ',
'text-[#282048] text-sm',
Expand All @@ -52,14 +52,14 @@ export function TimesheetCard({ ...props }: ITimesheetCard) {
aria-label="View timesheet details"
onClick={onClick}>
<span>{t('pages.timesheet.TIMESHEET_VIEW_DETAILS')}</span>
<ArrowRightIcon className={clsxm(
<ArrowRightIcon className={cn(
'h-6 w-6',
'text-[#282048] dark:text-[#6b7280]'
)} />
</Button>
</div>
<div
className={clsxm(
className={cn(
'h-16 w-16 rounded-lg p-5',
'flex items-center justify-center',
'text-white font-bold text-sm',
Expand All @@ -86,7 +86,7 @@ export const TimesheetCardDetail = ({ data }: { data?: Record<TimesheetStatus, T
{timesheetGroupByDate.map((plan, index) => {
return <div key={index}>
<div
className={clsxm(
className={cn(
'h-[48px] flex justify-between items-center w-full',
'bg-[#ffffffcc] dark:bg-dark--theme rounded-md border-1',
'border-gray-400 px-5 text-[#71717A] font-medium'
Expand All @@ -107,19 +107,19 @@ export const TimesheetCardDetail = ({ data }: { data?: Record<TimesheetStatus, T
return rows.length > 0 && status && <AccordionItem
key={status}
value={status === 'DENIED' ? 'REJECTED' : status}
className={clsxm("p-1 rounded")}
className={cn("p-1 rounded")}
>
<AccordionTrigger
style={{ backgroundColor: statusColor(status).bgOpacity }}
type="button"
className={clsxm(
className={cn(
'flex flex-row-reverse justify-end items-center w-full h-[50px] rounded-sm gap-x-2 hover:no-underline px-2',
statusColor(status).text
)}
>
<div className="flex items-center justify-between w-full space-x-1">
<div className="flex items-center space-x-1">
<div className={clsxm('p-2 rounded', statusColor(status).bg)}></div>
<div className={cn('p-2 rounded', statusColor(status).bg)}></div>
<div className="flex items-center gap-x-1">
<span className="text-base font-normal text-gray-400 uppercase">
{status === 'DENIED' ? 'REJECTED' : status}
Expand All @@ -144,17 +144,17 @@ export const TimesheetCardDetail = ({ data }: { data?: Record<TimesheetStatus, T
backgroundColor: statusColor(status).bgOpacity,
borderBottomColor: statusColor(status).bg
}}
className={clsxm(
className={cn(
'flex items-center border-b border-b-gray-200 dark:border-b-gray-600 space-x-4 p-1 h-[60px]'
)}>
<div className="flex-[2]">
<TaskNameInfoDisplay
task={task.task}
className={clsxm(
className={cn(
'shadow-[0px_0px_15px_0px_#e2e8f0] dark:shadow-transparent'
)}
taskTitleClassName={clsxm(
'text-sm text-ellipsis overflow-hidden text-sm'
taskTitleClassName={cn(
'text-sm !text-ellipsis !overflow-hidden text-sm'
)}
showSize={false}
dash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import { cn } from "@/lib/utils";
import { GroupedTimesheet } from "@/app/hooks/features/useTimesheet";
import { TotalDurationByDate } from "@/lib/features";
import { formatDate } from "@/app/helpers";
import { TranslationHooks } from "next-intl";

type WeeklyCalendarProps = {
t: TranslationHooks
data?: GroupedTimesheet[];
onDateClick?: (date: Date) => void;
renderDayContent?: (date: Date, plan?: GroupedTimesheet) => React.ReactNode;
Expand Down Expand Up @@ -38,6 +40,7 @@ const WeeklyTimesheetCalendar: React.FC<WeeklyCalendarProps> = ({
daysLabels = defaultDaysLabels,
noDataText = "No Data",
classNames = {},
t
}) => {
const [currentDate, setCurrentDate] = useState(new Date());

Expand All @@ -61,7 +64,7 @@ const WeeklyTimesheetCalendar: React.FC<WeeklyCalendarProps> = ({
onClick={handlePreviousWeek}
className="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300 dark:bg-primary-light hover:dark:bg-primary-light"
>
Previous
{t('common.PREV')}
</button>
<h2 className="text-xl font-bold">
{`Week of ${format(weekDates[0], "MMM d", { locale })} - ${format(
Expand All @@ -74,7 +77,7 @@ const WeeklyTimesheetCalendar: React.FC<WeeklyCalendarProps> = ({
onClick={handleNextWeek}
className="px-4 py-2 bg-gray-200 dark:bg-primary-light rounded hover:bg-gray-300 hover:dark:bg-primary-light"
>
Next
{t('common.NEXT')}
</button>
</div>

Expand Down
Loading
Loading