Skip to content

Commit

Permalink
[Feat]: Add Multilingual Support for Timesheet Component (#3225)
Browse files Browse the repository at this point in the history
* feat: Add multilingual support for timesheet component

* fix: bug

* improv: update multilingual support for timesheet component

* fix: change

* fix: sug-coderabbita

* fix: deepscan
  • Loading branch information
Innocent-Akim authored Nov 3, 2024
1 parent cc4e2bc commit 448fb96
Show file tree
Hide file tree
Showing 21 changed files with 357 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export function FilterWithStatus({
<Button
key={index}
className={clsxm(
'group flex items-center justify-start h-[2.2rem] rounded-xl border w-full',
'dark:bg-dark--theme-light dark:border-gray-700 bg-transparent text[#71717A] w-[80px]',
activeStatus === label && 'text-primary bg-white shadow-lg font-bold'
'group flex items-center justify-start h-[2.2rem] rounded-xl w-full',
'dark:bg-dark--theme-light dark:border-gray-700 bg-transparent text-[#71717A] w-[80px]',
activeStatus === label && 'text-primary bg-white shadow-lg font-bold border'
)}
onClick={() => onToggle(label)}>
<span className={clsxm('font-medium ml-1 text-[#7E7991]', `${activeStatus === label ? "text-primary" : ""}`)}>{label}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function FrequencySelect() {
<Select
value={selectedValue}
onValueChange={handleSelectChange}>
<SelectTrigger className="w-[170px] overflow-hidden text-clip border border-gray-200 dark:border-gray-700 bg-white dark:bg-dark--theme-light focus:ring-2 focus:ring-transparent">
<SelectTrigger className="w-auto overflow-hidden text-clip border border-gray-200 dark:border-gray-700 bg-white dark:bg-dark--theme-light focus:ring-2 focus:ring-transparent">
<SelectValue placeholder="Select a daily" />
</SelectTrigger>
<SelectContent>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { clsxm } from "@/app/utils";
import { TranslationHooks } from "next-intl";
import { ReactNode } from "react";
import { FaClipboardCheck } from "react-icons/fa6";
import { IoClose } from "react-icons/io5";
Expand All @@ -25,20 +26,21 @@ export const TimesheetButton = ({ className, icon, onClick, title }: ITimesheetB
export type StatusType = "Pending" | "Approved" | "Rejected";

// eslint-disable-next-line @typescript-eslint/no-empty-function
export const getTimesheetButtons = (status: StatusType) => {
export const getTimesheetButtons = (status: StatusType, t: TranslationHooks) => {

const buttonsConfig: Record<StatusType, { icon: JSX.Element; title: string }[]> = {
Pending: [
{ icon: <FaClipboardCheck className="!text-[#2932417c] rounded" />, title: "Approve Selected" },
{ icon: <IoClose className="!bg-[#2932417c] rounded" />, title: "Reject Selected" },
{ icon: <RiDeleteBin6Fill className="!text-[#2932417c] rounded" />, title: "Delete Selected" }
{ icon: <FaClipboardCheck className="!text-[#2932417c] rounded" />, title: t('pages.timesheet.TIMESHEET_ACTION_APPROVE_SELECTED') },
{ icon: <IoClose className="!bg-[#2932417c] rounded" />, title: t('pages.timesheet.TIMESHEET_ACTION_REJECT_SELECTED') },
{ icon: <RiDeleteBin6Fill className="!text-[#2932417c] rounded" />, title: t('pages.timesheet.TIMESHEET_ACTION_DELETE_SELECTED') }
],
Approved: [
{ icon: <IoClose className="!bg-[#2932417c] rounded" />, title: "Reject Selected" },
{ icon: <RiDeleteBin6Fill className="!text-[#2932417c] rounded" />, title: "Delete Selected" }
{ icon: <IoClose className="!bg-[#2932417c] rounded" />, title: t('pages.timesheet.TIMESHEET_ACTION_REJECT_SELECTED') },
{ icon: <RiDeleteBin6Fill className="!text-[#2932417c] rounded" />, title: t('pages.timesheet.TIMESHEET_ACTION_DELETE_SELECTED') }
],
Rejected: [
{ icon: <FaClipboardCheck className="!text-[#2932417c] rounded" />, title: "Approve Selected" },
{ icon: <RiDeleteBin6Fill className="!text-[#2932417c] rounded" />, title: "Delete Selected" }
{ icon: <FaClipboardCheck className="!text-[#2932417c] rounded" />, title: t('pages.timesheet.TIMESHEET_ACTION_APPROVE_SELECTED') },
{ icon: <RiDeleteBin6Fill className="!text-[#2932417c] rounded" />, title: t('pages.timesheet.TIMESHEET_ACTION_DELETE_SELECTED') }
]
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { clsxm } from '@app/utils';
import { ArrowRightIcon } from 'assets/svg';
import { Button, Card } from 'lib/components';
import { useTranslations } from 'next-intl';
import { ReactNode } from 'react';

interface ITimesheetCard {
Expand All @@ -19,6 +20,7 @@ interface ITimesheetCard {

export function TimesheetCard({ ...props }: ITimesheetCard) {
const { icon, title, date, description, hours, count, onClick, classNameIcon } = props;
const t = useTranslations();
return (
<Card
aria-label={`Timesheet card for ${title}`}
Expand All @@ -41,7 +43,7 @@ export function TimesheetCard({ ...props }: ITimesheetCard) {
)}
aria-label="View timesheet details"
onClick={onClick}>
<span>View Details</span>
<span>{t('pages.timesheet.TIMESHEET_VIEW_DETAILS')}</span>
<ArrowRightIcon className={clsxm(
'h-6 w-6',
'text-[#282048] dark:text-[#6b7280]'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import { IDailyPlan } from '@/app/interfaces'
import { DataTableTimeSheet } from 'lib/features/integrations/calendar'
import { IDailyPlan } from '@/app/interfaces';
import { DataTableTimeSheet } from 'lib/features/integrations/calendar';
import { useTranslations } from 'next-intl';

export function TimesheetView({ data }: { data?: IDailyPlan[] }) {
const t = useTranslations();
return (
<div className='grow h-full w-full bg-[#FFFFFF]'>
<DataTableTimeSheet data={data} />
{data ? (
data.length > 0 ? (
<DataTableTimeSheet data={data} />
) : (
<div className="flex items-center justify-center h-full">
<p>{t('pages.timesheet.NO_ENTRIES_FOUND')}</p>
</div>
)
) : (
<div className="flex items-center justify-center h-full">
<p>{t('pages.timesheet.LOADING')}</p>
</div>
)}
</div>
)
}
15 changes: 10 additions & 5 deletions apps/web/app/[locale]/timesheet/[memberId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { CalendarDaysIcon, Clock, User2 } from 'lucide-react';
import { GrTask } from "react-icons/gr";
import { GoSearch } from "react-icons/go";
import { getGreeting } from '@/app/helpers';
import { TranslationHooks } from 'next-intl';

type TimesheetViewMode = "ListView" | "CalendarView";

Expand All @@ -23,6 +24,7 @@ type ViewToggleButtonProps = {
active: boolean;
icon: React.ReactNode;
onClick: () => void;
t: TranslationHooks
};

interface FooterTimeSheetProps {
Expand All @@ -48,7 +50,7 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb
() => [
{ title: JSON.parse(t('pages.home.BREADCRUMB')), href: '/' },
{ title: activeTeam?.name || '', href: '/' },
{ title: 'Timesheet', href: `/${currentLocale}/timesheet/${params.memberId}` }
{ title: t("pages.timesheet.TIMESHEET_TITLE"), href: `/${currentLocale}/timesheet/${params.memberId}` }
],
[activeTeam?.name, currentLocale, t]
);
Expand All @@ -72,8 +74,8 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb
<Container fullWidth={fullWidth} className='h-full pt-14'>
<div className='py-5'>
<div className='flex flex-col justify-start items-start gap-y-2'>
<h1 className='!text-[23px] font-bold text-[#282048]'>{getGreeting()}, {username} !</h1>
<span className='text-[16px] text-[#3D5A80]'>This is your personal timesheet dashboard, showing you what needs your attention now.</span>
<h1 className='!text-[23px] font-bold text-[#282048]'>{getGreeting(t)}, {username} !</h1>
<span className='text-[16px] text-[#3D5A80]'>{t('pages.timesheet.HEADING_DESCRIPTION')}</span>
</div>
<div className='flex items-center w-full justify-between gap-6 pt-4'>
<TimesheetCard
Expand Down Expand Up @@ -106,12 +108,14 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb
mode='ListView'
active={timesheetNavigator === 'ListView'}
onClick={() => setTimesheetNavigator('ListView')}
t={t}
/>
<ViewToggleButton
icon={<CalendarDaysIcon size={20} className='!text-sm' />}
mode='CalendarView'
active={timesheetNavigator === 'CalendarView'}
onClick={() => setTimesheetNavigator('CalendarView')}
t={t}
/>
</div>
<div className='flex items-center h-9 w-[700px] bg-white gap-x-2 px-2 border border-gray-200 rounded-sm mb-2'>
Expand Down Expand Up @@ -163,7 +167,8 @@ const ViewToggleButton: React.FC<ViewToggleButtonProps> = ({
mode,
active,
icon,
onClick
onClick,
t
}) => (
<button
onClick={onClick}
Expand All @@ -172,6 +177,6 @@ const ViewToggleButton: React.FC<ViewToggleButtonProps> = ({
active && 'border-b-primary text-primary border-b-2 bg-[#F1F5F9]'
)}>
{icon}
<span>{mode === 'ListView' ? 'List View' : 'Calendar View'}</span>
<span>{mode === 'ListView' ? t('pages.timesheet.VIEWS.LIST') : t('pages.timesheet.VIEWS.CALENDAR')}</span>
</button>
);
9 changes: 5 additions & 4 deletions apps/web/app/helpers/date.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import moment from 'moment';
import * as momentTimezone from 'moment-timezone';
import { TranslationHooks } from 'next-intl';

const months: { [key: string]: string } = {
'01': 'January',
Expand Down Expand Up @@ -199,7 +200,7 @@ export function formatTimeString(timeString: string): string {
return result.length ? result : '0h 00m';
}

export const getGreeting = () => {
export const getGreeting = (t: TranslationHooks) => {
const GREETING_TIMES = {
MORNING_START: 5,
AFTERNOON_START: 12,
Expand All @@ -208,10 +209,10 @@ export const getGreeting = () => {
const currentHour = new Date().getHours();

if (currentHour >= GREETING_TIMES.MORNING_START && currentHour < GREETING_TIMES.AFTERNOON_START) {
return "Good morning";
return t('pages.timesheet.GREETINGS.GOOD_MORNING');
} else if (currentHour >= GREETING_TIMES.AFTERNOON_START && currentHour < GREETING_TIMES.EVENING_START) {
return "Good afternoon";
return t('pages.timesheet.GREETINGS.GOOD_AFTERNOON');
} else {
return "Good evening";
return t('pages.timesheet.GREETINGS.GOOD_EVENING');
}
}
11 changes: 7 additions & 4 deletions apps/web/lib/features/integrations/calendar/table-time-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import { statusColor } from "@/lib/components"
import { Badge } from '@components/ui/badge'
import { IDailyPlan } from "@/app/interfaces"
import { StatusType, getTimesheetButtons } from "@/app/[locale]/timesheet/[memberId]/components"
import { useTranslations } from "next-intl"



Expand Down Expand Up @@ -90,7 +91,7 @@ export const columns: ColumnDef<TimeSheet>[] = [
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
<span className="capitalize !text-sm break-words whitespace-break-spaces sm:text-base !truncate !overflow-hidden">{row.original.id}</span>
<span className="capitalize !text-sm break-words whitespace-break-spaces sm:text-base !truncate !overflow-hidden">{row.original.task}</span>
</div>
),
},
Expand Down Expand Up @@ -174,6 +175,7 @@ export const columns: ColumnDef<TimeSheet>[] = [


export function DataTableTimeSheet({ data }: { data?: IDailyPlan[] }) {
const t = useTranslations();
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})
Expand Down Expand Up @@ -247,7 +249,7 @@ export function DataTableTimeSheet({ data }: { data?: IDailyPlan[] }) {
</Badge>
</div>
<div className="flex items-center gap-2 p-x-1">
{getTimesheetButtons(status as StatusType)}
{getTimesheetButtons(status as StatusType, t)}
</div>
</div>
</AccordionTrigger>
Expand Down Expand Up @@ -421,6 +423,7 @@ const TaskDetails = ({ description, name }: { description: string; name: string
};

export const StatusTask = () => {
const t = useTranslations();
return (
<>
<DropdownMenuSub>
Expand Down Expand Up @@ -448,12 +451,12 @@ export const StatusTask = () => {
<DropdownMenuSubContent>
<DropdownMenuItem textValue={'Oui'} className="cursor-pointer">
<div className="flex items-center gap-3">
<span>Oui</span>
<span>{t('pages.timesheet.BILLABLE.YES')}</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem textValue={'No'} className="cursor-pointer">
<div className="flex items-center gap-3">
<span>No</span>
<span>{t('pages.timesheet.BILLABLE.NO')}</span>
</div>
</DropdownMenuItem>
</DropdownMenuSubContent>
Expand Down
23 changes: 23 additions & 0 deletions apps/web/locales/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,29 @@
"maintenance": {
"HEADING_TITLE": "نحن تحت الصيانة",
"HEADING_DESCRIPTION": "نقوم حاليًا بتحديث موقعنا الإلكتروني لخدمتك بشكل أفضل. يرجى التحقق مرة أخرى في وقت لاحق."
},
"timesheet": {
"TIMESHEET_TITLE": "سجل الساعات",
"TIMESHEET_VIEW_DETAILS": "عرض التفاصيل",
"TIMESHEET_ACTION_APPROVE_SELECTED": "الموافقة على المحدد",
"TIMESHEET_ACTION_REJECT_SELECTED": "رفض المحدد",
"TIMESHEET_ACTION_DELETE_SELECTED": "حذف المحدد",
"HEADING_DESCRIPTION": "هذا هو لوحة المعلومات الشخصية الخاصة بك، تظهر لك ما يحتاج إلى انتباهك الآن.",
"GREETINGS": {
"GOOD_MORNING": "صباح الخير",
"GOOD_AFTERNOON": "مساء الخير",
"GOOD_EVENING": "مساء الخير"
},
"VIEWS": {
"LIST": "عرض القائمة",
"CALENDAR": "عرض التقويم"
},
"BILLABLE": {
"YES": "نعم",
"NO": "لا"
},
"LOADING": "جارٍ تحميل بيانات سجل الزمن...",
"NO_ENTRIES_FOUND": "لم يتم العثور على إدخالات سجل الزمن"
}
},
"timer": {
Expand Down
23 changes: 23 additions & 0 deletions apps/web/locales/bg.json
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,29 @@
"maintenance": {
"HEADING_TITLE": "Ние сме в процес на поддръжка",
"HEADING_DESCRIPTION": "В момента актуализираме нашия уебсайт, за да ви служим по-добре. Моля, проверете отново по-късно."
},
"timesheet": {
"TIMESHEET_TITLE": "Таблица на часовете",
"TIMESHEET_VIEW_DETAILS": "Преглед на детайлите",
"TIMESHEET_ACTION_APPROVE_SELECTED": "Одобрете избраното",
"TIMESHEET_ACTION_REJECT_SELECTED": "Отхвърлете избраното",
"TIMESHEET_ACTION_DELETE_SELECTED": "Изтрийте избраното",
"HEADING_DESCRIPTION": "Това е вашата лична табло на часовете, показваща какво изисква вашето внимание сега.",
"GREETINGS": {
"GOOD_MORNING": "Добро утро",
"GOOD_AFTERNOON": "Добър следобед",
"GOOD_EVENING": "Добър вечер"
},
"VIEWS": {
"LIST": "Списъчен изглед",
"CALENDAR": "Календарен изглед"
},
"BILLABLE": {
"YES": "Да",
"NO": "Не"
},
"LOADING": "Зареждане на данни за таблицата на работното време...",
"NO_ENTRIES_FOUND": "Няма намерени записи за таблицата на работното време"
}
},
"timer": {
Expand Down
27 changes: 25 additions & 2 deletions apps/web/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,31 @@
"HEADING_DESCRIPTION": "If the problem persists, send a distress signal to our support team."
},
"maintenance": {
"HEADING_TITLE": "We zijn in onderhoud ",
"HEADING_DESCRIPTION": "Momenteel zijn wij onze website aan het vernieuwen om u nog beter van dienst te kunnen zijn. Kom later nog eens terug."
"HEADING_TITLE": "Wir führen Wartungsarbeiten durch",
"HEADING_DESCRIPTION": "Wir aktualisieren derzeit unsere Website, um Ihnen einen noch besseren Service bieten zu können. Bitte versuchen Sie es später erneut."
},
"timesheet": {
"TIMESHEET_TITLE": "Zeiterfassung",
"TIMESHEET_VIEW_DETAILS": "Details anzeigen",
"TIMESHEET_ACTION_APPROVE_SELECTED": "Ausgewählte genehmigen",
"TIMESHEET_ACTION_REJECT_SELECTED": "Ausgewählte ablehnen",
"TIMESHEET_ACTION_DELETE_SELECTED": "Ausgewählte löschen",
"HEADING_DESCRIPTION": "Dies ist Ihr persönliches Zeiterfassungs-Dashboard, das Ihnen zeigt, was jetzt Ihre Aufmerksamkeit erfordert.",
"GREETINGS": {
"GOOD_MORNING": "Guten Morgen",
"GOOD_AFTERNOON": "Guten Nachmittag",
"GOOD_EVENING": "Guten Abend"
},
"VIEWS": {
"LIST": "Listenansicht",
"CALENDAR": "Kalenderansicht"
},
"BILLABLE": {
"YES": "Ja",
"NO": "Nein"
},
"LOADING": "Lade Zeiterfassungsdaten...",
"NO_ENTRIES_FOUND": "Keine Zeiterfassungseinträge gefunden"
}
},
"timer": {
Expand Down
23 changes: 23 additions & 0 deletions apps/web/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,29 @@
"maintenance": {
"HEADING_TITLE": "We are Under Maintenance",
"HEADING_DESCRIPTION": "We are updating our website to serve you better. Please check back later."
},
"timesheet": {
"TIMESHEET_TITLE": "Timesheet",
"TIMESHEET_VIEW_DETAILS": "View Details",
"TIMESHEET_ACTION_APPROVE_SELECTED": "Approve selected",
"TIMESHEET_ACTION_REJECT_SELECTED": "Reject selected",
"TIMESHEET_ACTION_DELETE_SELECTED": "Delete selected",
"HEADING_DESCRIPTION": "This is your personal timesheet dashboard, showing you what needs your attention now.",
"GREETINGS": {
"GOOD_MORNING": "Good morning",
"GOOD_AFTERNOON": "Good afternoon",
"GOOD_EVENING": "Good evening"
},
"VIEWS": {
"LIST": "List View",
"CALENDAR": "Calendar View"
},
"BILLABLE": {
"YES": "Yes",
"NO": "No"
},
"LOADING": "Loading timesheet data...",
"NO_ENTRIES_FOUND": "No timesheet entries found"
}
},
"timer": {
Expand Down
Loading

0 comments on commit 448fb96

Please sign in to comment.