Skip to content

Commit eb4a693

Browse files
[Feat]: Add Timesheet Creation Functionality (#3417)
* feat: add timesheet creation functionality * fix: coderabbitai * fix: coderabbitai
1 parent b6cded9 commit eb4a693

File tree

8 files changed

+180
-102
lines changed

8 files changed

+180
-102
lines changed

apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx

Lines changed: 131 additions & 60 deletions
Large diffs are not rendered by default.

apps/web/app/[locale]/timesheet/[memberId]/components/CompactTimesheetComponent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const LoadingSpinner = ({ className }: { className?: string }) => (
2323
</svg>
2424
);
2525

26-
const ImageWithLoader = ({ imageUrl, alt, className = "w-6 h-6 rounded-full font-bold" }:
26+
const ImageWithLoader = ({ imageUrl, alt, className = "w-6 h-6 rounded-full" }:
2727
{ imageUrl: string; alt: string; className?: string }) => {
2828
const [isLoading, setIsLoading] = React.useState(true);
2929
return (

apps/web/app/[locale]/timesheet/[memberId]/components/EditTaskModal.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,13 @@ export function EditTaskModal({ isOpen, closeModal, dataTimesheet }: IEditTaskMo
124124
}).catch((error) => {
125125
toast({
126126
title: 'Error during modification',
127-
description: `An error occurred: ${error}. The timesheet could not be modified.`,
127+
description: "Failed to modify timesheet. Please try again.",
128128
variant: 'destructive',
129129
className: 'bg-red-50 text-red-600 border-red-500 z-[10000px]'
130130
});
131-
closeModal()
131+
if (!error) {
132+
closeModal();
133+
}
132134
});
133135
}, [dateRange, timeRange, timesheetData, dataTimesheet, updateTimesheet]);
134136

apps/web/app/[locale]/timesheet/[memberId]/components/TimeSheetFilterPopover.tsx

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import React from 'react';
22
import { useOrganizationProjects, useOrganizationTeams, useTeamTasks } from '@app/hooks';
33
import { Button } from '@components/ui/button';
4-
import { statusOptions } from '@app/constants';
54
import { MultiSelect } from 'lib/components/custom-select';
65
import { Popover, PopoverContent, PopoverTrigger } from '@components/ui/popover';
76
import { SettingFilterIcon } from '@/assets/svg';
87
import { useTranslations } from 'next-intl';
98
import { useTimelogFilterOptions } from '@/app/hooks';
109
import { useTimesheet } from '@/app/hooks/features/useTimesheet';
1110
import { cn } from '@/lib/utils';
11+
import { statusTable } from './TimesheetAction';
1212

1313
export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover() {
1414
const [shouldRemoveItems, setShouldRemoveItems] = React.useState(false);
@@ -19,7 +19,7 @@ export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover
1919
const t = useTranslations();
2020
const { setEmployeeState, setProjectState, setStatusState, setTaskState, employee, project, statusState, task } =
2121
useTimelogFilterOptions();
22-
const { timesheet, statusTimesheet } = useTimesheet({})
22+
const { timesheet, statusTimesheet, isManage } = useTimesheet({})
2323

2424
React.useEffect(() => {
2525
if (shouldRemoveItems) {
@@ -59,29 +59,31 @@ export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover
5959
<span className="text-gray-700 dark:text-white">{t('common.FILTER')}</span>
6060
</div>
6161
<div className="grid gap-5">
62-
<div className="">
63-
<label className="flex justify-between mb-1 text-sm text-gray-600">
64-
<span className="text-[12px]">{t('manualTime.EMPLOYEE')}</span>
65-
<span
66-
className={cn(
67-
'text-primary/10',
68-
employee?.length > 0 && 'text-primary dark:text-primary-light'
69-
)}
70-
>
71-
{t('common.CLEAR')}
72-
</span>
73-
</label>
74-
<MultiSelect
75-
localStorageKey="timesheet-select-filter-employee"
76-
removeItems={shouldRemoveItems}
77-
items={activeTeam?.members ?? []}
78-
itemToString={(members) => (members ? members.employee.fullName : '')}
79-
itemId={(item) => item.id}
80-
onValueChange={(selectedItems) => setEmployeeState(selectedItems as any)}
81-
multiSelect={true}
82-
triggerClassName="dark:border-gray-700"
83-
/>
84-
</div>
62+
{isManage && (
63+
<div className="">
64+
<label className="flex justify-between mb-1 text-sm text-gray-600">
65+
<span className="text-[12px]">{t('manualTime.EMPLOYEE')}</span>
66+
<span
67+
className={cn(
68+
'text-primary/10',
69+
employee?.length > 0 && 'text-primary dark:text-primary-light'
70+
)}
71+
>
72+
{t('common.CLEAR')}
73+
</span>
74+
</label>
75+
<MultiSelect
76+
localStorageKey="timesheet-select-filter-employee"
77+
removeItems={shouldRemoveItems}
78+
items={activeTeam?.members ?? []}
79+
itemToString={(members) => (members ? members.employee.fullName : '')}
80+
itemId={(item) => item.id}
81+
onValueChange={(selectedItems) => setEmployeeState(selectedItems as any)}
82+
multiSelect={true}
83+
triggerClassName="dark:border-gray-700"
84+
/>
85+
</div>
86+
)}
8587
<div className="">
8688
<label className="flex justify-between mb-1 text-sm text-gray-600">
8789
<span className="text-[12px]">{t('sidebar.PROJECTS')}</span>
@@ -145,9 +147,9 @@ export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover
145147
<MultiSelect
146148
localStorageKey="timesheet-select-filter-status"
147149
removeItems={shouldRemoveItems}
148-
items={statusOptions}
149-
itemToString={(status) => (status ? status.value : '')}
150-
itemId={(item) => item.value}
150+
items={statusTable?.flat()}
151+
itemToString={(status) => (status ? status.label : '')}
152+
itemId={(item) => item.label}
151153
onValueChange={(selectedItems) => setStatusState(selectedItems as any)}
152154
multiSelect={true}
153155
triggerClassName="dark:border-gray-700"

apps/web/app/hooks/features/useTimesheet.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export function useTimesheet({
167167
const response = await queryUpdateTimesheet(timesheet);
168168
setTimesheet(prevTimesheet =>
169169
prevTimesheet.map(item =>
170-
item.id === response.data.id
170+
item.timesheet?.id === response.data.id
171171
? response.data
172172
: item
173173
)

apps/web/app/interfaces/timer/ITimerLog.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,23 +137,23 @@ export interface UpdateTimesheetStatus extends BaseEntity {
137137
}
138138
export interface UpdateTimesheet extends Pick<
139139
Partial<TimesheetLog>,
140+
| 'id'
140141
| 'reason'
141142
| 'organizationContactId'
142143
| 'description'
143144
| 'organizationTeamId'
144145
| 'projectId'
145146
| 'taskId'
147+
| 'employeeId'
148+
| 'organizationId'
149+
| 'tenantId'
150+
| 'logType'
151+
| 'source'
146152
>,
147153
Pick<
148154
TimesheetLog,
149-
| 'id'
150155
| 'startedAt'
151156
| 'stoppedAt'
152-
| 'tenantId'
153-
| 'logType'
154-
| 'source'
155-
| 'employeeId'
156-
| 'organizationId'
157157
> {
158158
isBillable: boolean;
159159
}

apps/web/components/ui/sidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ const SidebarMenuAction = React.forwardRef<
540540
'peer-data-[size=lg]/menu-button:top-2.5',
541541
'group-data-[collapsible=icon]:hidden',
542542
showOnHover &&
543-
'group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0',
543+
'group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0',
544544
className
545545
)}
546546
{...props}

apps/web/lib/features/multiple-select/index.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
SelectLabel,
1010
} from '@components/ui/select';
1111
import { Check } from 'lucide-react';
12+
import { clsxm } from '@/app/utils';
1213

1314
type Option = {
1415
label: string;
@@ -92,11 +93,12 @@ type CustomSelectProps = {
9293
className?: string,
9394
ariaLabel?: string
9495
defaultValue?: string
96+
classNameGroup?: string
9597
/**
9698
* Array of string options to be displayed in the dropdown.
9799
* Each string represents a selectable , such as "daily" or "weekly".
98100
*/
99-
options: string[];
101+
options: string[] | any[];
100102

101103
/**
102104
* Optional render function that customizes the display of each option.
@@ -135,7 +137,8 @@ export function CustomSelect({
135137
className,
136138
value,
137139
onChange,
138-
defaultValue
140+
defaultValue,
141+
classNameGroup
139142
}: CustomSelectProps & {
140143
value?: string,
141144
onChange?: (value: string) => void
@@ -154,7 +157,7 @@ export function CustomSelect({
154157
<SelectValue placeholder="Select an option" />
155158
</SelectTrigger>
156159
<SelectContent className='z-[10000] dark:bg-dark--theme-light w-auto'>
157-
<SelectGroup>
160+
<SelectGroup className={clsxm('overflow-y-auto', classNameGroup)}>
158161
{options.map((value) => (
159162
<SelectItem key={value} value={value}>
160163
{renderOption ? renderOption(value) : value.charAt(0).toUpperCase() + value.slice(1)}

0 commit comments

Comments
 (0)