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

style(apps/analytics): organize performance analytics in tab structure #4404

Merged
merged 4 commits into from
Dec 20, 2024

Conversation

sjschlapbach
Copy link
Member

@sjschlapbach sjschlapbach commented Dec 20, 2024

Summary by CodeRabbit

  • New Features

    • Added a FormLabel to enhance the ActivitiesElementsSwitch component.
    • Introduced a ResetButton in multiple components for resetting filters.
    • Added a new ProgressLegend component for improved activity progress chart rendering.
    • Implemented a tabbed interface in the PerformanceDashboard for better navigation.
  • Bug Fixes

    • Improved layout and responsiveness in various components.
  • Documentation

    • Updated localization files for both German and English, adding new keys and modifying existing translations for clarity and consistency.

Copy link

coderabbitai bot commented Dec 20, 2024

Caution

Review failed

The pull request is closed.

📝 Walkthrough
📝 Walkthrough

Walkthrough

This pull request introduces significant enhancements to the analytics and performance components in the KlickerUZH frontend application. The changes focus on improving the user interface for performance tracking, adding new reset functionality, and refining the visualization of analytics data. Key modifications include introducing a tabbed interface for performance dashboards, creating reusable reset buttons, adding legend components, and updating localization files with new translation keys for analytics-related features.

Changes

File Change Summary
apps/frontend-manage/src/components/analytics/ActivitiesElementsSwitch.tsx Added FormLabel component, modified button layout and styling
apps/frontend-manage/src/components/analytics/performance/ActivityInstanceFeedbacksPlot.tsx Introduced ResetButton, updated header text, simplified rendering logic
apps/frontend-manage/src/components/analytics/performance/ActivityProgressPlot.tsx Added ProgressLegend component, refined progress chart rendering
apps/frontend-manage/src/components/analytics/performance/PerformanceRates.tsx Introduced ResetButton, improved layout and entry rendering
apps/frontend-manage/src/components/analytics/performance/StackedProgress.tsx Added showScale prop, enhanced chart visualization
apps/frontend-manage/src/pages/analytics/[courseId]/performance.tsx Implemented tabbed interface for performance dashboard
packages/i18n/messages/de.ts Updated German translations for analytics and performance features
packages/i18n/messages/en.ts Added new translation keys for analytics and performance sections

Possibly related PRs

Suggested reviewers

  • rschlaefli

📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 71b0e14 and 0ce95ea.

📒 Files selected for processing (1)
  • apps/frontend-manage/src/components/analytics/performance/StackedProgress.tsx (4 hunks)

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

@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: 3

🧹 Nitpick comments (12)
apps/frontend-manage/src/pages/analytics/[courseId]/performance.tsx (3)

23-29: Initialize tabValue with a safer fallback or constant.
While initializing state with "performanceRates" seems fine, consider placing the possible tab values in a constant or enum to avoid magic strings. This can help maintain a single source of truth if you add or remove tabs in the future.


86-99: Wrap style classes into a shared style or a design system token.
You’re repeatedly applying "border border-solid" and manual classes to multiple <Tabs.Tab> components. Since each tab has nearly identical classes, consider introducing a shared style or design system token to avoid duplication.


170-179: Align naming between feedback properties.
Ensure "instanceFeedbacks" and "activityFeedbacks" correspond with the new tab’s label "feedbackOverview". If there are older references to "negativeFeedbackOverview," consider updating for clarity.

apps/frontend-manage/src/components/analytics/ActivitiesElementsSwitch.tsx (1)

1-1: Improve descriptive labels for accessibility.
The newly added FormLabel is a great enhancement. Consider adding “aria-label” or describing the grouping of the two buttons for screen readers to clearly correlate the label with these elements.

Also applies to: 15-40

apps/frontend-manage/src/components/analytics/performance/ActivityInstanceFeedbacksPlot.tsx (2)

102-104: Align headings to the new translation keys.
The H2 label now references “feedbackOverviewActivityInstances.” Ensure the translation key and usage are consistent to avoid confusion for other potential headings.


Line range hint 151-166: Avoid repeated conditions for displaying charts.
Both lines 151 and 152 check for “entries.length > 0”, so you can unify the logic or rely on the parent conditional. This might reduce nesting and improve readability.

-{entries.length > 0
-  ? entries.map((feedback) => (
+entries.map((feedback) => (
    <ElementFeedbackBarChart
      ...
    />
apps/frontend-manage/src/components/analytics/performance/ActivityProgressPlot.tsx (2)

57-59: Improve header accessibility with semantic HTML

The flex layout for the header could be more semantic.

Consider using header elements:

- <div className="relative flex flex-row">
+ <header className="relative flex flex-row items-center justify-between w-full">
    <H4>{t('shared.generic.practiceQuizzes')}</H4>
    <ProgressLegend />
- </div>
+ </header>

74-86: Reduce code duplication by extracting common pattern

The Microlearnings section is identical to Practice Quizzes section except for the title.

Consider extracting a common component:

const ProgressSection = ({ 
  title, 
  progresses, 
  participants, 
  colors 
}: {
  title: string
  progresses: ActivityProgress[]
  participants: number
  colors: typeof chartColors
}) => (
  <div>
    <header className="relative flex flex-row items-center justify-between w-full">
      <H4>{title}</H4>
      <ProgressLegend colors={colors} />
    </header>
    {progresses.map((progress, idx) => (
      <StackedProgress
        key={`activity-progress-${idx}`}
        progress={progress}
        participants={participants}
        colors={colors}
        showScale={idx === progresses.length - 1}
      />
    ))}
  </div>
)
apps/frontend-manage/src/components/analytics/performance/StackedProgress.tsx (1)

63-76: Consider making reference lines more configurable

The reference lines are hardcoded at fixed intervals.

Consider making them configurable or generating them dynamically:

const REFERENCE_LINES = [25, 50, 75];
// ... in JSX
{REFERENCE_LINES.map((value) => (
  <ReferenceLine 
    key={`ref-line-${value}`}
    x={value} 
    stroke="#666" 
    strokeDasharray="3 3" 
  />
))}

This makes it easier to adjust the intervals if needed.

apps/frontend-manage/src/components/analytics/performance/PerformanceRates.tsx (3)

74-97: Consider extracting and optimizing the ResetButton component

The button implementation could be improved in several ways:

  1. Extract it outside the parent component to prevent recreation on each render
  2. Add aria-label for better accessibility
  3. Simplify the disabled state logic

Consider this implementation:

- const ResetButton = () => (
+ const ResetButton = ({
+   currentFilters,
+   defaultFilters,
+   onReset,
+ }: {
+   currentFilters: typeof defaultFilters,
+   defaultFilters: typeof defaultFilters,
+   onReset: () => void,
+ }) => (
    <Button
      className={{
        root: 'py-0.25 flex h-8 w-max flex-row items-center gap-2 self-end px-2 shadow-none',
      }}
-     disabled={
-       type === defaultFilters.type &&
-       attemptsType === defaultFilters.attemptsType &&
-       activityType === defaultFilters.activityType &&
-       elementType === defaultFilters.elementType
-     }
+     disabled={Object.entries(currentFilters).every(
+       ([key, value]) => value === defaultFilters[key]
+     )}
-     onClick={() => {
-       setType(defaultFilters.type)
-       setAttemptsType(defaultFilters.attemptsType)
-       setActivityType(defaultFilters.activityType)
-       setElementType(defaultFilters.elementType)
-       setActivitySearch('')
-       setInstanceSearch('')
-     }}
+     onClick={onReset}
+     aria-label={t('manage.analytics.resetSelectors')}
    >
      <FontAwesomeIcon icon={faX} />
      <div>{t('manage.analytics.resetSelectors')}</div>
    </Button>
  )

102-104: Consider mobile-first responsive design

The current layout might not provide optimal spacing on smaller screens. Consider adding responsive classes for the gap and flex direction.

- <div className="flex w-full flex-row justify-between gap-8">
+ <div className="flex w-full flex-col gap-4 sm:flex-row sm:justify-between sm:gap-8">

Line range hint 108-141: Consider extracting filter section layout to reduce duplication

The activity and instance filter sections share similar layout structure. Consider extracting the common layout into a reusable component.

Example implementation:

const FilterSection = ({ children }: { children: React.ReactNode }) => (
  <div className="mb-3 flex flex-col gap-1 lg:flex-row lg:gap-8">
    {children}
  </div>
)

Usage:

- <div className="mb-3 flex flex-col gap-1 lg:flex-row lg:gap-8">
+ <FilterSection>
    <ActivitiesElementsSwitch type={type} setType={setType} />
    {/* other filters */}
- </div>
+ </FilterSection>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c5f02ce and 71b0e14.

📒 Files selected for processing (8)
  • apps/frontend-manage/src/components/analytics/ActivitiesElementsSwitch.tsx (2 hunks)
  • apps/frontend-manage/src/components/analytics/performance/ActivityInstanceFeedbacksPlot.tsx (4 hunks)
  • apps/frontend-manage/src/components/analytics/performance/ActivityProgressPlot.tsx (1 hunks)
  • apps/frontend-manage/src/components/analytics/performance/PerformanceRates.tsx (4 hunks)
  • apps/frontend-manage/src/components/analytics/performance/StackedProgress.tsx (3 hunks)
  • apps/frontend-manage/src/pages/analytics/[courseId]/performance.tsx (3 hunks)
  • packages/i18n/messages/de.ts (1 hunks)
  • packages/i18n/messages/en.ts (1 hunks)
🔇 Additional comments (11)
apps/frontend-manage/src/pages/analytics/[courseId]/performance.tsx (5)

3-3: Use imports consistently and purge unused ones if necessary.
It’s good to see you’re using "Tabs" from your design system. If there are any unused imports, consider removing them to keep the file tidy.

Also applies to: 7-7


140-149: Ensure performance rates data is not null.
Double-check that “activityPerformances” and “instancePerformances” from “course” data exist. If they can be null or undefined, adding safe-guards or optional chaining is recommended.


150-159: Maintain consistent naming for activityProgress metrics.
The variable "activityProgresses" and param "participants" look consistent. Just confirm that the naming matches the rest of the analytics code in other files for uniformity.


160-169: Guard participantPerformances to avoid runtime errors.
If "participantPerformances" can be undefined, ensure it’s always defined or fallback to an empty list. This helps avoid potential runtime exceptions.


72-85: Check for missing defaultValue alignment.
You’re providing both "defaultValue" and a separate "value" prop to the Tabs component. Make sure you want the "defaultValue" to remain "performanceRates" and that it doesn’t conflict with the "tabValue" controlled state.

packages/i18n/messages/de.ts (1)

1891-1899: Keep keys consistent with newly introduced tabs.
You’ve added new translation keys (e.g., "performanceRates", "activityProgress", etc.). Verify that each key matches the actual tab values in your components to avoid mismatch issues.

apps/frontend-manage/src/components/analytics/performance/ActivityInstanceFeedbacksPlot.tsx (2)

75-99: Validate filter defaults thoroughly.
The reset button logic is straightforward. Just confirm that default filters match the expected initial state for "activitySearchTerm" and "instanceSearchTerm," especially if future features change them.


108-108: Double-check type usage for ActivitiesElementsSwitch.
Switching between "activity" and "instance" is essential. Validate that all references to "type" in the filters are updated properly. If any old references remain, it could break the filters.

Also applies to: 121-121

packages/i18n/messages/en.ts (1)

1877-1885: LGTM! The new analytics translation keys are well-structured and descriptive.

The added translation keys follow the established naming conventions and provide clear, concise labels for the analytics dashboard tabs. The translations effectively support the PR's objective of organizing performance analytics in a tab structure.

apps/frontend-manage/src/components/analytics/performance/StackedProgress.tsx (1)

49-61: LGTM! Clean implementation of conditional styling

Good use of twMerge for handling conditional classes and proper height adjustments based on the showScale prop.

apps/frontend-manage/src/components/analytics/performance/PerformanceRates.tsx (1)

85-92: Verify filter reset behavior

Let's verify that all filter states are properly reset and there are no memory leaks from state updates.

✅ Verification successful

Let me analyze the code further to verify the filter reset behavior.


Filter reset behavior is properly implemented

The code shows that:

  1. All filter states are initialized with values from defaultFilters object
  2. Reset logic properly resets all states to their default values:
    • Type filters (type, attemptsType, activityType, elementType) are reset to their respective default values
    • Search inputs (activitySearch, instanceSearch) are cleared to empty strings
  3. States are independent and localized to the component with no shared state dependencies
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other components that might be affected by the filter reset
# Look for shared state management or filter dependencies

# Search for filter-related state management
ast-grep --pattern 'useState<$_>($_)'

# Search for filter reset implementations
rg -A 2 'setType|setAttemptsType|setActivityType|setElementType'

Length of output: 119101


Script:

#!/bin/bash
# Search for filter state dependencies and reset patterns
rg -A 2 "defaultFilters" apps/frontend-manage/src/components/analytics/performance/PerformanceRates.tsx

# Search for any shared state management
ast-grep --pattern 'const [$_] = useState<$_>($_)'

Length of output: 1263


function StackedProgress({
progress,
participants,
colors,
showScale = false,
Copy link

@coderabbitai coderabbitai bot Dec 20, 2024

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove duplicate type definition for showScale prop

The showScale prop type is defined twice in the component props.

Remove the duplicate definition:

function StackedProgress({
  progress,
  participants,
  colors,
  showScale = false,
}: {
  progress: ActivityProgress
  participants: number
  colors: {
    started: string
    completed: string
    repeated: string
  }
- showScale?: boolean
}) {

Also applies to: 27-27

Copy link
Member Author

Choose a reason for hiding this comment

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

this is wrong, please do not suggest wrong things

Copy link

Choose a reason for hiding this comment

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

@sjschlapbach, understood. We'll disregard that suggestion. Let me know if there's anything else you'd like me to do.

@sjschlapbach sjschlapbach merged commit 09b14b1 into v3-analytics Dec 20, 2024
7 of 10 checks passed
@sjschlapbach sjschlapbach deleted the tabs-performance-analytics branch December 20, 2024 14:06
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
6.9% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

1 participant