Skip to content

Commit 77dbebe

Browse files
[Feat]:create TimesheetCard component with customizable props (#3191)
* feat:create TimesheetCard component with customizable props * refact: code * refact: code * fix: cspell * feat(timesheet): add filter components * fix: deep scan * fix: build
1 parent 45fb45e commit 77dbebe

File tree

16 files changed

+455
-20
lines changed

16 files changed

+455
-20
lines changed

.cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@
355355
"tailess",
356356
"Tailess",
357357
"tailwindcss",
358+
"timesheet-viewMode",
358359
"tanstack",
359360
"taskid",
360361
"taskstatus",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React from 'react'
2+
3+
export function CalendarView() {
4+
return (
5+
<div className='grow h-full w-full bg-[#FFFFFF]'>
6+
7+
</div>
8+
)
9+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React, { HTMLAttributes } from 'react';
2+
import { Button } from 'lib/components';
3+
import { clsxm } from '@app/utils';
4+
5+
export type FilterStatus = "All Tasks" | "Pending" | "Approved" | "Rejected";
6+
export function FilterWithStatus({
7+
activeStatus,
8+
onToggle,
9+
className
10+
}: {
11+
activeStatus: FilterStatus;
12+
onToggle: (status: FilterStatus) => void;
13+
className?: HTMLAttributes<HTMLDivElement>
14+
}) {
15+
const buttonData: { label: FilterStatus; count: number; icon: React.ReactNode }[] = [
16+
{ label: 'All Tasks', count: 46, icon: <i className="icon-all" /> },
17+
{ label: 'Pending', count: 12, icon: <i className="icon-pending" /> },
18+
{ label: 'Approved', count: 28, icon: <i className="icon-approved" /> },
19+
{ label: 'Rejected', count: 6, icon: <i className="icon-rejected" /> },
20+
];
21+
22+
return (
23+
<div className={clsxm('grid grid-cols-4 h-[2.4rem] items-center justify-start bg-[#e2e8f0aa] rounded-xl w-full', className)}>
24+
{buttonData.map(({ label, count, icon }, index) => (
25+
<Button
26+
key={index}
27+
className={clsxm('group flex items-center justify-start h-[2.4rem] rounded-xl border dark:bg-dark--theme-light dark:border-gray-700 bg-[#e2e8f0aa] text[#71717A]', `${activeStatus === label ? "text-primary bg-white shadow-lg font-bold" : ""}`)}
28+
onClick={() => onToggle(label)}>
29+
<span className={clsxm('font-medium ml-1 text-[#71717A]', `${activeStatus === label ? "text-primary" : ""}`)}>{label}</span>
30+
<span className='font-medium ml-1 text-[#71717A]'>{count}</span>
31+
</Button>
32+
))}
33+
</div>
34+
);
35+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import React from "react";
2+
import {
3+
Select,
4+
SelectContent,
5+
SelectGroup,
6+
SelectItem,
7+
SelectTrigger,
8+
SelectValue,
9+
} from "@components/ui/select"
10+
import {
11+
DropdownMenu,
12+
DropdownMenuContent,
13+
DropdownMenuItem,
14+
DropdownMenuPortal,
15+
DropdownMenuSub,
16+
DropdownMenuSubContent,
17+
DropdownMenuSubTrigger,
18+
DropdownMenuTrigger,
19+
} from "@components/ui/dropdown-menu"
20+
import { Button } from "lib/components/button";
21+
22+
export function FrequencySelect() {
23+
const [selectedValue, setSelectedValue] = React.useState<string | undefined>(undefined);
24+
25+
const handleSelectChange = (value: string) => {
26+
setSelectedValue(value);
27+
};
28+
29+
return (
30+
<Select
31+
value={selectedValue}
32+
onValueChange={handleSelectChange}>
33+
<SelectTrigger className="w-[180px] border border-gray-200 dark:border-gray-700 bg-white dark:bg-dark--theme-light focus:ring-2 focus:ring-transparent">
34+
<SelectValue placeholder="Select a daily" />
35+
</SelectTrigger>
36+
<SelectContent>
37+
<SelectGroup>
38+
<SelectItem value="daily">Daily</SelectItem>
39+
<SelectItem value="weekly">Weekly</SelectItem>
40+
<SelectItem value="monthly">Monthly</SelectItem>
41+
</SelectGroup>
42+
</SelectContent>
43+
</Select>
44+
);
45+
}
46+
47+
48+
49+
50+
export const FilterTaskActionMenu = () => {
51+
// const handleCopyPaymentId = () => navigator.clipboard.writeText(idTasks);
52+
return (
53+
<DropdownMenu open={true} >
54+
<DropdownMenuTrigger asChild>
55+
<Button variant="ghost" className="h-8 w-8 p-0 text-sm sm:text-base">
56+
{/* <span className="sr-only">Open menu</span> */}
57+
<span>Today</span>
58+
</Button>
59+
</DropdownMenuTrigger>
60+
<DropdownMenuContent align="end" className="z-50">
61+
<DropdownMenuItem className="cursor-pointer" >
62+
Today
63+
</DropdownMenuItem>
64+
<DropdownMenuItem className="cursor-pointer" >
65+
Last 7 days
66+
</DropdownMenuItem>
67+
<DropdownMenuItem className="cursor-pointer" >
68+
Last 30 days
69+
</DropdownMenuItem>
70+
<DropdownMenuItem className="cursor-pointer" >
71+
This year (2024)
72+
{/* ({new Date().getFullYear()}) */}
73+
</DropdownMenuItem>
74+
{/* <DropdownMenuSeparator /> */}
75+
<CustomDateRange />
76+
</DropdownMenuContent>
77+
</DropdownMenu>
78+
);
79+
};
80+
81+
export const CustomDateRange = () => {
82+
return (
83+
<DropdownMenuSub>
84+
<DropdownMenuSubTrigger>
85+
<span>Custom Date Range</span>
86+
</DropdownMenuSubTrigger>
87+
<DropdownMenuPortal>
88+
<DropdownMenuSubContent>
89+
<DropdownMenuItem className="cursor-pointer">
90+
<div className="flex items-center gap-3">
91+
<div className="h-1 w-1 rounded-full bg-black dark:bg-white"></div>
92+
<span>Calendar</span>
93+
</div>
94+
</DropdownMenuItem>
95+
</DropdownMenuSubContent>
96+
</DropdownMenuPortal>
97+
</DropdownMenuSub>
98+
)
99+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
2+
import { clsxm } from '@app/utils';
3+
import { ArrowRightIcon } from 'assets/svg';
4+
import { Button, Card } from 'lib/components';
5+
import React, { ReactNode } from 'react'
6+
7+
interface ITimesheetCard {
8+
title?: string;
9+
date?: string
10+
description?: string;
11+
hours?: string;
12+
count?: number;
13+
color?: string;
14+
icon?: ReactNode;
15+
classNameIcon?: string
16+
onClick?: () => void;
17+
}
18+
19+
20+
export function TimesheetCard({ ...props }: ITimesheetCard) {
21+
const { icon, title, date, description, hours, count, onClick, classNameIcon } = props;
22+
return (
23+
<Card
24+
aria-label={`Timesheet card for ${title}`}
25+
shadow='custom'
26+
className='w-full h-[175px] rounded-md border border-gray-200 flex gap-8 shadow shadow-gray-100 p-3'>
27+
<div className='!gap-8 w-full space-y-4 '>
28+
<div className='flex flex-col gap-1 justify-start items-start'>
29+
<h1 className='text-2xl md:text-[25px] font-bold truncate w-full'>{hours ?? count}</h1>
30+
<h2 className='text-base md:text-[16px] font-medium text-[#282048] truncate w-full'>{title}</h2>
31+
<span className='text-sm md:text-[14px] text-[#3D5A80] truncate w-full'>{date ?? description}</span>
32+
</div>
33+
<Button
34+
variant='outline'
35+
className={clsxm(
36+
'h-9 px-3 py-2',
37+
'border border-gray-200',
38+
'text-[#282048] text-sm',
39+
'flex items-center',
40+
'hover:bg-gray-50 focus:ring-2 focus:ring-offset-2 focus:ring-gray-200'
41+
)}
42+
aria-label="View timesheet details"
43+
onClick={onClick}>
44+
<span>View Details</span>
45+
<ArrowRightIcon className={clsxm(
46+
'h-6 w-6',
47+
'text-[#282048] dark:text-[#6b7280]'
48+
)} />
49+
</Button>
50+
</div>
51+
<Card
52+
shadow='custom'
53+
className={clsxm(
54+
'h-7 w-7',
55+
'flex items-center justify-center',
56+
'text-white font-bold text-sm',
57+
'shadow-lg',
58+
classNameIcon
59+
)}
60+
aria-hidden="true">
61+
{icon}
62+
</Card>
63+
</Card>
64+
)
65+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from 'react'
2+
import { FilterWithStatus } from './FilterWithStatus'
3+
import { FrequencySelect } from '.';
4+
import { Button } from 'lib/components';
5+
import { SettingFilterIcon } from 'assets/svg';
6+
7+
export function TimesheetFilter() {
8+
return (
9+
<div className="grid grid-cols-3 w-full">
10+
<div className="col-span-1">
11+
<FilterWithStatus
12+
activeStatus="Rejected"
13+
onToggle={(label) => {
14+
console.log(label)
15+
}}
16+
/>
17+
</div>
18+
<div className="col-span-1"></div>
19+
<div className="col-span-1">
20+
<div className='flex gap-2'>
21+
<FrequencySelect />
22+
<button
23+
onClick={() => null}
24+
className='flex items-center justify-center h-10 rounded-lg bg-white dark:bg-dark--theme-light border dark:border-gray-700 hover:bg-white p-3 gap-2' >
25+
<SettingFilterIcon className="text-gray-700 dark:text-white w-3.5" strokeWidth="1.8" />
26+
<span className="text-gray-700 dark:text-white">Filter</span>
27+
</button>
28+
<Button
29+
onClick={() => null}
30+
variant='outline'
31+
className='bg-primary/5 dark:bg-primary-light h-10 w-[2.5rem] font-medium'>
32+
Add Time
33+
</Button>
34+
</div>
35+
</div>
36+
</div>
37+
38+
)
39+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { DataTableTimeSheet } from 'lib/features/integrations/calendar'
2+
import React from 'react'
3+
4+
export function TimesheetView() {
5+
return (
6+
<div className='grow h-full w-full bg-[#FFFFFF]'>
7+
<DataTableTimeSheet />
8+
</div>
9+
)
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export * from './TimesheetCard';
2+
export * from './TimesheetView';
3+
export * from './CalendarView';
4+
export * from './TimesheetFilter';
5+
export * from './FrequencySelect';
6+
export * from './FilterWithStatus';

0 commit comments

Comments
 (0)