Skip to content

Commit

Permalink
feat: add timesheet creation functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Innocent-Akim committed Dec 11, 2024
1 parent 31507e2 commit d532588
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 103 deletions.
191 changes: 130 additions & 61 deletions apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const LoadingSpinner = ({ className }: { className?: string }) => (
</svg>
);

const ImageWithLoader = ({ imageUrl, alt, className = "w-6 h-6 rounded-full font-bold" }:
const ImageWithLoader = ({ imageUrl, alt, className = "w-6 h-6 rounded-full" }:
{ imageUrl: string; alt: string; className?: string }) => {
const [isLoading, setIsLoading] = React.useState(true);
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,14 @@ export function EditTaskModal({ isOpen, closeModal, dataTimesheet }: IEditTaskMo
}).catch((error) => {
toast({
title: 'Error during modification',
description: `An error occurred: ${error}. The timesheet could not be modified.`,
description: "Failed to modify timesheet. Please try again.",
variant: 'destructive',
className: 'bg-red-50 text-red-600 border-red-500 z-[10000px]'
});
closeModal()
if (error) {
return;
}
closeModal();
});
}, [dateRange, timeRange, timesheetData, dataTimesheet, updateTimesheet]);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React from 'react';
import { useOrganizationProjects, useOrganizationTeams, useTeamTasks } from '@app/hooks';
import { Button } from '@components/ui/button';
import { statusOptions } from '@app/constants';
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 { useTimelogFilterOptions } from '@/app/hooks';
import { useTimesheet } from '@/app/hooks/features/useTimesheet';
import { cn } from '@/lib/utils';
import { statusTable } from './TimesheetAction';

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

React.useEffect(() => {
if (shouldRemoveItems) {
Expand Down Expand Up @@ -59,29 +59,31 @@ export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover
<span className="text-gray-700 dark:text-white">{t('common.FILTER')}</span>
</div>
<div className="grid gap-5">
<div className="">
<label className="flex justify-between mb-1 text-sm text-gray-600">
<span className="text-[12px]">{t('manualTime.EMPLOYEE')}</span>
<span
className={cn(
'text-primary/10',
employee?.length > 0 && 'text-primary dark:text-primary-light'
)}
>
{t('common.CLEAR')}
</span>
</label>
<MultiSelect
localStorageKey="timesheet-select-filter-employee"
removeItems={shouldRemoveItems}
items={activeTeam?.members ?? []}
itemToString={(members) => (members ? members.employee.fullName : '')}
itemId={(item) => item.id}
onValueChange={(selectedItems) => setEmployeeState(selectedItems as any)}
multiSelect={true}
triggerClassName="dark:border-gray-700"
/>
</div>
{isManage && (
<div className="">
<label className="flex justify-between mb-1 text-sm text-gray-600">
<span className="text-[12px]">{t('manualTime.EMPLOYEE')}</span>
<span
className={cn(
'text-primary/10',
employee?.length > 0 && 'text-primary dark:text-primary-light'
)}
>
{t('common.CLEAR')}
</span>
</label>
<MultiSelect
localStorageKey="timesheet-select-filter-employee"
removeItems={shouldRemoveItems}
items={activeTeam?.members ?? []}
itemToString={(members) => (members ? members.employee.fullName : '')}
itemId={(item) => item.id}
onValueChange={(selectedItems) => setEmployeeState(selectedItems as any)}
multiSelect={true}
triggerClassName="dark:border-gray-700"
/>
</div>
)}
<div className="">
<label className="flex justify-between mb-1 text-sm text-gray-600">
<span className="text-[12px]">{t('sidebar.PROJECTS')}</span>
Expand Down Expand Up @@ -145,9 +147,9 @@ export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover
<MultiSelect
localStorageKey="timesheet-select-filter-status"
removeItems={shouldRemoveItems}
items={statusOptions}
itemToString={(status) => (status ? status.value : '')}
itemId={(item) => item.value}
items={statusTable?.flatMap((status) => status)}
itemToString={(status) => (status ? status.label : '')}
itemId={(item) => item.label}
onValueChange={(selectedItems) => setStatusState(selectedItems as any)}
multiSelect={true}
triggerClassName="dark:border-gray-700"
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/hooks/features/useTimesheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export function useTimesheet({
const response = await queryUpdateTimesheet(timesheet);
setTimesheet(prevTimesheet =>
prevTimesheet.map(item =>
item.id === response.data.id
item.timesheet.id === response.data.id
? response.data
: item
)
Expand Down
12 changes: 6 additions & 6 deletions apps/web/app/interfaces/timer/ITimerLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,23 +137,23 @@ export interface UpdateTimesheetStatus extends BaseEntity {
}
export interface UpdateTimesheet extends Pick<
Partial<TimesheetLog>,
| 'id'
| 'reason'
| 'organizationContactId'
| 'description'
| 'organizationTeamId'
| 'projectId'
| 'taskId'
| 'employeeId'
| 'organizationId'
| 'tenantId'
| 'logType'
| 'source'
>,
Pick<
TimesheetLog,
| 'id'
| 'startedAt'
| 'stoppedAt'
| 'tenantId'
| 'logType'
| 'source'
| 'employeeId'
| 'organizationId'
> {
isBillable: boolean;
}
2 changes: 1 addition & 1 deletion apps/web/components/ui/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ const SidebarMenuAction = React.forwardRef<
'peer-data-[size=lg]/menu-button:top-2.5',
'group-data-[collapsible=icon]:hidden',
showOnHover &&
'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',
'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',
className
)}
{...props}
Expand Down
9 changes: 6 additions & 3 deletions apps/web/lib/features/multiple-select/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
SelectLabel,
} from '@components/ui/select';
import { Check } from 'lucide-react';
import { clsxm } from '@/app/utils';

type Option = {
label: string;
Expand Down Expand Up @@ -92,11 +93,12 @@ type CustomSelectProps = {
className?: string,
ariaLabel?: string
defaultValue?: string
classNameGroup?: string
/**
* Array of string options to be displayed in the dropdown.
* Each string represents a selectable , such as "daily" or "weekly".
*/
options: string[];
options: string[] | any[];

/**
* Optional render function that customizes the display of each option.
Expand Down Expand Up @@ -135,7 +137,8 @@ export function CustomSelect({
className,
value,
onChange,
defaultValue
defaultValue,
classNameGroup
}: CustomSelectProps & {
value?: string,
onChange?: (value: string) => void
Expand All @@ -154,7 +157,7 @@ export function CustomSelect({
<SelectValue placeholder="Select an option" />
</SelectTrigger>
<SelectContent className='z-[10000] dark:bg-dark--theme-light w-auto'>
<SelectGroup>
<SelectGroup className={clsxm('overflow-y-auto', classNameGroup)}>
{options.map((value) => (
<SelectItem key={value} value={value}>
{renderOption ? renderOption(value) : value.charAt(0).toUpperCase() + value.slice(1)}
Expand Down

0 comments on commit d532588

Please sign in to comment.