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]: Productivity project table view #3616

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from

Conversation

Innocent-Akim
Copy link
Contributor

@Innocent-Akim Innocent-Akim commented Feb 20, 2025

Description

#3261
Please include a summary of the changes and the related issues.

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 on 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 the previous status

Current screenshots

Please add here videos or images of the current (new) status

Summary by CodeRabbit

  • New Features

    • Enhanced the productivity dashboard with dynamic grouping options, allowing users to switch between different data views (e.g., by date or project).
    • Introduced new components for displaying project activities, including progress bars, detailed activity rows, and a comprehensive employee productivity table.
    • Added intuitive loading and empty states for improved feedback when data is being fetched or is unavailable.
  • Style

    • Refined layout elements to ensure responsive loading indicators and progress visuals for a smoother display.

- Create new ProductivityProjectTable component
- Group activities by project with dedicated headers
- Update activity report interfaces
- Implement date-based sorting within project groups
- Add visual progress indicators for time usage
- Group activities by both project and date
- Combine activities for the same date
- Show all unique members and applications per date
- Fix type error in duration formatting
- Change totalDuration type to string
- Fix string/number type conversions in duration calculations
- Ensure consistent type handling throughout the component
- Add hierarchical structure with project, date, and activity levels
- Show individual member activities with time and percentage
- Add date summaries with total time and combined percentage
- Improve visual hierarchy with subtle background colors
- Split into smaller, reusable components
- Add proper TypeScript interfaces
- Extract data processing logic
- Improve code organization and maintainability
- Add loading and empty states as separate components
- Split into modular components with proper file organization
- Create separate files for types, utils, components, and states
- Improve TypeScript types and interfaces
- Enhance dark mode support and styling
- Add proper component documentation
- Replace if/else and ternary operators with switch statements
- Simplify APPS-URLS case to only fetch activity report
- Standardize Promise.all usage for default case
- Improve code consistency across all fetch operations
- Add conditional rendering based on groupBy type
- Show ProductivityProjectTable for project grouping
- Show ProductivityTable for date grouping
- Improve skeleton loading UI with full-width progress bars
- Fix group type handling in switch statement
Copy link
Contributor

coderabbitai bot commented Feb 20, 2025

Warning

Rate limit exceeded

@Innocent-Akim has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 12 minutes and 24 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between bb4ee5e and 6c17e8a.

📒 Files selected for processing (1)
  • apps/web/app/[locale]/dashboard/app-url/components/productivity-employee/ProductivityEmployeeTable.tsx (1 hunks)

Walkthrough

The changes update the dashboard’s productivity view. In the AppUrls component, a new state variable groupByType is introduced along with an inline handler for grouping changes. The rendering logic now conditionally displays either ProductivityProjectTable, ProductivityTable, or ProductivityEmployeeTable. Additionally, skeleton and progress bar widths in ProductivityTable have been adjusted. New productivity project components, associated states, TypeScript interfaces, and utility functions have been added. The useReportActivity hook now uses a switch statement for conditional fetching logic, and two new activity report interfaces have been introduced.

Changes

File(s) Change Summary
apps/web/app/[locale]/dashboard/app-url/[teamId]/page.tsx Added groupByType state, updated onGroupByChange handler to update state and call handleGroupByChange, conditional rendering between ProductivityProjectTable, ProductivityTable, and ProductivityEmployeeTable, and reorganized import order.
apps/web/app/[locale]/dashboard/app-url/components/ProductivityTable.tsx Modified the Skeleton and progress bar width properties from fixed w-24 to flexible w-full.
apps/web/app/[locale]/dashboard/app-url/components/productivity-project/* Added new components (ProgressBar, ActivityRow, DateSummaryRow, ProjectHeaderRow, DateHeaderRow), a new ProductivityProjectTable component, and state components (LoadingSkeleton, EmptyState); introduced new TypeScript interfaces and utility functions (formatDuration, groupActivitiesByProjectAndDate).
apps/web/app/hooks/features/useReportActivity.ts Revised control flow with a switch statement based on the types parameter for conditional fetching and improved error handling.
apps/web/app/interfaces/activity/IActivityReport.ts Introduced new interfaces: IProjectActivity and IActivityReportByProject for project-specific activity reporting.
apps/web/app/[locale]/dashboard/app-url/components/GroupBySelect.tsx Updated SelectItem value from "person" to "employee" while keeping the display label as "Person".
apps/web/app/[locale]/dashboard/app-url/components/productivity-employee/ProductivityEmployeeTable.tsx Introduced a new component to display employee productivity data, including props for data and loading state, and organized activity data by employee and date.

Possibly related PRs

Suggested labels

Timesheets

Suggested reviewers

  • evereq

Poem

I'm a hopping rabbit with code in my heart,
Leaping through changes, a brand new start.
With group-by states and tables anew,
I nibble on bugs and chase them off too.
Lines of code dance in rhythmic flow,
Celebrating each commit with a joyful glow!
🥕 Happy coding, off we go!


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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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.

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: 4

🧹 Nitpick comments (8)
apps/web/app/[locale]/dashboard/app-url/components/productivity-project/types.ts (2)

3-9: Consider adding validation constraints to the Employee interface.

The interface could benefit from additional type safety:

  • Consider making fullName a non-empty string
  • Add validation for image URL format
 export interface Employee {
   id: string;
-  fullName: string;
+  fullName: NonEmptyString; // Create a type that ensures non-empty string
   user?: {
-    imageUrl?: string;
+    imageUrl?: URL | string; // Consider using URL type for better validation
   };
 }

16-26: Consider adding validation for project names and dates.

The ProjectGroups interface could benefit from stronger typing for project names and dates:

 export interface ProjectGroups {
-  [projectName: string]: {
-    [date: string]: ProjectGroupData;
+  [projectName: NonEmptyString]: {
+    [date: ISODateString]: ProjectGroupData; // Create a type for ISO date strings
   };
 }
apps/web/app/[locale]/dashboard/app-url/components/productivity-project/states.tsx (2)

25-39: Extract magic number and consider pagination limits.

The hardcoded value of 7 rows should be extracted to a constant and consider matching the actual pagination limit:

+const SKELETON_ROWS = 7; // Match this with your pagination limit
 {[...Array(7)].map((_, i) => (

44-51: Consider internationalizing the text content.

The hardcoded text should be internationalized for better maintainability:

 export const EmptyState: React.FC = () => (
   <Card className="bg-white rounded-md border border-gray-100 dark:border-gray-800 dark:bg-dark--theme-light min-h-[600px] flex items-center justify-center w-full">
     <div className="text-center text-gray-500 dark:text-gray-400">
-      <p className="text-lg font-medium">No activity data available</p>
-      <p className="text-sm">Select a different date range or check back later</p>
+      <p className="text-lg font-medium">{t('no_activity_data')}</p>
+      <p className="text-sm">{t('select_different_date_range')}</p>
     </div>
   </Card>
 );
apps/web/app/[locale]/dashboard/app-url/components/productivity-project/components.tsx (1)

54-56: Remove commented code.

The commented code block should either be removed or uncommented if needed.

-      {/* <TableCell colSpan={2} className="text-sm text-gray-500 dark:text-gray-400">
-        Total for {format(new Date(date), 'MMM dd')}
-      </TableCell> */}
apps/web/app/[locale]/dashboard/app-url/[teamId]/page.tsx (1)

136-145: Simplify the switch statement.

The switch statement can be simplified as the default case returns the same component as the 'date' case.

-				{(() => {
-					switch (groupByType) {
-						case 'project':
-							return <ProductivityProjectTable data={activityReport} isLoading={loadingActivityReport} />;
-						case 'date':
-							return <ProductivityTable data={activityReport} isLoading={loadingActivityReport} />;
-						default:
-							return <ProductivityTable data={activityReport} isLoading={loadingActivityReport} />;
-					}
-				})()}
+				{groupByType === 'project' ? (
+					<ProductivityProjectTable data={activityReport} isLoading={loadingActivityReport} />
+				) : (
+					<ProductivityTable data={activityReport} isLoading={loadingActivityReport} />
+				)}
apps/web/app/hooks/features/useReportActivity.ts (2)

72-72: Consider adding JSDoc documentation for the types parameter.

The introduction of the types parameter is a good pattern for managing different data fetching strategies. Consider adding JSDoc documentation to explain the purpose and impact of each type.

+/**
+ * @param types - Determines the data fetching strategy:
+ * - 'TEAM-DASHBOARD': Fetches all reports in parallel
+ * - 'APPS-URLS': Fetches only activity report
+ */
 export function useReportActivity({ types }: { types?: 'TEAM-DASHBOARD' | 'APPS-URLS' }) {

Also applies to: 88-88, 91-91


243-254: Consider consistent error handling across switch cases.

While both error handling approaches work, consider using a consistent pattern. You could either:

  1. Use try-catch blocks in both cases, or
  2. Use .catch(console.error) consistently
 switch (types) {
   case 'APPS-URLS':
-    fetchActivityReport(newProps).catch(console.error);
+    try {
+      await fetchActivityReport(newProps);
+    } catch (error) {
+      console.error('Failed to fetch activity report:', error);
+    }
     break;
   default:
-    Promise.all([
-      fetchReportActivity(newProps),
-      fetchDailyReport(newProps),
-      fetchStatisticsCounts(newProps)
-    ]).catch(console.error);
+    try {
+      await Promise.all([
+        fetchReportActivity(newProps),
+        fetchDailyReport(newProps),
+        fetchStatisticsCounts(newProps)
+      ]);
+    } catch (error) {
+      console.error('Failed to fetch reports:', error);
+    }
     break;
 }

Also applies to: 273-284

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f17ceec and 2bd8218.

📒 Files selected for processing (9)
  • apps/web/app/[locale]/dashboard/app-url/[teamId]/page.tsx (4 hunks)
  • apps/web/app/[locale]/dashboard/app-url/components/ProductivityTable.tsx (2 hunks)
  • apps/web/app/[locale]/dashboard/app-url/components/productivity-project/components.tsx (1 hunks)
  • apps/web/app/[locale]/dashboard/app-url/components/productivity-project/index.tsx (1 hunks)
  • apps/web/app/[locale]/dashboard/app-url/components/productivity-project/states.tsx (1 hunks)
  • apps/web/app/[locale]/dashboard/app-url/components/productivity-project/types.ts (1 hunks)
  • apps/web/app/[locale]/dashboard/app-url/components/productivity-project/utils.ts (1 hunks)
  • apps/web/app/hooks/features/useReportActivity.ts (5 hunks)
  • apps/web/app/interfaces/activity/IActivityReport.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: deploy
🔇 Additional comments (9)
apps/web/app/interfaces/activity/IActivityReport.ts (1)

127-145: LGTM! Well-structured interfaces for project activity reporting.

The new interfaces IProjectActivity and IActivityReportByProject are well-designed and properly documented, providing a clear structure for project-based activity reporting.

apps/web/app/[locale]/dashboard/app-url/components/productivity-project/components.tsx (1)

8-93: LGTM! Well-structured and reusable components.

The components are well-designed with proper type definitions, error handling, and consistent styling. Good use of composition and reusability.

apps/web/app/[locale]/dashboard/app-url/[teamId]/page.tsx (1)

37-37: LGTM! Good state management implementation.

The state management for group by type is well-implemented with proper type safety and state updates.

Also applies to: 107-111

apps/web/app/[locale]/dashboard/app-url/components/ProductivityTable.tsx (1)

45-45: LGTM! Improved UI consistency.

The changes to skeleton and progress bar widths improve UI consistency and space utilization.

Also applies to: 145-145

apps/web/app/hooks/features/useReportActivity.ts (5)

112-114: LGTM!

The formatting changes improve readability while maintaining the same functionality.


120-127: LGTM!

The consistent use of the nullish coalescing operator improves readability and maintains the same fallback behavior.


147-151: LGTM!

The union type correctly includes the new queryActivityReport function while maintaining type safety.


292-303: Apply consistent error handling here as well.

This section has the same error handling inconsistency as noted in the previous comment.


307-329: LGTM!

The return object is well-organized with logical grouping of related values.

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

♻️ Duplicate comments (2)
apps/web/app/[locale]/dashboard/app-url/components/productivity-project/utils.ts (2)

4-11: ⚠️ Potential issue

Improve type safety and error handling in formatDuration.

The function could benefit from better input validation and error handling.

-export const formatDuration = (seconds: string): string => {
+export const formatDuration = (seconds: string | number): string => {
+  if (typeof seconds === 'string' && !/^\d+$/.test(seconds)) {
+    throw new Error('Invalid duration format');
+  }
   const totalSeconds = parseInt(seconds);
+  if (isNaN(totalSeconds) || totalSeconds < 0) {
+    throw new Error('Invalid duration value');
+  }
   const hours = Math.floor(totalSeconds / 3600);
   const minutes = Math.floor((totalSeconds % 3600) / 60);
   const remainingSeconds = totalSeconds % 60;

25-38: ⚠️ Potential issue

Replace 'any' types with proper interfaces.

The function uses any type which reduces type safety.

+interface IEmployeeData {
+  projects?: Array<{
+    activity: IActivityItem[];
+  }>;
+  activity?: IActivityItem[];
+  employee?: IEmployee;
+}
+
+interface IActivityItem {
+  projectId?: string;
+  duration?: string;
+}
+
+interface IEmployee {
+  id: string;
+}
+
-    dayData.employees.forEach((employeeData: any) => {
+    dayData.employees.forEach((employeeData: IEmployeeData) => {
       if (!employeeData) {
         console.warn('employeeData is undefined');
         return;
       }

       const activities = employeeData.projects?.[0]?.activity || employeeData.activity || [];

       if (!Array.isArray(activities)) {
         console.warn('activities must be an array');
         return;
       }

-      activities.forEach((activity: any) => {
+      activities.forEach((activity: IActivityItem) => {
🧹 Nitpick comments (6)
apps/web/app/[locale]/dashboard/app-url/components/productivity-project/states.tsx (2)

12-42: Consider making the number of skeleton rows configurable.

The component looks good, but consider making the number of skeleton rows configurable via props for better reusability.

-export const LoadingSkeleton: React.FC = () => (
+interface LoadingSkeletonProps {
+  rowCount?: number;
+}
+
+export const LoadingSkeleton: React.FC<LoadingSkeletonProps> = ({ rowCount = 7 }) => (
   <Card className="bg-white rounded-md border border-gray-100 dark:border-gray-800 dark:bg-dark--theme-light min-h-[600px] w-full">
     <Table>
       <TableHeader>
         <TableRow className="bg-gray-50 dark:bg-gray-800">
           <TableHead className="font-semibold">Date</TableHead>
           <TableHead className="font-semibold">Member</TableHead>
           <TableHead className="font-semibold">Application</TableHead>
           <TableHead className="font-semibold">Time Spent</TableHead>
           <TableHead className="font-semibold">Percent used</TableHead>
         </TableRow>
       </TableHeader>
       <TableBody>
-        {[...Array(7)].map((_, i) => (
+        {[...Array(rowCount)].map((_, i) => (

48-67: Add i18n support for better internationalization.

The component looks good, but consider adding i18n support for the text content to support multiple languages.

+import { useTranslations } from 'next-intl';
+
 export const EmptyState: React.FC<EmptyStateProps> = ({ selectedDate }) => {
+  const t = useTranslations('productivity');
   const formattedDate = selectedDate ? new Date(selectedDate).toLocaleDateString('en-US', {
     weekday: 'long',
     day: '2-digit',
     month: 'long',
     year: 'numeric'
   }) : '';

   return (
     <Card className="bg-white rounded-md border border-gray-100 dark:border-gray-800 dark:bg-dark--theme-light min-h-[600px] flex items-center justify-center w-full">
       <div className="text-center text-gray-500 dark:text-gray-400">
-        <p className="text-lg font-medium">No activity data available</p>
+        <p className="text-lg font-medium">{t('noActivityData')}</p>
         {selectedDate && (
-          <p className="mb-2 text-sm">for {formattedDate}</p>
+          <p className="mb-2 text-sm">{t('forDate', { date: formattedDate })}</p>
         )}
-        <p className="text-sm">Select a different date range or check back later</p>
+        <p className="text-sm">{t('selectDifferentDate')}</p>
       </div>
     </Card>
   );
 };
apps/web/app/[locale]/dashboard/app-url/[teamId]/page.tsx (1)

139-148: Consider using object literal for conditional rendering.

The IIFE with switch statement could be simplified using an object literal for better readability and maintainability.

-{(() => {
-  switch (groupByType) {
-    case 'project':
-      return <ProductivityProjectTable data={activityReport} isLoading={loadingActivityReport} />;
-    case 'date':
-      return <ProductivityTable data={activityReport} isLoading={loadingActivityReport} />;
-    case 'employee':
-      return <ProductivityEmployeeTable data={activityReport} isLoading={loadingActivityReport} />;
-  }
-})()}
+{
+  {
+    'project': <ProductivityProjectTable data={activityReport} isLoading={loadingActivityReport} />,
+    'date': <ProductivityTable data={activityReport} isLoading={loadingActivityReport} />,
+    'employee': <ProductivityEmployeeTable data={activityReport} isLoading={loadingActivityReport} />
+  }[groupByType]
+}
apps/web/app/[locale]/dashboard/app-url/components/productivity-employee/ProductivityEmployeeTable.tsx (3)

228-228: Remove console.log statements.

Development console.log statements should be removed before merging.

-    console.log("New data received:", data);
-    console.log("Grouping data:", localData);

Also applies to: 233-233


232-284: Consider extracting data transformation logic.

The data grouping logic in the useMemo hook is complex and could be moved to a separate utility function for better maintainability and testability.

+const groupEmployeeData = (data: any[]): Map<string, EmployeeActivityGroup> => {
+  const employeeMap = new Map<string, EmployeeActivityGroup>();
+  if (!Array.isArray(data)) {
+    console.warn('Invalid data format: array expected');
+    return employeeMap;
+  }
+  // ... rest of the grouping logic ...
+  return employeeMap;
+};

 const groupedData = React.useMemo(() => {
-  console.log("Grouping data:", localData);
-  const employeeMap = new Map<string, EmployeeActivityGroup>();
-  // ... rest of the current logic ...
+  return groupEmployeeData(localData);
 }, [localData]);

290-293: Consider adding a more descriptive empty state message.

The empty state could provide more context about why no data is available and what actions the user can take.

-    return <EmptyState selectedDate={selectedDate} />;
+    return (
+      <EmptyState
+        selectedDate={selectedDate}
+        message="No employee productivity data available for the selected date range."
+        action="Try selecting a different date range or updating the filters."
+      />
+    );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b351dd and b0ee249.

📒 Files selected for processing (6)
  • apps/web/app/[locale]/dashboard/app-url/[teamId]/page.tsx (5 hunks)
  • apps/web/app/[locale]/dashboard/app-url/components/GroupBySelect.tsx (1 hunks)
  • apps/web/app/[locale]/dashboard/app-url/components/productivity-employee/ProductivityEmployeeTable.tsx (1 hunks)
  • apps/web/app/[locale]/dashboard/app-url/components/productivity-project/components.tsx (1 hunks)
  • apps/web/app/[locale]/dashboard/app-url/components/productivity-project/states.tsx (1 hunks)
  • apps/web/app/[locale]/dashboard/app-url/components/productivity-project/utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/[locale]/dashboard/app-url/components/productivity-project/components.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (5)
apps/web/app/[locale]/dashboard/app-url/components/productivity-project/utils.ts (1)

13-75: Add unit tests for the groupActivitiesByProjectAndDate function.

The function has complex logic and error handling that should be covered by unit tests.

Create a new test file utils.test.ts with the following test cases:

import { groupActivitiesByProjectAndDate } from './utils';
import { IActivityReportGroupByDate } from '@/app/interfaces/activity/IActivityReport';

describe('groupActivitiesByProjectAndDate', () => {
  it('should handle empty input array', () => {
    const result = groupActivitiesByProjectAndDate([]);
    expect(result).toEqual({});
  });

  it('should handle invalid input', () => {
    const result = groupActivitiesByProjectAndDate(null as any);
    expect(result).toEqual({});
  });

  it('should group activities by project and date', () => {
    const input: IActivityReportGroupByDate[] = [{
      date: '2024-02-14',
      employees: [{
        employee: { id: '1', name: 'John' },
        projects: [{
          activity: [{
            projectId: 'project1',
            duration: '3600'
          }]
        }]
      }]
    }];

    const result = groupActivitiesByProjectAndDate(input);
    
    expect(result).toHaveProperty('project1');
    expect(result.project1).toHaveProperty('2024-02-14');
    expect(result.project1['2024-02-14'].totalDuration).toBe(3600);
    expect(result.project1['2024-02-14'].members.size).toBe(1);
  });

  it('should handle missing project ID', () => {
    const input: IActivityReportGroupByDate[] = [{
      date: '2024-02-14',
      employees: [{
        employee: { id: '1', name: 'John' },
        activity: [{
          duration: '3600'
        }]
      }]
    }];

    const result = groupActivitiesByProjectAndDate(input);
    
    expect(result).toHaveProperty('No project');
  });
});
apps/web/app/[locale]/dashboard/app-url/components/GroupBySelect.tsx (1)

22-22:

❓ Verification inconclusive

Verify the impact of changing the select value from "person" to "employee".

The value change from "person" to "employee" could break existing code that relies on the "person" value. Please ensure all consumers of this component are updated accordingly.


🏁 Script executed:

#!/bin/bash
# Search for any remaining usage of "person" value in group by logic
rg -i "groupByType.*[\"']person[\"']" -g "*.{ts,tsx,js,jsx}"

Length of output: 66


Manual Verification Required: Impact of Value Change in GroupBySelect

The updated select value from "person" to "employee" appears to have been applied, and our automated search using the rg command did not return any references to a "groupBy" logic checking for the "person" value. However, the empty output might not be fully conclusive. Since this change constitutes a breaking change on a public API, please manually verify that no downstream consumers depend on the old "person" value. In particular, confirm that any grouping logic or associated configurations have been updated to work with "employee."

  • Verify all usages of the GroupBySelect component across the codebase.
  • Ensure that tests covering grouping functionality pass and that no logic explicitly relies on the old "person" API.
apps/web/app/[locale]/dashboard/app-url/[teamId]/page.tsx (2)

38-38: LGTM! State management is properly implemented.

The groupByType state is correctly initialized with a default value of 'date'.


49-52: LGTM! Handler properly updates state and triggers callback.

The handleGroupTypeChange function correctly manages both local state and parent callback.

apps/web/app/[locale]/dashboard/app-url/components/productivity-employee/ProductivityEmployeeTable.tsx (1)

113-123: LGTM! Proper use of React.memo for performance optimization.

The EmployeeAvatar component is correctly memoized to prevent unnecessary re-renders.

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

🧹 Nitpick comments (2)
apps/web/app/[locale]/dashboard/app-url/components/productivity-employee/ProductivityEmployeeTable.tsx (2)

21-45: Improve time calculation accuracy and parsing safety.

The utility functions have potential issues:

  1. formatDuration drops seconds, which could lead to inaccurate time reporting
  2. calculateTotalDuration uses parseInt without a radix parameter

Consider these improvements:

 const formatDuration = (seconds: number): string => {
   const hours = Math.floor(seconds / 3600);
   const minutes = Math.floor((seconds % 3600) / 60);
-  return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
+  const remainingSeconds = seconds % 60;
+  return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
 };

 const calculateTotalDuration = (activities: IActivityItem[]): number => {
   return activities.reduce((total, activity) => {
-    const duration = parseInt(activity.duration || '0');
+    const duration = parseInt(activity.duration || '0', 10);
     return isNaN(duration) ? total : total + duration;
   }, 0);
 };

246-307: Optimize component performance and memory usage.

The main component has several areas for optimization:

  1. Missing cleanup in useEffect
  2. Complex data transformation logic in a single memo

Consider these improvements:

  1. Add cleanup to useEffect:
 React.useEffect(() => {
   setLocalData(data);
+  return () => {
+    setLocalData([]);
+  };
 }, [data]);
  1. Split the data transformation logic:
const sortByDate = (data: any[]) => 
  [...data].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());

const createEmployeeGroup = (employee: IEmployee) => ({
  employee,
  dateGroups: new Map()
});

const processEmployeeActivities = (projects: any[]) => {
  const activities = projects?.flatMap((p: any) => p?.activity || []) || [];
  return {
    activities,
    totalDuration: calculateTotalDuration(activities)
  };
};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0ee249 and bb4ee5e.

📒 Files selected for processing (1)
  • apps/web/app/[locale]/dashboard/app-url/components/productivity-employee/ProductivityEmployeeTable.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: deploy
🔇 Additional comments (1)
apps/web/app/[locale]/dashboard/app-url/components/productivity-employee/ProductivityEmployeeTable.tsx (1)

68-71: Type the data prop properly to maintain type safety.

The data prop is typed as any[] which loses type safety benefits. Consider using a proper interface for the data structure.

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.

[Feature] Apps & URLs Dashboard | Group the Apps & URLs Report Data by Different Categories
1 participant