Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat]: Add Component for Timesheet Creation and Optimize Task Button #3359

Merged
merged 5 commits into from
Nov 22, 2024

Conversation

Innocent-Akim
Copy link
Contributor

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

Description

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

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

Release Notes

  • New Features

    • Introduced a modal for adding tasks with fields for task name, issue type, and billable status.
    • Enhanced filtering capabilities with new statuses: 'In review' and 'Draft'.
    • Added grouping options for timesheet entries by day, week, or month.
  • Improvements

    • Improved localization for status labels in the timesheet management component.
    • Streamlined frequency selection with updated state management for better user experience.
    • Enhanced the rendering logic for displaying timesheet dates, providing clearer visual cues.
  • Bug Fixes

    • Adjusted rendering logic for timesheet dates for better clarity in daily and weekly views.

@Innocent-Akim Innocent-Akim self-assigned this Nov 21, 2024
Copy link
Contributor

coderabbitai bot commented Nov 21, 2024

Walkthrough

The changes introduced in this pull request involve the addition and modification of several components related to timesheet management in a web application. A new AddTaskModal component is implemented for creating tasks, while existing components such as FilterWithStatus, TimesheetFilter, and FrequencySelect have been updated to enhance their functionality and support new data structures. Additionally, the useTimesheet hook has been extended to include new grouping features, and various interfaces and types have been introduced or modified to accommodate these changes.

Changes

File Change Summary
apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx Introduced AddTaskModal component for task creation with localization and state management for task details.
apps/web/app/[locale]/timesheet/[memberId]/components/FilterWithStatus.tsx Expanded FilterStatus to include 'In review' and 'Draft'; updated logic for dynamic button data construction.
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx Replaced AddManualTimeModal with AddTaskModal; updated to pass new data prop for filtering tasks.
apps/web/app/[locale]/timesheet/[memberId]/page.tsx Updated TimeSheet component to include statusTimesheet and refactored filtering logic for timesheet data.
apps/web/app/hooks/features/useTimesheet.ts Added grouping functionalities with new groupByWeek and groupByMonth functions; included statusTimesheet.
apps/web/lib/features/integrations/calendar/table-time-sheet.tsx Enhanced localization with useTranslations; updated rendering logic for timesheet dates based on grouping.
apps/web/app/[locale]/timesheet/[memberId]/components/FrequencySelect.tsx Integrated local storage for frequency selection; updated state management for frequency options.
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetAction.tsx Introduced DailyTable constant for grouping options; no changes to button configurations.
apps/web/app/hooks/features/useTimelogFilterOptions.ts Added state management for timesheetGroupByDays to the hook, enhancing its functionality.
apps/web/app/interfaces/ITask.ts Introduced new type TimesheetFilterByDays for frequency options.
apps/web/app/stores/time-logs.ts Added new atom timesheetGroupByDayState for managing grouping state of timesheet data.

Possibly related PRs

Suggested reviewers

  • evereq
  • Cedric921

🐰 In the meadow where tasks are made,
A modal blooms, no need to be afraid.
With filters and statuses, all in a row,
Creating and grouping, watch the workflow grow!
So hop along, let’s add with glee,
For every task, there’s a place to be! 🌼

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 (1.23.1)

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/FilterWithStatus.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 marked this pull request as draft November 21, 2024 18:45
@Innocent-Akim Innocent-Akim added the Don't Merge Do not merge this PR until label not removed label Nov 21, 2024
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: 8

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

15-17: Add JSDoc comments for the new data prop

The new data prop's type signature is well-structured, but adding documentation would help explain its purpose and expected format to other developers.

 interface ITimesheetFilter {
     filterStatus?: FilterStatus,
+    /** Record mapping timesheet status to array of timesheet logs
+     * Used to display count of tasks in each status category
+     */
     data?: Record<TimesheetStatus, TimesheetLog[]>
 }

Line range hint 44-46: Consider updating button text for consistency

Since we've switched from manual time entry to task management, consider updating the button text from 'common.ADD_TIME' to something more task-oriented like 'common.ADD_TASK'.

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

21-28: Consider using distinct icons for different statuses

Currently, some different statuses share the same icon:

  • 'In review' and 'Rejected' both use 'icon-rejected'
  • 'Draft' and 'Approved' both use 'icon-approved'

This might confuse users about the actual status of their timesheets.

Consider using unique icons for each status:

 const statusIcons: Record<FilterStatus, string> = {
   'All Tasks': 'icon-all',
   Pending: 'icon-pending',
   Approved: 'icon-approved',
-  'In review': 'icon-rejected',
+  'In review': 'icon-review',
-  Draft: 'icon-approved',
+  Draft: 'icon-draft',
   Rejected: 'icon-rejected',
 };

Line range hint 8-69: Consider separating data transformation logic

The component currently handles both data transformation and presentation. Consider extracting the data transformation logic into a custom hook for better separation of concerns and reusability.

Example structure:

// useFilterStatus.ts
export function useFilterStatus(data: Record<TimesheetStatus, TimesheetLog[]>) {
  const statusIcons = {...}; // Move statusIcons here
  
  const buttonData = React.useMemo(() => {
    // Move buttonData calculation here
    return [...];
  }, [data]);
  
  return { buttonData, statusIcons };
}

// FilterWithStatus.tsx
export function FilterWithStatus({ data, ...props }) {
  const { buttonData } = useFilterStatus(data);
  return (...); // Focus on presentation only
}
apps/web/app/hooks/features/useTimesheet.ts (4)

171-172: Optimize array flattening operation

The changes look good! However, since we're only flattening the array without transforming its elements, we can optimize by using flat() instead of flatMap().

-        statusTimesheet: getStatusTimesheet(timesheet.flatMap((data) => data))
+        statusTimesheet: getStatusTimesheet(timesheet.flat())
🧰 Tools
🪛 Biome (1.9.4)

[error] 172-172: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)


Line range hint 73-89: Enhance error handling for invalid timesheet statuses

The status grouping logic is well-structured, but we could improve the error handling for invalid statuses to aid in debugging.

 const getStatusTimesheet = (items: TimesheetLog[] = []) => {
     const STATUS_MAP: Record<TimesheetStatus, TimesheetLog[]> = {
         PENDING: [],
         APPROVED: [],
         DENIED: [],
         DRAFT: [],
         'IN REVIEW': []
     };
 
     return items.reduce((acc, item) => {
         const status = item.timesheet.status;
         if (isTimesheetStatus(status)) {
             acc[status].push(item);
         } else {
-            console.warn(`Invalid timesheet status: ${status}`);
+            console.warn(
+                `Invalid timesheet status: ${status}`,
+                `for timesheet ID: ${item.timesheet.id}`,
+                `Expected one of: ${Object.keys(STATUS_MAP).join(', ')}`
+            );
         }
         return acc;
     }, STATUS_MAP);
 }
🧰 Tools
🪛 Biome (1.9.4)

[error] 172-172: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)


Line range hint 91-102: Ensure consistency between status definitions

The type guard implementation could be improved by using the same source of truth for status values.

 function isTimesheetStatus(status: unknown): status is TimesheetStatus {
-    const timesheetStatusValues: TimesheetStatus[] = [
-        "DRAFT",
-        "PENDING",
-        "IN REVIEW",
-        "DENIED",
-        "APPROVED"
-    ];
+    const STATUS_MAP: Record<TimesheetStatus, TimesheetLog[]> = {
+        PENDING: [],
+        APPROVED: [],
+        DENIED: [],
+        DRAFT: [],
+        'IN REVIEW': []
+    };
+    const timesheetStatusValues = Object.keys(STATUS_MAP) as TimesheetStatus[];
     return Object.values(timesheetStatusValues).includes(status as TimesheetStatus);
 }
🧰 Tools
🪛 Biome (1.9.4)

[error] 172-172: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)


Line range hint 1-173: Consider architectural improvements for better maintainability

The hook implementation is solid, but there are a few architectural improvements that could make it more maintainable:

  1. Extract the status map to a constant at the module level to avoid duplication
  2. Consider splitting the hook into smaller, more focused hooks (e.g., useTimesheetStatus, useTimesheetOperations)
  3. Implement consistent error handling across all operations

Here's how you could extract the status map:

const TIMESHEET_STATUS_MAP: Record<TimesheetStatus, TimesheetLog[]> = {
    PENDING: [],
    APPROVED: [],
    DENIED: [],
    DRAFT: [],
    'IN REVIEW': []
} as const;

Would you like me to help create a proposal for splitting this hook into smaller, more focused hooks?

🧰 Tools
🪛 Biome (1.9.4)

[error] 172-172: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)

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

Line range hint 26-156: Add form validation and error handling

The form lacks proper validation and error handling mechanisms:

  1. Required fields are marked with asterisks but can be submitted empty
  2. Time inputs need validation for valid ranges and end time > start time
  3. Empty handlers for handleSelectedValuesChange and handleChange
  4. No loading states or error handling for form submission

Consider implementing form validation using a form management library like react-hook-form or formik. Example implementation:

import { useForm } from 'react-hook-form';

interface TaskFormData {
  startTime: string;
  endTime: string;
  date: Date;
  project: string;
  notes: string;
  isBillable: boolean;
}

const {
  register,
  handleSubmit,
  formState: { errors, isSubmitting }
} = useForm<TaskFormData>();

const onSubmit = async (data: TaskFormData) => {
  try {
    // Handle form submission
  } catch (error) {
    // Handle error
  }
};

Line range hint 213-227: Enhance ToggleButton accessibility and styling

The ToggleButton component needs improvements in several areas:

  1. Missing ARIA attributes for accessibility
  2. Using inline styles instead of Tailwind classes
  3. Lacking hover/focus states
  4. No keyboard navigation support

Consider this improved implementation:

 export const ToggleButton = ({ isActive, onClick, label }: ToggleButtonProps) => (
-	<div className="flex items-center gap-x-2">
+	<div className="flex items-center gap-x-2 group">
 		<div
-			className="w-6 h-6 flex items-center bg-[#6c57f4b7] rounded-full p-1 cursor-pointer"
+			className={clsxm(
+				"w-6 h-6 flex items-center rounded-full p-1 cursor-pointer",
+				"focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary",
+				"transition-colors duration-200 ease-in-out",
+				isActive ? "bg-gradient-to-r from-[#9d91efb7] to-[#8a7bedb7]" : "bg-gray-800",
+				"hover:opacity-90"
+			)}
 			onClick={onClick}
-			style={{
-				background: isActive ? 'linear-gradient(to right, #9d91efb7, #8a7bedb7)' : '#1f2937'
-			}}
+			role="switch"
+			aria-checked={isActive}
+			tabIndex={0}
+			onKeyDown={(e) => {
+				if (e.key === 'Enter' || e.key === ' ') {
+					e.preventDefault();
+					onClick();
+				}
+			}}
 		>
 			<div
 				className={clsxm(
-					'bg-[#3826A6] w-4 h-4 rounded-full shadow-md transform transition-transform translate-x-0',
-					isActive && 'bg-white'
+					'w-4 h-4 rounded-full shadow-md transform transition-transform duration-200',
+					'translate-x-0',
+					isActive ? 'bg-white' : 'bg-[#3826A6]'
 				)}
 			/>
 		</div>
-		<span>{label}</span>
+		<span className="select-none">{label}</span>
 	</div>
 );

Line range hint 39-49: Implement handlers and improve type safety

The component has several unimplemented features and type safety issues:

  1. Empty handler functions
  2. Loose typing on selectedValues
  3. No cleanup for state on unmount

Consider implementing the handlers and improving type safety:

interface SelectedValues {
  Teams: Item | null;
  [key: string]: Item | null;
}

const [selectedValues, setSelectedValues] = useState<SelectedValues>({
  Teams: null
});

const handleSelectedValuesChange = (values: SelectedValues) => {
  setSelectedValues(values);
};

const handleChange = (field: keyof SelectedValues, selectedItem: Item | null) => {
  setSelectedValues(prev => ({
    ...prev,
    [field]: selectedItem
  }));
};

// Cleanup on unmount
useEffect(() => {
  return () => {
    // Cleanup state if needed
  };
}, []);
apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx (1)

14-19: Fix state setter naming for consistency.

The state setter name setTasks doesn't match the singular nature of the state variable task.

-const [task, setTasks] = React.useState('')
+const [task, setTask] = React.useState('')
apps/web/app/[locale]/timesheet/[memberId]/page.tsx (1)

Line range hint 41-65: Consider performance optimizations.

A few suggestions to improve performance:

  1. Debounce the search input to prevent excessive filtering on every keystroke
  2. Memoize the date range state to prevent unnecessary re-renders
  3. Consider using useCallback for event handlers

Example implementation for search debouncing:

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

// Memoize the debounced search handler
const debouncedSetSearch = useMemo(
  () => debounce((value: string) => setSearch(value), 300),
  []
);

// Memoize the onChange handler
const handleSearchChange = useCallback(
  (e: React.ChangeEvent<HTMLInputElement>) => {
    debouncedSetSearch(e.target.value);
  },
  [debouncedSetSearch]
);
apps/web/lib/features/integrations/calendar/table-time-sheet.tsx (4)

Line range hint 192-201: Enhance error handling in handleConfirm

The current error handling implementation has several issues:

  1. No user feedback on error
  2. State updates proceed even after error
  3. Redundant error logging

Consider this improved implementation:

 const handleConfirm = () => {
-  try {
-    deleteTaskTimesheet()
-      .then(() => {
-        setSelectTimesheet([]);
-        setIsDialogOpen(false);
-      })
-      .catch((error) => {
-        console.error('Delete timesheet error:', error);
-      });
-  } catch (error) {
-    console.error('Delete timesheet error:', error);
-  }
+  deleteTaskTimesheet()
+    .then(() => {
+      setSelectTimesheet([]);
+      setIsDialogOpen(false);
+    })
+    .catch((error) => {
+      console.error('Delete timesheet error:', error);
+      // Add user notification here
+      toast.error('Failed to delete timesheet. Please try again.');
+    });
 };

399-399: Consider extracting status-related logic

While the internationalization is correctly implemented, there are opportunities for improvement:

  1. Extract status color mapping to a constant:
const STATUS_COLOR_CLASSES = {
  Rejected: 'text-red-500 border-gray-200',
  Approved: 'text-green-500 border-gray-200',
  Pending: 'text-orange-500 border-gray-200',
  default: 'text-gray-500 border-gray-200'
} as const;
  1. Consider moving status options to a configuration file and adding translations:
export const STATUS_OPTIONS = {
  REJECTED: 'pages.timesheet.status.rejected',
  APPROVED: 'pages.timesheet.status.approved',
  PENDING: 'pages.timesheet.status.pending'
} as const;

Also applies to: 431-431


Line range hint 500-513: Improve type safety and key handling in status mapping

There are several potential issues in the status mapping implementation:

  1. Add type safety for status objects:
- {statusTable?.map((status, index) => (
+ {statusTable?.map((status: { label: TimesheetStatus }, index) => (
  1. Use meaningful keys instead of array indices:
- <DropdownMenuItem key={index} textValue={status.label} className="cursor-pointer">
+ <DropdownMenuItem key={status.label} textValue={status.label} className="cursor-pointer">
  1. Consider adding error boundaries or fallback UI for when statusTable is undefined:
if (!statusTable?.length) {
  return <DropdownMenuItem disabled>No status options available</DropdownMenuItem>;
}

Approval logic implementation required

The verification confirms there is a TODO comment for implementing approval logic in apps/web/lib/features/integrations/calendar/table-time-sheet.tsx. Since this is a documented placeholder and there's no existing implementation, this is a valid concern that needs to be addressed.

  • apps/web/lib/features/integrations/calendar/table-time-sheet.tsx: Missing approval logic implementation at the TODO comment
🔗 Analysis chain

Line range hint 267-269: Verify implementation status of approval logic

The TODO comment indicates missing approval logic implementation. This could affect the functionality of the timesheet approval flow.

Let's check for any existing approval implementations:

Would you like me to help implement the approval logic or create a GitHub issue to track this task?

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for approval-related implementations
echo "Searching for approval-related code..."
rg -l "handleApproval|approveTimesheet" --type ts --type tsx

# Search for similar TODO comments
echo "Checking for related TODO comments..."
rg "TODO.*implement.*approv" --type ts --type tsx

Length of output: 342


Script:

#!/bin/bash
# Search for approval-related implementations with correct file type flags
echo "Searching for approval-related code..."
rg -l "handleApproval|approveTimesheet" -t typescript -t tsx

# Search for TODO comments about approval
echo "Checking for related TODO comments..."
rg "TODO.*approv" -t typescript -t tsx

# Check the specific file for TODO comments
echo "Checking TODOs in the timesheet file..."
rg "TODO" "apps/web/lib/features/integrations/calendar/table-time-sheet.tsx"

Length of output: 547

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 681714b and 97521cb.

📒 Files selected for processing (7)
  • apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx (1 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/EditTaskModal.tsx (1 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/FilterWithStatus.tsx (2 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx (2 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/page.tsx (3 hunks)
  • apps/web/app/hooks/features/useTimesheet.ts (1 hunks)
  • apps/web/lib/features/integrations/calendar/table-time-sheet.tsx (7 hunks)
🧰 Additional context used
📓 Learnings (1)
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx (1)
Learnt from: Innocent-Akim
PR: ever-co/ever-teams#3208
File: apps/web/app/[locale]/timesheet/components/TimesheetFilter.tsx:30-35
Timestamp: 2024-11-12T14:06:02.202Z
Learning: In the `TimesheetFilter` component, the `Add Time` button does not need to use the `AddManualTimeModal` component, as per the user's decision.
🪛 Biome (1.9.4)
apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx

[error] 76-76: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)

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

[error] 172-172: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)

🪛 GitHub Check: Cspell
apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx

[warning] 28-28:
Unknown word (Preject)

🔇 Additional comments (8)
apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx (1)

5-6: LGTM! Import changes align with requirements

The replacement of AddManualTimeModal with AddTaskModal aligns with previous decisions and the PR objectives for task management optimization.

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

58-58: LGTM: UI enhancement for active state

The added shadow and border improve the visual feedback for the active filter state, making it more prominent and user-friendly.

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

1-13: LGTM! Clean imports and well-defined interface.

The imports are properly organized and the interface clearly defines the required props.

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

214-214: LGTM: Improved dark mode support.

The styling changes correctly implement dark mode variants while maintaining the existing active state behavior.


47-50: Verify the useTimesheet hook implementation.

The hook now destructures an additional statusTimesheet value. Please ensure that:

  1. The hook properly implements this new value
  2. The value is correctly computed based on the date range parameters
✅ Verification successful

The useTimesheet hook implementation is correct and properly supports statusTimesheet

The hook implementation correctly provides the statusTimesheet value through the getStatusTimesheet function, which:

  • Takes timesheet logs and groups them by their status (PENDING, APPROVED, DENIED, DRAFT, IN REVIEW)
  • Is properly exposed in the hook's return value
  • Works with the date range parameters as it processes the timesheet data fetched based on these dates
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the useTimesheet hook implementation
ast-grep --pattern 'export const useTimesheet = $_'

Length of output: 53


Script:

#!/bin/bash
# Search for useTimesheet hook implementation with a broader pattern and ripgrep
rg "useTimesheet" -A 10

# Also search for any file containing timesheet in its name that might contain the hook
fd "timesheet" --type f

Length of output: 6963


Script:

#!/bin/bash
# Let's examine the useTimesheet hook implementation
cat apps/web/app/hooks/features/useTimesheet.ts

Length of output: 5782


Line range hint 171-182: Verify TimesheetFilter component prop types.

The component now receives additional data prop containing timesheet status information. Please ensure that:

  1. The prop types are properly defined
  2. The component handles the new data prop correctly
✅ Verification successful

Let me try a different search pattern to find the TimesheetFilter component definition.


Let me try searching specifically for the TimesheetFilter component definition.


Let me check the TimesheetFilter component definition to verify its props.


Props are properly defined and match the component usage

The TimesheetFilter component has a properly defined interface ITimesheetFilter that includes all the props being passed, including:

  • data?: Record<TimesheetStatus, TimesheetLog[]> for the timesheet status data
  • filterStatus?: FilterStatus for the filter status
  • onChangeStatus?: (status: FilterStatus) => void for status change handling
  • initDate for date range handling
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the TimesheetFilter component definition
ast-grep --pattern 'type $_ = {
  $$$
  data: $_
  $$$
}'

Length of output: 898


Script:

#!/bin/bash
# Search for TimesheetFilter component and its props
ast-grep --pattern 'interface $_ {
  $$$
}'

# Also search for type definitions
ast-grep --pattern 'type $_ = {
  $$$
}'

# Backup search using ripgrep
rg -A 10 "TimesheetFilter.*Props" 

Length of output: 100310


Script:

#!/bin/bash
# Search for TimesheetFilter component file
fd TimesheetFilter

# Also search for any file containing TimesheetFilter
rg -l "TimesheetFilter"

Length of output: 536


Script:

#!/bin/bash
# Get the content of TimesheetFilter component
cat "apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx"

Length of output: 2066

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

55-56: LGTM! Import changes align with new functionality

The addition of getTimesheetButtons and statusTable imports supports the enhanced status management functionality.


449-450: LGTM! Internationalization properly implemented

The translation implementation for menu items follows best practices.

Also applies to: 463-463, 468-468

@Innocent-Akim Innocent-Akim force-pushed the feat/timesheet-creation-task-buttons branch from 97521cb to 6af3dc0 Compare November 22, 2024 09:09
@Innocent-Akim Innocent-Akim marked this pull request as ready for review November 22, 2024 14: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: 13

🧹 Outside diff range and nitpick comments (12)
apps/web/app/stores/time-logs.ts (1)

19-20: Consider enhancing state persistence and configuration

The new timesheetGroupByDayState atom is well-typed and follows proper conventions. However, consider these improvements:

  1. The default value could be moved to a configuration constant
  2. The state could be persisted across sessions if this is a user preference

Consider this refactor:

+// At the top of the file with other constants
+export const DEFAULT_TIMESHEET_GROUP_BY = 'Daily' as const;

-export const timesheetGroupByDayState = atom<TimesheetFilterByDays>('Daily')
+export const timesheetGroupByDayState = atom<TimesheetFilterByDays>(
+  // Get from localStorage or fall back to default
+  (typeof window !== 'undefined' && localStorage.getItem('timesheetGroupBy') as TimesheetFilterByDays) 
+  || DEFAULT_TIMESHEET_GROUP_BY
+);
apps/web/app/hooks/features/useTimelogFilterOptions.ts (1)

11-11: LGTM! State management implementation follows best practices

The implementation:

  • Uses Jotai's useAtom hook consistently with other state management in the file
  • Follows the established pattern of exposing both state and setter
  • Maintains consistent naming conventions

Consider documenting the expected values and usage of timesheetGroupByDays in a comment above the state initialization, as this appears to be a key feature for timesheet organization.

Also applies to: 36-37

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

Line range hint 34-72: Consider improving accessibility and maintainability

A few suggestions to enhance this section:

  1. Move color values (e.g., #2932417c) to a theme system for better maintainability
  2. Add aria-labels to icon buttons for better accessibility

Example improvement:

 <TimesheetButton
     className="hover:underline text-sm gap-2"
     disabled={disabled}
     key={index}
     icon={button.icon}
+    aria-label={button.title}
     onClick={() => onClick(button.action)}
     title={button.title}
 />
apps/web/app/interfaces/ITask.ts (1)

147-150: LGTM! Consider using an enum for better type safety.

The new TimesheetFilterByDays type is well-defined and appropriately placed. However, since these are fixed values that represent distinct cases, consider using an enum instead of a union type for better type safety and maintainability.

Here's a suggested enhancement:

-export type TimesheetFilterByDays =
-	| "Daily"
-	| "Weekly"
-	| "Monthly"
+export enum TimesheetFilterByDays {
+    Daily = "Daily",
+    Weekly = "Weekly",
+    Monthly = "Monthly"
+}

This change would:

  • Prevent accidental string assignments
  • Enable using the enum as a type and value
  • Make it easier to add new filter options in the future
  • Provide better IDE support for value completion
apps/web/app/[locale]/timesheet/[memberId]/components/FrequencySelect.tsx (2)

36-52: LGTM: Clean state management implementation.

The frequency selection logic is well-implemented with proper type safety. However, consider simplifying the handleSelectChange function since it's a simple passthrough.

Optional improvement:

-const handleSelectChange = (value: string) => {
-    setTimesheetGroupByDays(value as TimesheetFilterByDays);
-};

<Select
    value={timesheetGroupByDays}
-   onValueChange={handleSelectChange}>
+   onValueChange={(value) => setTimesheetGroupByDays(value as TimesheetFilterByDays)}>

The FrequencySelect component should be removed as it's redundant with TimesheetFilterDate

The codebase shows that:

  1. PR [Feat]: Implement dynamic date range picker for timesheet filtering #3213 implemented the date range filtering functionality through TimesheetFilterDate component
  2. TimesheetFilterDate already includes all the functionality present in FrequencySelect:
    • Today filter
    • Last 7 days
    • Last 30 days
    • This year
    • Custom date range with date picker

The FrequencySelect component in the reviewed file appears to be a duplicate implementation that should be removed, as the proper date range filtering is already handled by TimesheetFilterDate which is being used in the same TimesheetFilter component.

🔗 Analysis chain

Line range hint 59-146: Verify the status of date range selection implementation.

Based on previous learnings (PR #3208), the date range selection functionality was planned for a future PR. However, this PR includes a basic implementation. Please clarify if:

  1. This is the planned implementation mentioned in PR Feat: Implement Timesheet Filter Component With Status, Date Filters #3208
  2. This implementation might conflict with or duplicate future changes

Let's check for any related implementations or discussions:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar date range implementations
rg -l "CustomDateRange|FilterTaskActionMenu" --type tsx

# Search for related PR discussions
gh pr list --search "date range in:title,body" --state all --limit 5

Length of output: 791


Script:

#!/bin/bash
# Let's check the content of PR #3213 which seems directly related
gh pr view 3213 --json body

# Search for similar date range implementations with correct file extension
rg -l "CustomDateRange|FilterTaskActionMenu" --type ts --type tsx --type jsx --type js

# Look for any other date range related components
rg -l "DateRange|date.*range" --type ts --type tsx --type jsx --type js

Length of output: 1730


Script:

#!/bin/bash
# Try with just the file extension without --type flag
rg -l "CustomDateRange|FilterTaskActionMenu" .*\.[tj]sx?$

# Search for TimesheetFilterDate mentioned in PR #3213
rg -l "TimesheetFilterDate" .*\.[tj]sx?$

# Look for any files with timesheet and filter in their path
fd -e tsx -e ts -e js -e jsx . | grep -i "timesheet.*filter"

Length of output: 577


Script:

#!/bin/bash
# Check the content of TimesheetFilterDate.tsx which seems to be the main implementation
cat "apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilterDate.tsx"

# Check TimesheetFilter.tsx to understand the overall filter structure
cat "apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx"

# Check if FrequencySelect.tsx is being used in these files
rg "FrequencySelect" "apps/web/app/[locale]/timesheet/[memberId]/components/Timesheet.*"

Length of output: 13524

🧰 Tools
🪛 Biome (1.9.4)

[error] 34-34: Unexpected empty array pattern.

(lint/correctness/noEmptyPattern)

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

54-59: Add type annotation and consider edge cases.

The week calculation looks correct, but consider adding a return type annotation and handling edge cases around year boundaries.

-const getWeekYearKey = (date: Date): string => {
+const getWeekYearKey = (date: Date): `${number}-W${number}` => {

222-230: Enhance memoization robustness.

The memoization logic could be more robust by:

  1. Using a type-safe discriminator for grouping options
  2. Handling invalid grouping options
type GroupingOption = 'Daily' | 'Weekly' | 'Monthly';

const timesheetElementGroup = useMemo(() => {
    switch (timesheetGroupByDays as GroupingOption) {
        case 'Daily':
            return groupByDate(timesheet);
        case 'Weekly':
            return groupByWeek(timesheet);
        case 'Monthly':
            return groupByMonth(timesheet);
        default:
            console.warn(`Invalid grouping option: ${timesheetGroupByDays}, falling back to Daily`);
            return groupByDate(timesheet);
    }
}, [timesheetGroupByDays, timesheet]);

Line range hint 123-245: Standardize error handling and improve error messages.

Consider implementing a consistent error handling strategy:

  1. Add error boundaries for React components using this hook
  2. Improve error messages with more context
  3. Consider adding error state to the hook's return value

Example implementation:

interface TimesheetError {
    code: string;
    message: string;
    context?: unknown;
}

// Add to return object
error?: TimesheetError;
setError: (error: TimesheetError | undefined) => void;

// Use in error handlers
catch (error) {
    setError({
        code: 'TIMESHEET_FETCH_ERROR',
        message: 'Failed to fetch timesheet data',
        context: error
    });
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 245-245: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)

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

508-513: Add type safety to status table mapping

The current implementation could benefit from stronger typing to prevent potential runtime errors.

Consider adding type safety:

- {statusTable?.map((status, index) => (
+ {statusTable?.map((status: { label: TimesheetStatus }, index) => (

403-403: Consider centralizing translation keys

While the translations are correctly implemented, maintaining translation keys across the file could become challenging as the component grows.

Consider creating a constant object for translation keys at the top of the file:

const TRANSLATION_KEYS = {
  common: {
    STATUS: 'common.STATUS',
    EDIT: 'common.EDIT',
    DELETE: 'common.DELETE'
  },
  timesheet: {
    BILLABLE: 'pages.timesheet.BILLABLE.BILLABLE'
  }
} as const;

Then use it throughout the component:

t(TRANSLATION_KEYS.common.EDIT)

Also applies to: 453-454, 471-471, 476-476, 521-521


Line range hint 156-171: Enhance error handling for timesheet deletion

The current error handling only logs to console, which doesn't provide feedback to users when operations fail.

Consider implementing proper error handling:

 const handleConfirm = () => {
   try {
     deleteTaskTimesheet()
       .then(() => {
         setSelectTimesheet([]);
         setIsDialogOpen(false);
+        // Show success notification
+        toast.success(t('common.DELETE_SUCCESS'));
       })
       .catch((error) => {
         console.error('Delete timesheet error:', error);
+        // Show error notification
+        toast.error(t('common.DELETE_ERROR'));
+        setIsDialogOpen(false);
       });
   } catch (error) {
     console.error('Delete timesheet error:', error);
+    // Show error notification
+    toast.error(t('common.DELETE_ERROR'));
+    setIsDialogOpen(false);
   }
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 97521cb and e614e65.

📒 Files selected for processing (11)
  • apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx (1 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/FilterWithStatus.tsx (2 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/FrequencySelect.tsx (1 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetAction.tsx (3 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx (2 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/page.tsx (3 hunks)
  • apps/web/app/hooks/features/useTimelogFilterOptions.ts (3 hunks)
  • apps/web/app/hooks/features/useTimesheet.ts (3 hunks)
  • apps/web/app/interfaces/ITask.ts (1 hunks)
  • apps/web/app/stores/time-logs.ts (2 hunks)
  • apps/web/lib/features/integrations/calendar/table-time-sheet.tsx (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetFilter.tsx
  • apps/web/app/[locale]/timesheet/[memberId]/page.tsx
🧰 Additional context used
📓 Learnings (1)
apps/web/app/[locale]/timesheet/[memberId]/components/FrequencySelect.tsx (1)
Learnt from: Innocent-Akim
PR: ever-co/ever-teams#3208
File: apps/web/app/[locale]/timesheet/components/FrequencySelect.tsx:67-79
Timestamp: 2024-11-12T14:06:02.202Z
Learning: The date range selection functionality for the date range options in the `FilterTaskActionMenu` component will be implemented in a future PR.
🪛 Biome (1.9.4)
apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx

[error] 77-77: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)

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

[error] 34-34: Unexpected empty array pattern.

(lint/correctness/noEmptyPattern)

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

[error] 245-245: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)

🔇 Additional comments (8)
apps/web/app/stores/time-logs.ts (1)

3-3: LGTM! Clean import addition

The import statement is well-organized, grouping the new TimesheetFilterByDays type with related interfaces.

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

1-1: Verify the usage of timesheetGroupByDayState atom

The atom import looks correct, but let's verify its usage across the codebase to ensure proper integration.

✅ Verification successful

Based on the shell script results, I can now generate the final response as the usage of timesheetGroupByDayState is well-documented across the codebase:

The timesheetGroupByDayState atom is properly integrated and used

The atom is correctly defined in the stores and consistently used across the codebase:

  • Defined as a Jotai atom in apps/web/app/stores/time-logs.ts
  • Properly consumed in useTimelogFilterOptions hook
  • Actively used in multiple components:
    • useTimesheet.ts for grouping logic (Daily/Weekly/Monthly)
    • FrequencySelect.tsx for frequency selection UI
    • table-time-sheet.tsx for display formatting
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check usage of timesheetGroupByDayState atom
# Expected: Find references in components using this grouping functionality

# Search for direct usage of the atom
echo "Searching for direct atom usage..."
rg "timesheetGroupByDayState" -A 3

# Search for the state variables to understand component integration
echo "Searching for state variable usage..."
rg "timesheetGroupByDays" -A 3

Length of output: 6007

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

5-5: LGTM: Type definitions are well-structured

The import and type definitions are clear and properly organized.

Also applies to: 7-7


11-16: Skip: Issue already identified in previous review

The nullability handling issue has been previously identified and a solution has been proposed.


58-58: LGTM: Style enhancement for active status

The addition of border styling for the active status button improves visual feedback.

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

74-78: 🛠️ Refactor suggestion

Enhance DailyTable implementation

A few suggestions for improvement:

  1. Use translation keys for internationalization support
  2. Add more descriptive explanations in the descriptions
  3. Consider adding type validation for the label values

Here's a suggested implementation:

 export const DailyTable: { label: TimesheetFilterByDays; description: string }[] = [
-    { label: "Daily", description: 'Group by Daily' },
-    { label: "Weekly", description: 'Group by Weekly' },
-    { label: "Monthly", description: 'Group by Monthly' },
+    { 
+        label: "Daily", 
+        description: t('pages.timesheet.GROUP_DAILY_DESC') 
+    },
+    { 
+        label: "Weekly", 
+        description: t('pages.timesheet.GROUP_WEEKLY_DESC') 
+    },
+    { 
+        label: "Monthly", 
+        description: t('pages.timesheet.GROUP_MONTHLY_DESC') 
+    },
 ];

Let's verify if these strings are already defined in the translation files:

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

25-26: LGTM: Import statements are properly structured.

The new imports for hooks and types are correctly added and align with the component's enhanced functionality.

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

1-13: LGTM! Clean imports and well-defined interface.

The imports are properly organized and the interface clearly defines the required props.

@Innocent-Akim Innocent-Akim removed the Don't Merge Do not merge this PR until label not removed label Nov 22, 2024
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: 0

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

202-210: Enhance type safety for grouping selection.

Consider using a type-safe approach for the grouping selection to prevent potential runtime errors.

+type TimesheetGroupByDays = 'Daily' | 'Weekly' | 'Monthly';

 const timesheetElementGroup = useMemo(() => {
-    if (timesheetGroupByDays === 'Daily') {
-        return groupByDate(timesheet);
-    }
-    if (timesheetGroupByDays === 'Weekly') {
-        return groupByWeek(timesheet);
-    }
-    return groupByMonth(timesheet);
+    const groupingMap: Record<TimesheetGroupByDays, (items: TimesheetLog[]) => GroupedTimesheet[]> = {
+        'Daily': groupByDate,
+        'Weekly': groupByWeek,
+        'Monthly': groupByMonth
+    };
+    return groupingMap[timesheetGroupByDays](timesheet);
 }, [timesheetGroupByDays, timesheet]);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e614e65 and a2e98aa.

📒 Files selected for processing (4)
  • apps/web/app/[locale]/timesheet/[memberId]/components/FilterWithStatus.tsx (2 hunks)
  • apps/web/app/[locale]/timesheet/[memberId]/components/FrequencySelect.tsx (1 hunks)
  • apps/web/app/hooks/features/useTimesheet.ts (3 hunks)
  • apps/web/lib/features/integrations/calendar/table-time-sheet.tsx (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/web/app/[locale]/timesheet/[memberId]/components/FilterWithStatus.tsx
  • apps/web/app/[locale]/timesheet/[memberId]/components/FrequencySelect.tsx
  • apps/web/lib/features/integrations/calendar/table-time-sheet.tsx
🔇 Additional comments (5)
apps/web/app/hooks/features/useTimesheet.ts (5)

54-59: Verify week calculation around year boundaries.

The week calculation logic might have edge cases around year boundaries. Consider using a well-tested date library like date-fns for more reliable week calculations.

-const getWeekYearKey = (date: Date): string => {
-    const startOfYear = new Date(date.getFullYear(), 0, 1);
-    const daysSinceStart = Math.floor((date.getTime() - startOfYear.getTime()) / (1000 * 60 * 60 * 24));
-    const week = Math.ceil((daysSinceStart + startOfYear.getDay() + 1) / 7);
-    return `${date.getFullYear()}-W${week}`;
-};
+import { getWeek, getYear } from 'date-fns';
+
+const getWeekYearKey = (date: Date): string => {
+    return `${getYear(date)}-W${getWeek(date)}`;
+};

64-95: LGTM! Well-structured grouping functions.

The refactoring effectively addresses the code duplication issue from the previous review. The higher-order function pattern provides a clean and maintainable solution.


103-103: LGTM! Clean state management integration.

The addition of timesheetGroupByDays from useTimelogFilterOptions is well-integrated with the existing state management pattern.


215-215: LGTM! Proper effect dependencies.

The addition of timesheetGroupByDays to the effect dependencies ensures proper data synchronization when grouping changes.


219-225: LGTM! Clean return object updates.

The return object updates are well-structured and consistent with the new grouping functionality. Good job addressing the previous review comment about using flat() instead of flatMap().

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