Skip to content

Conversation

Innocent-Akim
Copy link
Contributor

@Innocent-Akim Innocent-Akim commented Nov 11, 2024

#3044
#3040
#3046

Description

Please include a summary of the changes and the related issue.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings

Previous screenshots

Please add here videos or images of previous status

Current screenshots

Please add here videos or images of previous status

Summary by CodeRabbit

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced filtering capabilities in the Timesheet section, allowing users to clear selected items in MultiSelect components.
    • Introduced search functionality for filtering timesheet data based on task titles, employee names, and project names.
    • Added a new FilterCalendar component for improved date selection and planning.
    • New TaskIssueStatusTimesheet function for rendering task statuses related to timesheets.
  • Improvements

    • Updated visual feedback for buttons in dark mode.
    • Optimized date filtering logic and performance in the TimesheetFilterDate component.
    • Improved handling of timesheet data with grouping and sorting features.
    • Enhanced the TimesheetFilter component to manage filter status changes more effectively.
  • Bug Fixes

    • Resolved issues with date range updates in the TimesheetFilterDate component.

Copy link
Contributor

coderabbitai bot commented Nov 11, 2024

Walkthrough

The pull request introduces several enhancements across multiple components in the timesheet application. Key modifications include the addition of new state variables and props in the TimeSheetFilterPopover, TimesheetFilter, and TimesheetFilterDate components to improve filter functionality and interactivity. The data structure for the TimesheetView and DataTableTimeSheet components has been updated to utilize GroupedTimesheet[], reflecting a new grouping logic. Additional changes include the introduction of new functions and interfaces, as well as updates to existing ones to accommodate these enhancements.

Changes

File Change Summary
apps/web/app/[locale]/timesheet/[memberId]/components/TimeSheetFilterPopover.tsx Added state variable shouldRemoveItems, updated MultiSelect props to include removeItems, and modified filtering logic.
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetCard.tsx Updated Button component's className for dark mode hover state.
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx Expanded ITimesheetFilter interface to include onChangeStatus and filterStatus, modified function signature and props for FilterWithStatus.
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx Added state management with useCallback and useMemo, introduced FilterCalendar component, and updated date handling logic.
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetView.tsx Changed data prop type from IDailyPlan[] to GroupedTimesheet[].
apps/web/app/[locale]/timesheet/[memberId]/page.tsx Introduced search and filterStatus state variables, enhanced filtering logic, and updated date formatting using moment.
apps/web/app/hooks/features/useTimesheet.ts Added GroupedTimesheet interface and groupByDate function for improved data grouping.
apps/web/app/interfaces/IProject.ts Added optional name property to IProject interface.
apps/web/app/interfaces/ITask.ts Minor formatting change in ICreateTask interface regarding members property.
apps/web/app/interfaces/timer/ITimerLog.ts Added optional issueType property to ITimeSheet interface.
apps/web/app/services/client/api/timer/timer-log.ts Updated getTaskTimesheetLogsApi function to include a new relation for task status.
apps/web/lib/components/custom-select/multi-select.tsx Added removeItems prop to MultiSelect component, enhancing its functionality.
apps/web/lib/features/integrations/calendar/table-time-sheet.tsx Changed data prop type from IDailyPlan[] to GroupedTimesheet[] and adjusted rendering logic accordingly.
apps/web/lib/features/task/task-issue.tsx Introduced TaskIssueStatusTimesheet function for rendering task statuses specific to ITimeSheet.

Possibly related PRs

Suggested reviewers

  • evereq
  • Cedric921

🐇 In the land of time and tasks,
A filter popover now unmasked,
With states that clear and items gone,
The timesheet's dance goes on and on.
So hop along, let's celebrate,
For every change, we elevate! 🎉

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 eslint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

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

Oops! Something went wrong! :(

ESLint: 8.46.0

ESLint couldn't find the config "next/core-web-vitals" to extend from. Please check that the name of the config is correct.

The config "next/core-web-vitals" was referenced from the config file in "/apps/web/.eslintrc.json".

If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Innocent-Akim Innocent-Akim self-assigned this Nov 11, 2024
@Innocent-Akim Innocent-Akim added the Don't Merge Do not merge this PR until label not removed label Nov 11, 2024
@Innocent-Akim Innocent-Akim marked this pull request as draft November 11, 2024 14:29
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Outside diff range and nitpick comments (18)
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetView.tsx (1)

Line range hint 6-24: Consider enhancing accessibility for status messages

The implementation looks good, but we could improve accessibility for screen readers.

Consider applying these changes:

-                    <div className="flex items-center justify-center h-full">
+                    <div className="flex items-center justify-center h-full" role="status" aria-live="polite">
                         <p>{t('pages.timesheet.NO_ENTRIES_FOUND')}</p>
                     </div>

-                <div className="flex items-center justify-center h-full">
+                <div className="flex items-center justify-center h-full" role="status" aria-live="polite">
                     <p>{t('pages.timesheet.LOADING')}</p>
                 </div>
apps/web/app/services/client/api/timer/timer-log.ts (1)

Line range hint 13-13: Remove unnecessary comment.

The comment // todayStart, todayEnd; appears to be unused and doesn't provide any meaningful context.

-// todayStart, todayEnd;
apps/web/app/hooks/features/useTimesheet.ts (1)

20-33: Consider adding error state handling.

While the hook handles loading state, it could benefit from proper error state management to improve user experience when API calls fail.

Consider extending the hook's return value:

return {
    loadingTimesheet,
    timesheet: groupByDate(timesheet),
    getTaskTimesheet,
+   error: null, // Add error state
+   clearError: () => {}, // Add error clearing function
};
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetCard.tsx (2)

42-42: LGTM! Consider extracting hover styles for reusability.

The dark mode hover state enhancement improves visual feedback. However, these styles could be reused across similar buttons.

Consider creating a reusable class in your global styles:

.btn-hover-effect {
  @apply hover:bg-gray-50 hover:dark:bg-primary-light/40 focus:ring-2 focus:ring-offset-2 focus:ring-gray-200;
}

Line range hint 29-29: Consider using design tokens for magic numbers.

The fixed height value h-[175px] should be defined as a design token for better maintainability and consistency.

Consider moving this to your theme configuration:

export const theme = {
  components: {
    cards: {
      timesheet: {
        height: '175px'
      }
    }
  }
}

Then use it as: h-[theme.components.cards.timesheet.height]

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

18-18: Consider simplifying the state reset logic

The current implementation uses a useEffect to reset the state, but this could be simplified by handling it directly in the clear filter click handler.

Consider this alternative approach:

-const [shouldRemoveItems, setShouldRemoveItems] = React.useState(false);
+const clearFilters = () => {
+  setShouldRemoveItems(true);
+  // Reset on next tick to allow MultiSelect components to process the clear
+  setTimeout(() => setShouldRemoveItems(false), 0);
+};

-useEffect(() => {
-    if (shouldRemoveItems) {
-        setShouldRemoveItems(false);
-    }
-}, [shouldRemoveItems]);

Also applies to: 23-27


Line range hint 52-57: Implement proper state management for filter values

The MultiSelect components currently log selections to console without managing the state. This indicates incomplete implementation.

  1. Add state management for selected values:
interface FilterState {
  employees: typeof activeTeam.members[number][];
  projects: typeof activeTeam.projects[number][];
  tasks: typeof tasks[number][];
  statuses: typeof statusOptions[number][];
}

const [filters, setFilters] = useState<FilterState>({
  employees: [],
  projects: [],
  tasks: [],
  statuses: []
});
  1. Update the onValueChange handlers:
-onValueChange={(selectedItems) => console.log(selectedItems)}
+onValueChange={(selectedItems) => setFilters(prev => ({
+  ...prev,
+  employees: selectedItems
+}))}

Also applies to: 67-73, 82-88, 97-103


Line range hint 108-116: Implement filter application logic

The "Apply Filter" button lacks an onClick handler, and the clear filter implementation could be improved.

Add proper handlers:

-onClick={() => setShouldRemoveItems(true)}
+onClick={clearFilters}

 <Button
+  onClick={() => {
+    // Implement filter application logic
+    onApplyFilters(filters);
+  }}
   className='flex items-center text-sm justify-center h-10  rounded-lg bg-primary dark:bg-primary-light dark:text-gray-300' >
   <span className="text-sm">Apply Filter</span>
 </Button>

49-49: Improve the Clear label functionality

The "Clear" text in labels appears to be styled with low opacity (primary/10) but lacks click handlers.

Consider making these individual clear buttons functional:

-<span className={clsxm("text-primary/10")}>Clear</span>
+<button
+  onClick={() => {
+    setFilters(prev => ({ ...prev, employees: [] }));
+    setShouldRemoveItems(true);
+    setTimeout(() => setShouldRemoveItems(false), 0);
+  }}
+  className={clsxm(
+    "text-primary hover:text-primary/80 text-xs cursor-pointer transition-colors"
+  )}
+>
+  Clear
+</button>

Also applies to: 64-64, 79-79, 94-94


Add ARIA attributes and keyboard interactions for better accessibility

The TimeSheetFilterPopover component needs accessibility improvements based on the codebase analysis:

  1. Add ARIA labels to the filter buttons and sections:
<Button 
    variant="outline"
    aria-label={t('common.OPEN_FILTER')}
    aria-expanded={open}
    aria-controls="filter-popover-content"
    className='flex items-center justify-center h-[2.2rem] rounded-lg...'
>
  1. Add keyboard navigation support for the filter sections:
<PopoverContent 
    id="filter-popover-content"
    role="dialog"
    aria-label={t('common.FILTER_OPTIONS')}
    className="w-96"
>
  1. The MultiSelect component already has accessibility features in the codebase (apps/web/lib/components/custom-select/multi-select.tsx), but needs proper ARIA labels for each filter section:
<MultiSelect
    aria-label={t('manualTime.EMPLOYEE_FILTER')}
    removeItems={shouldRemoveItems}
    items={activeTeam?.members ?? []}
    ...
/>
🔗 Analysis chain

Line range hint 15-122: Verify accessibility and add component documentation

The component could benefit from improved accessibility and documentation.

  1. Run accessibility checks:

  2. Add JSDoc documentation:

/**
 * TimeSheetFilterPopover Component
 * 
 * A popover component that provides filtering capabilities for timesheet data.
 * Allows filtering by employees, projects, tasks, and status.
 * 
 * @component
 * @example
 * ```tsx
 * <TimeSheetFilterPopover />
 * ```
 */
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for aria-label attributes and role definitions
rg -l 'aria-|role=' --type tsx

# Check for keyboard navigation handlers
rg -l 'onKeyDown|onKeyPress|onKeyUp' --type tsx

Length of output: 138


Script:

#!/bin/bash
# Search for aria-label attributes and role definitions in TypeScript/React files
rg -l 'aria-|role=' -t ts -t tsx -t jsx

# Check for keyboard navigation handlers in TypeScript/React files
rg -l 'onKeyDown|onKeyPress|onKeyUp' -t ts -t tsx -t jsx

# Look for existing documentation in the TimeSheetFilterPopover file
rg -A 5 '\/\*\*' "apps/web/app/[locale]/timesheet/[memberId]/components/TimeSheetFilterPopover.tsx"

# Check if MultiSelect component has accessibility features
rg -A 10 'MultiSelect' -t ts -t tsx -t jsx

Length of output: 402


Script:

#!/bin/bash
# Search for aria-label attributes and role definitions
rg 'aria-|role=' --type-add 'typescript:*.{ts,tsx}' --type typescript

# Check for keyboard navigation handlers
rg 'onKeyDown|onKeyPress|onKeyUp' --type-add 'typescript:*.{ts,tsx}' --type typescript

# Look for existing documentation
rg '/\*\*' "apps/web/app/\[locale\]/timesheet/\[memberId\]/components/TimeSheetFilterPopover.tsx"

# Check MultiSelect component implementation
rg -A 10 'export.*MultiSelect' --type-add 'typescript:*.{ts,tsx}' --type typescript

Length of output: 14753

apps/web/lib/features/task/task-issue.tsx (1)

217-217: Add JSDoc documentation for consistency.

The new TaskIssueStatusTimesheet component is missing JSDoc documentation. Consider adding documentation similar to other components in the file for better maintainability.

Add documentation above the component:

/**
 * Renders a TaskStatus component for a timesheet entry
 * @param props - Object containing the timesheet entry and display options
 * @returns A TaskStatus component displaying the issue type of the timesheet
 */
apps/web/lib/components/custom-select/multi-select.tsx (2)

Line range hint 57-71: Consider adding type guard for onValueChange callback.

The functions are well-implemented with proper handling of both single and multi-select cases. However, consider adding a type guard before calling onValueChange for additional safety.

 const removeAllItems = () => {
     setSelectedItems([]);
-    if (onValueChange) {
+    if (typeof onValueChange === 'function') {
         onValueChange(null);
     }
 };

Line range hint 19-77: Consider debouncing rapid removeItems changes.

The current implementation might cause unnecessary re-renders if the removeItems prop changes rapidly. Consider implementing debouncing for better performance.

Example implementation using useCallback and debounce:

import { debounce } from 'lodash';

const debouncedRemoveAll = useCallback(
  debounce(() => {
    removeAllItems();
  }, 250),
  [removeAllItems]
);

useEffect(() => {
  if (removeItems) {
    debouncedRemoveAll();
  }
  return () => {
    debouncedRemoveAll.cancel();
  };
}, [removeItems, debouncedRemoveAll]);
apps/web/app/[locale]/timesheet/[memberId]/page.tsx (2)

2-2: Consider using date-fns consistently instead of moment

The codebase is already using date-fns (see startOfDay, endOfDay imports). For consistency and bundle size optimization, consider using date-fns for date formatting as well instead of introducing moment.

-import moment from 'moment';
+import { format } from 'date-fns';

// Later in the code:
-date={`${moment(dateRange.from).format('YYYY-MM-DD')} - ${moment(dateRange.to).format('YYYY-MM-DD')}`}
+date={`${format(dateRange.from, 'yyyy-MM-dd')} - ${format(dateRange.to, 'yyyy-MM-dd')}`}

Also applies to: 4-4, 40-41


150-157: Add debounce to search input

For better performance, especially with large datasets, consider debouncing the search input to prevent excessive re-renders.

+import { useMemo, useState, useCallback } from 'react';
+import debounce from 'lodash/debounce';

+const debouncedSetSearch = useCallback(
+  debounce((value: string) => setSearch(value), 300),
+  []
+);

-onChange={(v) => setSearch(v.target.value)}
+onChange={(v) => debouncedSetSearch(v.target.value)}
apps/web/lib/features/integrations/calendar/table-time-sheet.tsx (1)

Line range hint 214-225: Implement missing action handlers

The handleButtonClick function has TODO comments for approval and pending logic. This leaves the timesheet approval workflow incomplete.

Would you like me to help implement these handlers or create GitHub issues to track their implementation? The implementation would need to:

  1. Handle approval logic for timesheet entries
  2. Handle pending status changes
  3. Integrate with any existing approval workflow systems
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx (2)

324-324: Avoid unnecessary use of moment in onSelect

The date parameter is already a Date object. Wrapping it with moment(date).toDate() is redundant. You can directly use date.

Apply this diff to simplify the code:

-onSelect={(date) => date && setSelectedPlan(moment(date).toDate())}
+onSelect={(date) => date && setSelectedPlan(date)}

17-17: Consider replacing moment with date-fns for consistency and performance

Since date-fns is already imported and used in the project, you might consider replacing moment with date-fns to reduce bundle size and improve performance.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f8320f1 and 234f329.

📒 Files selected for processing (14)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimeSheetFilterPopover.tsx (6 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetCard.tsx (1 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx (3 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx (7 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetView.tsx (1 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/page.tsx (8 hunks)
  • apps/web/app/hooks/features/useTimesheet.ts (1 hunks)
  • apps/web/app/interfaces/IProject.ts (1 hunks)
  • apps/web/app/interfaces/ITask.ts (1 hunks)
  • apps/web/app/interfaces/timer/ITimerLog.ts (2 hunks)
  • apps/web/app/services/client/api/timer/timer-log.ts (1 hunks)
  • apps/web/lib/components/custom-select/multi-select.tsx (5 hunks)
  • apps/web/lib/features/integrations/calendar/table-time-sheet.tsx (3 hunks)
  • apps/web/lib/features/task/task-issue.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/app/interfaces/ITask.ts
🧰 Additional context used
🪛 GitHub Check: Cspell
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx

[warning] 286-286:
Unknown word (Caledar)

🔇 Additional comments (16)
apps/web/app/interfaces/IProject.ts (1)

27-27: Consider clarifying the relationship between IProject.name and IProject.repository.name

The addition of an optional name property to IProject creates potential ambiguity since:

  1. IProjectRepository already has a required name field
  2. IProject has an optional reference to IProjectRepository via the repository property
  3. IProjectCreate requires name but IProject makes it optional

This could lead to confusion about which name should be used in different contexts.

Let's verify how these interfaces are being used:

Consider one of these alternatives:

  1. If name should always come from the repository:
interface IProject {
  // ... other properties ...
  repository: IProjectRepository; // Make required
  // Remove duplicate name property
}
  1. If name is separate from repository name:
interface IProject {
  // ... other properties ...
  name: string; // Make required and document the difference
  repository?: IProjectRepository;
}
✅ Verification successful

The optional name property in IProject is justified and correctly implemented

Based on the codebase analysis:

  1. The name property is used in UI components for project selection, specifically in TimeSheetFilterPopover.tsx where it's accessed directly via project.name
  2. The interface hierarchy is correct:
    • IProjectCreate requires name for creation
    • IProject makes it optional since it can be derived from repository.name when available
    • This dual-source approach supports both repository-linked and standalone projects

The current implementation provides flexibility while maintaining type safety, allowing projects to have names either directly or through their repository association.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage patterns of IProject interface and its name properties
echo "Searching for IProject usage patterns..."
rg -t typescript "IProject" -A 5

echo "Searching specifically for project name access patterns..."
rg -t typescript "project\.name|project\.repository\.name" -A 2

Length of output: 386


Script:

#!/bin/bash
# Search for IProject interface usage patterns
echo "Searching for IProject usage..."
rg "IProject" -A 5

echo -e "\nSearching for project name access patterns..."
rg "project\.name|project\.repository\.name" -A 2

echo -e "\nSearching for files containing IProject..."
fd -e ts -e tsx . | xargs rg "IProject"

Length of output: 11363

apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetView.tsx (2)

1-3: LGTM! Imports are well-organized and necessary.

The imports correctly include the new GroupedTimesheet type and all required dependencies.


5-5: Verify consistent usage of GroupedTimesheet type

The type change from IDailyPlan[] to GroupedTimesheet[] looks good, but let's verify its consistent usage across the codebase.

✅ Verification successful

The verification results show that the type change from IDailyPlan[] to GroupedTimesheet[] in TimesheetView component is consistent with its usage across the codebase. The GroupedTimesheet type is properly defined in useTimesheet.ts and is used consistently in both TimesheetView and DataTableTimeSheet components. The IDailyPlan[] type is still used extensively but in different, unrelated contexts (daily planning features) and doesn't conflict with the timesheet functionality.

Type change is correctly implemented and consistent

The GroupedTimesheet interface is well-defined and its usage is consistent across timesheet-related components. The change from IDailyPlan[] to GroupedTimesheet[] is correct as these are separate concerns in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining IDailyPlan[] type usage that might need updating
# and verify GroupedTimesheet type implementation

# Check for any remaining IDailyPlan[] usage that might need updating
rg "IDailyPlan\[\]" -A 3

# Check GroupedTimesheet type implementation and usage
ast-grep --pattern 'interface GroupedTimesheet {
  $$$
}'

# Check for other components using GroupedTimesheet
rg "GroupedTimesheet(\[\])?" -A 3

Length of output: 12735

apps/web/app/interfaces/timer/ITimerLog.ts (2)

1-1: LGTM! Import statement is correctly placed.

The import for ITaskIssue is appropriately added at the top of the file.


74-74: Verify the optional property usage across the codebase.

The addition of the optional issueType property maintains backward compatibility while extending the interface for issue tracking functionality. This change appears to be well-structured.

Let's verify the usage of this new property:

✅ Verification successful

Based on the search results, I can now provide a conclusive response about the optional issueType property usage:

The optional issueType property is well-integrated across the codebase

The property is consistently used throughout the application with proper type checking and handling:

  • Used in task creation/updates with proper optionality in useTeamTasks.ts
  • Properly typed in multiple interfaces (ITask.ts, ITimerLog.ts)
  • Handled in UI components with proper null checks (e.g., task?.issueType)
  • Used in filtering and display logic with fallbacks to default values
  • Integrated with the task status system and issue type management
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how the new issueType property is being used across the codebase

# Find components that might need updates to handle the new property
rg -l "ITimeSheet" --type ts --type tsx

# Check for any existing issue type handling in timesheet components
rg "issueType" --type ts --type tsx -C 3

# Look for TaskStatus components that might need updates
ast-grep --pattern 'TaskStatus' -l

Length of output: 277


Script:

#!/bin/bash
# Let's try with correct syntax and without tsx extension

# Find components that might need updates to handle the new property
rg -l "ITimeSheet" --type typescript

# Check for any existing issue type handling in timesheet components
rg "issueType" --type typescript -C 3

# Look for TaskStatus components that might need updates
ast-grep -l -p 'TaskStatus' --lang typescript

Length of output: 296


Script:

#!/bin/bash
# Let's try with simpler commands and without file type specifications

# Find files containing ITimeSheet
rg -l "ITimeSheet"

# Check for any existing issue type handling
rg "issueType" -C 3

# Look for files containing TaskStatus
rg -l "TaskStatus"

Length of output: 79630

apps/web/app/services/client/api/timer/timer-log.ts (1)

52-53: LGTM! Verify backend API support.

The addition of the 'task.taskStatus' relation follows the existing pattern and aligns with the grouped timesheet feature. However, please ensure that the backend API supports this relation to avoid runtime errors.

apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx (2)

1-1: LGTM: Well-structured interface changes

The interface updates and imports are well-organized, with proper TypeScript typing and backward compatibility through optional props.

Also applies to: 12-14


1-1: Verify FilterWithStatus component interface

Let's ensure the FilterWithStatus component properly handles these prop types.

Also applies to: 30-31

✅ Verification successful

FilterWithStatus component interface is correctly implemented

The component's interface matches the expected usage:

  • activeStatus prop is correctly typed as FilterStatus enum
  • onToggle prop is properly typed as a callback function that takes a FilterStatus parameter
  • Additional className prop is available for styling flexibility
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for FilterWithStatus component definition to verify prop types
ast-grep --pattern 'interface $interface {
  $$$
  activeStatus: $type
  onToggle: $callback
  $$$
}'

# Backup: Search using ripgrep if component is not using TypeScript interface
rg -A 10 'export.*FilterWithStatus.*='

Length of output: 144


Script:

#!/bin/bash
# Let's try a different approach to find the FilterWithStatus component and its props
# First, let's locate the file
fd "FilterWithStatus.tsx"

# Then let's examine its contents
rg -A 20 "export.*FilterWithStatus" $(fd "FilterWithStatus.tsx")

# Also search for any type/interface definitions
rg "type|interface.*FilterWithStatus" $(fd "FilterWithStatus.tsx")

# Let's also check for any Props type definition
rg "Props" $(fd "FilterWithStatus.tsx")

Length of output: 1475

apps/web/app/hooks/features/useTimesheet.ts (1)

15-18: LGTM! Clear and well-structured interface definition.

The GroupedTimesheet interface provides good type safety for the grouped timesheet data structure.

apps/web/lib/features/task/task-issue.tsx (1)

2-2: LGTM!

The import of ITimeSheet is appropriate for the new functionality being added.

apps/web/lib/components/custom-select/multi-select.tsx (2)

19-19: LGTM! Interface extension is well-defined.

The new optional removeItems prop follows TypeScript best practices and boolean naming conventions.


32-32: LGTM! Props destructuring is correct.

The removeItems prop is correctly destructured from the component props.

apps/web/app/[locale]/timesheet/[memberId]/page.tsx (1)

163-164: LGTM! Filter status implementation looks good

The filter status implementation with proper prop passing and state management is well done.

apps/web/lib/features/integrations/calendar/table-time-sheet.tsx (2)

61-61: LGTM: Import of new data type

The import of GroupedTimesheet type aligns with the data structure changes mentioned in the summary.


297-298: 🛠️ Refactor suggestion

Verify task property access safety

The code accesses new properties isActive and employee.fullName directly. These could cause runtime errors if the properties are undefined.

Consider adding null checks:

- <span className="flex-1">{task.isActive}</span>
- <span className="flex-1">{task.employee.fullName}</span>
+ <span className="flex-1">{task.isActive?.toString() ?? 'N/A'}</span>
+ <span className="flex-1">{task.employee?.fullName ?? 'Unknown'}</span>
✅ Verification successful

Property access is safe, but type coercion needed

The properties are guaranteed to be present based on the ITimeSheet interface definition found in apps/web/app/interfaces/timer/ITimerLog.ts:

  • isActive is defined as a boolean
  • employee is defined with type Employee

However, the display of boolean isActive should be coerced to string:

- <span className="flex-1">{task.isActive}</span>
+ <span className="flex-1">{task.isActive.toString()}</span>
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the GroupedTimesheet type definition and verify required properties
ast-grep --pattern 'type GroupedTimesheet = {
  $$$
  tasks: {
    $$$
    isActive: $_
    employee: {
      fullName: $_
      $$$
    }
    $$$
  }[]
  $$$
}'

Length of output: 163


Script:

#!/bin/bash
# Let's try a broader search for type definitions related to tasks
ast-grep --pattern 'type $_ = {
  $$$
  tasks: $_
  $$$
}'

# Also search for interface definitions
ast-grep --pattern 'interface $_ {
  $$$
  tasks: $_
  $$$
}'

# Search for task type definitions
ast-grep --pattern 'type Task = {
  $$$
}'

# Let's also check the actual usage context
rg -B5 -A5 "task\.(isActive|employee\.fullName)" apps/web/lib/features/integrations/calendar/table-time-sheet.tsx

Length of output: 2556


Script:

#!/bin/bash
# Let's check the ITimeSheet interface definition since we found it's used in GroupedTimesheet
ast-grep --pattern 'interface ITimeSheet {
  $$$
}'

# Also search for any imports of ITimeSheet to find its definition
rg "ITimeSheet" -A 2 apps/web/lib/features/integrations/calendar/table-time-sheet.tsx

# Let's also check the actual data being passed to this component
rg -B10 "TableTimeSheet" apps/web/lib/features/integrations/calendar/table-time-sheet.tsx

Length of output: 2840

apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx (1)

326-326: ⚠️ Potential issue

Fix the logical operator in the disabled prop

The disabled function currently uses &&, which only disables dates that are both in the past and available for planning. To disable dates that are either in the past or unavailable, use || and adjust the condition accordingly.

Apply this diff to correct the logic:

-disabled={(date) => checkPastDate(date) && isDateAvailableForPlanning(date)}
+disabled={(date) => checkPastDate(date) || !isDateAvailableForPlanning(date)}

Likely invalid or redundant comment.

@Innocent-Akim Innocent-Akim marked this pull request as ready for review November 11, 2024 18:08
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (8)
apps/web/app/hooks/features/useTimesheet.ts (1)

Line range hint 42-85: Enhance error handling and input validation.

Consider the following improvements:

  1. Add error state handling instead of just console.log
  2. Validate startDate and endDate parameters
  3. Reset loading state on error

Here's a suggested implementation:

 export function useTimesheet({
     startDate,
     endDate,
 }: TimesheetParams) {
     const { user } = useAuthenticateUser();
     const [timesheet, setTimesheet] = useAtom(timesheetRapportState);
+    const [error, setError] = useState<Error | null>(null);
 
     const { loading: loadingTimesheet, queryCall: queryTimesheet } = useQuery(getTaskTimesheetLogsApi);
 
     const getTaskTimesheet = useCallback(
         ({ startDate, endDate }: TimesheetParams) => {
             if (!user) return;
+            if (!startDate || !endDate) {
+                setError(new Error('Start date and end date are required'));
+                return;
+            }
             const from = moment(startDate).format('YYYY-MM-DD');
             const to = moment(endDate).format('YYYY-MM-DD')
+            setError(null);
             queryTimesheet({
                 startDate: from,
                 endDate: to,
                 organizationId: user.employee?.organizationId,
                 tenantId: user.tenantId ?? '',
                 timeZone: user.timeZone?.split('(')[0].trim(),
             }).then((response) => {
                 setTimesheet(response.data);
             }).catch((error) => {
-                console.error('Error fetching timesheet:', error);
+                setError(error);
+                setTimesheet([]);
             });
         },
         [user, queryTimesheet, setTimesheet]
     );

     return {
         loadingTimesheet,
         timesheet: groupByDate(timesheet),
         getTaskTimesheet,
+        error,
     };
 }
apps/web/lib/components/custom-select/multi-select.tsx (1)

66-71: Consider memoizing removeAllItems with useCallback.

The function is used as a dependency in useEffect. Memoizing it would prevent unnecessary effect runs.

-    const removeAllItems = () => {
+    const removeAllItems = useCallback(() => {
         setSelectedItems([]);
         if (onValueChange) {
             onValueChange(null);
         }
-    };
+    }, [onValueChange]);
apps/web/app/[locale]/timesheet/[memberId]/page.tsx (2)

120-120: Enhance date formatting with localization support

Replace moment.js with date-fns and add localization support for date formatting:

-date={`${moment(dateRange.from).format('YYYY-MM-DD')} - ${moment(dateRange.to).format('YYYY-MM-DD')}`}
+date={`${format(dateRange.from, 'P', { locale: userLocale })} - ${format(dateRange.to, 'P', { locale: userLocale })}`}

153-160: Enhance search input with debounce and clear functionality

Consider adding:

  1. Debounce to prevent excessive filtering on each keystroke
  2. A clear button to reset the search
+const debouncedSetSearch = useMemo(
+  () => debounce((value: string) => setSearch(value), 300),
+  []
+);

 <input
-  onChange={(v) => setSearch(v.target.value)}
+  onChange={(v) => debouncedSetSearch(v.target.value)}
+  value={search}
   role="searchbox"
   aria-label="Search timesheet"
   type="search"
   name="timesheet-search"
   id="timesheet-search"
   className="!h-[2.2rem] w-full bg-transparent focus:border-transparent focus:ring-2 focus:ring-transparent placeholder-gray-500 placeholder:font-medium shadow-sm outline-none"
   placeholder={t('common.SEARCH')}
 />
+{search && (
+  <button
+    onClick={() => setSearch('')}
+    className="p-1"
+    aria-label={t('common.CLEAR_SEARCH')}
+  >
+    <XIcon className="h-4 w-4 text-gray-500" />
+  </button>
+)}
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx (4)

Line range hint 46-51: Potential stale state in onChange callback within handleFromChange.

In the handleFromChange function, the onChange callback uses dateRange, which may not reflect the latest state due to the asynchronous nature of setState. This can lead to inconsistencies in the date range updates.

To ensure onChange receives the updated date range, compute the new date range before updating the state:

const handleFromChange = (fromDate: Date | null) => {
  if (maxDate && fromDate && fromDate > maxDate) {
    return;
  }
- setDateRange((prev) => ({ ...prev, from: fromDate }));
- onChange?.({ ...dateRange, from: fromDate });
+ const newDateRange = { ...dateRange, from: fromDate };
+ setDateRange(newDateRange);
+ onChange?.(newDateRange);
};

Line range hint 53-58: Missing onChange callback in handleToChange.

The handleToChange function updates dateRange but does not invoke onChange, which may prevent parent components from receiving updates.

Consider updating handleToChange to call onChange with the new date range:

const handleToChange = (toDate: Date | null) => {
  if (dateRange.from && toDate && toDate < dateRange.from) {
    return;
  }
- setDateRange((prev) => ({ ...prev, to: toDate }));
+ const newDateRange = { ...dateRange, to: toDate };
+ setDateRange(newDateRange);
+ onChange?.(newDateRange);
};

305-312: Simplify date comparisons in isDateAvailableForPlanning.

The current date comparison logic involves multiple conversions and string manipulations, which can be simplified for better readability and performance.

Refactor the function using moment's isSame method:

const isDateAvailableForPlanning = useCallback(
  (dateToCheck: Date) => {
    return !plans.some(plan => {
-     const formattedPlanDate = moment(plan.date.toString().split('T')[0]).toISOString().split('T')[0];
-     const formattedDateToCheck = moment(dateToCheck).toISOString().split('T')[0];
-     return formattedPlanDate === formattedDateToCheck;
+     return moment(plan.date).isSame(dateToCheck, 'day');
    });
  },
  [plans]
);

233-236: Refactor repeated class names to enhance maintainability.

Multiple class names are repeated across the component, which can be extracted into constants or utility functions to improve code readability and ease future modifications.

Extract repeated class names into constants:

+ const datePickerClassName = "dark:bg-dark--theme-light rounded-lg bg-white dark:text-gray-200";
+ const datePickerButtonClassName = "decoration-transparent flex items-center w-full h-[2.2em] bg-white dark:text-gray-200 dark:bg-dark--theme-light border-gray-300 justify-start text-left font-normal text-black h-[2.2rem] border dark:border-slate-600 rounded-md hover:border-primary";

<DatePicker
-   className="dark:bg-dark--theme-light rounded-lg bg-white dark:text-gray-200 "
+   className={datePickerClassName}
-   buttonClassName={'decoration-transparent flex items-center w-full h-[2.2em] bg-white dark:text-gray-200 dark:bg-dark--theme-light border-gray-300 justify-start text-left font-normal text-black  h-[2.2rem] border dark:border-slate-600 rounded-md hover:border-primary'}
+   buttonClassName={datePickerButtonClassName}
    ...
/>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 234f329 and b95a006.

📒 Files selected for processing (5)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx (3 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx (7 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/page.tsx (8 hunks)
  • apps/web/app/hooks/features/useTimesheet.ts (1 hunks)
  • apps/web/lib/components/custom-select/multi-select.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx
🔇 Additional comments (7)
apps/web/app/hooks/features/useTimesheet.ts (1)

15-18: LGTM! Clear and well-defined interface.

The GroupedTimesheet interface is concise and properly typed for its purpose.

apps/web/lib/components/custom-select/multi-select.tsx (2)

19-19: LGTM: Interface extension is clean and backward compatible.

The new optional removeItems prop is well-named and follows boolean prop naming conventions.


32-32: LGTM: Props destructuring matches interface changes.

apps/web/app/[locale]/timesheet/[memberId]/page.tsx (3)

40-41: Well-implemented state management and filter logic

Good job on:

  • Using localStorage for filter status persistence
  • Implementing memoized filter logic with proper null checks
  • Efficient search implementation with toLowerCase memoization

Also applies to: 53-61


166-167: LGTM! Well-structured filter state management

Good implementation of filter status props with proper typing and state management.


181-181: LGTM! Proper implementation of filtered data rendering

Good use of memoized filtered data with appropriate conditional rendering.

apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx (1)

210-222: Addition of maxDate prop improves component flexibility.

The inclusion of the maxDate prop in DatePickerFilter allows for better control over the range of selectable dates.

@Innocent-Akim Innocent-Akim added feature WEB Web app Ever Teams and removed Don't Merge Do not merge this PR until label not removed labels Nov 11, 2024
@Innocent-Akim Innocent-Akim requested a review from evereq November 11, 2024 18:19
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (3)
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx (3)

Line range hint 47-58: Enhance date handling logic for better readability and maintainability

The date validation logic could be more concise and readable.

Consider this refactoring:

 const handleFromChange = (fromDate: Date | null) => {
-    if (maxDate && fromDate && fromDate > maxDate) {
+    if (fromDate && maxDate && fromDate > maxDate) {
         return;
     }
     setDateRange((prev) => ({ ...prev, from: fromDate }));
     onChange?.({ ...dateRange, from: fromDate });
 };

 const handleToChange = (toDate: Date | null) => {
-    if (dateRange.from && toDate && toDate < dateRange.from) {
+    if (toDate && dateRange.from && toDate < dateRange.from) {
         return;
     }
     setDateRange((prev) => ({ ...prev, to: toDate }));
+    onChange?.({ ...dateRange, to: toDate });
 };

Also added the missing onChange call in handleToChange for consistency with handleFromChange.


249-256: Extract modifier class definitions for better maintainability

The modifier classes could be extracted into constants to improve maintainability and reusability.

Consider this approach:

+const MODIFIER_CLASSES = {
+  booked: clsxm(
+    'relative after:absolute after:bottom-0 after:left-1/2 after:-translate-x-1/2 after:w-1.5 after:h-1.5 after:bg-primary after:rounded-full'
+  ),
+  selected: clsxm('bg-primary after:hidden text-white !rounded-full'),
+  pastDay: clsxm(
+    'relative after:absolute after:bottom-0 after:left-1/2 after:-translate-x-1/2 after:w-1.5 after:h-1.5 after:bg-yellow-600 after:rounded-full'
+  ),
+  today: clsxm('border-2 !border-yellow-700 rounded')
+};

 modifiersClassNames={{
-  booked: clsxm(
-    'relative after:absolute after:bottom-0 after:left-1/2 after:-translate-x-1/2 after:w-1.5 after:h-1.5 after:bg-primary after:rounded-full'
-  ),
-  selected: clsxm('bg-primary after:hidden text-white !rounded-full'),
-  pastDay: clsxm(
-    'relative after:absolute after:bottom-0 after:left-1/2 after:-translate-x-1/2 after:w-1.5 after:h-1.5 after:bg-yellow-600 after:rounded-full'
-  ),
-  today: clsxm('border-2 !border-yellow-700 rounded')
+  ...MODIFIER_CLASSES
}}

270-340: Consider replacing moment.js with modern alternatives

moment.js is considered legacy and is now in maintenance mode. Consider migrating to more modern alternatives like date-fns (already used in the codebase) or Temporal API.

Benefits of migration:

  • Better tree-shaking support
  • Smaller bundle size
  • Better performance
  • Immutable API design

The migration would involve replacing moment.js operations with date-fns equivalents throughout the component.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between b95a006 and 0467309.

📒 Files selected for processing (1)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx (7 hunks)
🔇 Additional comments (1)
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx (1)

1-2: LGTM: Import additions are appropriate

The new imports and hooks are well-chosen for the component's needs, particularly for performance optimization with memo, useCallback, and useMemo.

Also applies to: 16-17

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (2)
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx (2)

Line range hint 52-59: Inconsistent onChange callback usage between handlers

The handleToChange function doesn't trigger the onChange callback immediately like handleFromChange does. This inconsistency could lead to unexpected behavior when consuming components rely on immediate updates.

Apply this diff to maintain consistency:

 const handleToChange = (toDate: Date | null) => {
     if (dateRange.from && toDate && toDate < dateRange.from) {
         return;
     }
     setDateRange((prev) => ({ ...prev, to: toDate }));
+    onChange?.({ ...dateRange, to: toDate });
 };

Line range hint 224-228: Optimize isDateDisabled function with useMemo

Since isDateDisabled returns a function that depends on minDate and maxDate, using useMemo would be more appropriate than useCallback.

Apply this diff to optimize the implementation:

-    const isDateDisabled = React.useCallback((date: Date) => {
+    const isDateDisabled = React.useMemo(() => (date: Date) => {
         if (minDate && date < minDate) return true;
         if (maxDate && date > maxDate) return true;
         return false;
     }, [minDate, maxDate]);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0467309 and fc3284b.

📒 Files selected for processing (1)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx (7 hunks)

@Innocent-Akim Innocent-Akim requested review from evereq and removed request for evereq November 11, 2024 21:30
@evereq evereq merged commit 1a1d0c6 into develop Nov 11, 2024
13 checks passed
@evereq evereq deleted the feat/timesheet-filter-calendar branch November 11, 2024 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants