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

Website: Dedicated page for all survey answers #928

Merged
merged 5 commits into from
Dec 28, 2024

Conversation

ssandino
Copy link
Member

@ssandino ssandino commented Oct 24, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Added survey response visualization with bar charts.
    • Enhanced survey data processing and statistics calculation.
    • Introduced comprehensive survey question types and localization support.
  • Localization

    • Updated survey localization files for English, Italian, and Krio languages.
    • Added new translation options for yes/no questions.
  • UI Improvements

    • New accent variant for badge component.
    • Improved survey response page layout and data presentation.
  • Technical Enhancements

    • Implemented survey stats calculator for data aggregation.
    • Standardized survey question mapping and translation functions.

Copy link

vercel bot commented Oct 24, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
public ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 28, 2024 4:09pm

@ssandino ssandino linked an issue Oct 24, 2024 that may be closed by this pull request
4 tasks
@ssandino
Copy link
Member Author

ssandino commented Oct 27, 2024

Copy link

github-actions bot commented Oct 31, 2024

Visit the preview URL for this PR (updated for commit d94d428):

https://si-admin-staging--pr928-gavrill-raw-survey-a-ryohjgr2.web.app

(expires Sat, 04 Jan 2025 14:52:40 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: b7b0969384059dce6ea8fad1ee1d1737e54e6676

Copy link

coderabbitai bot commented Nov 5, 2024

📝 Walkthrough

Walkthrough

This pull request introduces significant enhancements to survey-related functionality across various files. It includes a new localization JSON for survey responses, a SurveyStatsCalculator class for processing and aggregating survey data, an updated survey responses page featuring bar chart visualizations, and modifications to survey question types and localization files. The changes collectively aim to improve the handling, visualization, and internationalization of survey data.

Changes

File Change Summary
shared/locales/en/website-responses.json New JSON file with localized survey response strings.
shared/src/utils/stats/SurveyStatsCalculator.ts New class for processing and aggregating survey statistics, with added interfaces and methods.
website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx Updated to display survey responses with bar charts and enhanced data processing.
shared/locales/*/website-survey.json Added "true" and "false" localization keys for yes/no questions.
shared/src/types/question.ts Added comprehensive question type definitions and constants.
website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts Refactored question mapping and translation functions for better maintainability.
ui/src/components/badge.tsx Added new accent badge variant for styling options.
website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx New component for rendering bar charts of survey responses.
shared/src/utils/stats/SurveyStatsCalculator.test.ts New test file for SurveyStatsCalculator, covering initialization and functionality.
website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questionnaires.ts Updated function signatures to ensure proper execution of questionnaire functions.

Possibly related PRs

Suggested labels

feature

Suggested reviewers

  • andrashee
  • socialincome-dev
  • mkue

📜 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 d94d428 and e0421f6.

📒 Files selected for processing (1)
  • website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx

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.

@ssandino
Copy link
Member Author

@coderabbitai review

Copy link

coderabbitai bot commented Nov 24, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🧹 Outside diff range and nitpick comments (6)
website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx (2)

20-28: Improve variable naming and data transformation clarity.

The variable name 'temp' is not descriptive of its purpose. Consider renaming it to something more meaningful like 'surveyStats' or 'rawSurveyData'.

-const temp = surveyStatsCalculator.data;
-const allSurveyData = Object.values(SurveyQuestionnaire).map((it) => temp.find((survey) => survey.type == it));
+const rawSurveyData = surveyStatsCalculator.data;
+const allSurveyData = Object.values(SurveyQuestionnaire).map((surveyType) => 
+  rawSurveyData.find((survey) => survey.type === surveyType)
+);

59-60: Simplify Fragment key generation.

The key concatenation can be simplified using template literals.

-<Fragment key={selectedSurvey + key + 'statistics'}>
+<Fragment key={`${selectedSurvey}-${key}-statistics`}>
shared/src/utils/stats/SurveyStatsCalculator.ts (4)

37-40: Simplify 'return await' to 'return' in async functions

The return await inside an async function is redundant. Simplify it to return to improve code clarity.

Apply this diff:

-return await firestoreAdmin
+return firestoreAdmin

42-42: Use 'const' instead of 'let' for variables that are not reassigned

The variable ignored is not reassigned after its initial assignment. Replace let with const to indicate that the value doesn't change.

Apply this diff:

-let ignored = ['pageNo'];
+const ignored = ['pageNo'];

43-43: Use 'const' instead of 'let' for 'surveysData'

Since surveysData is not reassigned, use const to enhance code readability and maintainability.

Apply this diff:

-let surveysData = documents.flatMap((snapshot) => snapshot.docs).map((survey) => survey.data());
+const surveysData = documents.flatMap((snapshot) => snapshot.docs).map((survey) => survey.data());

28-30: Complete the JSDoc comment for the 'build' method

The JSDoc comment for the build method is incomplete. Provide a description of the method and its parameters to improve code documentation.

Apply this diff:

/**
+ * Builds a SurveyStatsCalculator instance by aggregating survey statistics from Firestore.
 * @param firestoreAdmin FirestoreAdmin instance for database operations.
 */
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c00fdf2 and c946bcd.

📒 Files selected for processing (4)
  • shared/locales/en/website-responses.json (1 hunks)
  • shared/src/utils/stats/SurveyStatsCalculator.ts (1 hunks)
  • ui/src/components/tabs.tsx (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • shared/locales/en/website-responses.json
🔇 Additional comments (3)
ui/src/components/tabs.tsx (2)

11-11: Implementation is consistent and clean

The simplified className handling is consistently applied across all tab components while maintaining proper type safety and ref forwarding.

Also applies to: 17-17, 23-23


11-11: Verify className handling in consuming components

The removal of the cn utility might affect components that rely on className merging functionality.

Also applies to: 17-17, 23-23

✅ Verification successful

No issues with className handling in consuming components

The search results show that the consuming components in website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx are using single className values for TabsList, TabsTrigger, and TabsContent components. The className values are being passed directly without any merging functionality, so the removal of the cn utility does not affect these implementations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for components using these tab components with multiple className values
rg -l "TabsList|TabsTrigger|TabsContent" | xargs rg "className=.*\s+.*"

Length of output: 2885

website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx (1)

1-19: LGTM! Imports and configuration are well-organized.

The imports are logically grouped and the revalidation time of 1 hour is appropriate for survey data that doesn't change frequently.

shared/src/utils/stats/SurveyStatsCalculator.ts Outdated Show resolved Hide resolved
shared/src/utils/stats/SurveyStatsCalculator.ts Outdated Show resolved Hide resolved
shared/src/utils/stats/SurveyStatsCalculator.ts Outdated Show resolved Hide resolved
@Gavriil-Tzortzakis Gavriil-Tzortzakis force-pushed the gavrill/raw-survey-aggregation branch 2 times, most recently from 5c97059 to 7c7d6ee Compare November 30, 2024 19:58
@ssandino ssandino changed the title Website: new page for survey answers Website: Dedicated page for all survey answers Nov 30, 2024
@Gavriil-Tzortzakis Gavriil-Tzortzakis force-pushed the gavrill/raw-survey-aggregation branch from 7c7d6ee to fcb398c Compare December 1, 2024 21:31
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: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f364539 and 7608cd5.

📒 Files selected for processing (11)
  • shared/locales/en/website-responses.json (1 hunks)
  • shared/locales/en/website-survey.json (1 hunks)
  • shared/locales/it/website-survey.json (1 hunks)
  • shared/locales/kri/website-survey.json (1 hunks)
  • shared/src/types/question.ts (1 hunks)
  • shared/src/utils/stats/SurveyStatsCalculator.test.ts (1 hunks)
  • shared/src/utils/stats/SurveyStatsCalculator.ts (1 hunks)
  • ui/src/components/tabs.tsx (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx (1 hunks)
  • website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts (21 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • shared/locales/en/website-responses.json
  • shared/locales/en/website-survey.json
  • ui/src/components/tabs.tsx
  • website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx
  • shared/locales/it/website-survey.json
  • shared/locales/kri/website-survey.json
  • shared/src/utils/stats/SurveyStatsCalculator.test.ts
  • website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
shared/src/utils/stats/SurveyStatsCalculator.ts

[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 41-41: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 72-72: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 80-80: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts

[error] 67-67: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 60-60: Don't use 'Object' as a type.

Prefer explicitly define the object shape. This type means "any non-nullable value", which is slightly better than 'unknown', but it's still a broad type.

(lint/complexity/noBannedTypes)


[error] 350-350: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)

🪛 Gitleaks (8.21.2)
shared/src/types/question.ts

47-47: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


235-235: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (5)
shared/src/utils/stats/SurveyStatsCalculator.ts (1)

40-41: 🛠️ Refactor suggestion

Replace 'this' with class name in static methods

Using this in static methods can be confusing. Use the class name SurveyStatsCalculator instead.

-const surveysData = await this.fetchAndProcessSurveys(firestoreAdmin, recipients);
-const { typeCounts, aggregatedData, oldestDate } = this.aggregateSurveyData(surveysData);
+const surveysData = await SurveyStatsCalculator.fetchAndProcessSurveys(firestoreAdmin, recipients);
+const { typeCounts, aggregatedData, oldestDate } = SurveyStatsCalculator.aggregateSurveyData(surveysData);

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 41-41: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts (2)

350-354: 🛠️ Refactor suggestion

Improve type safety in translateChoices function

Replace any[] and String with more specific types.

-export const translateChoices = (t: TranslateFunction, choices: any[], choicesTranslationKey: String) =>
+export const translateChoices = (t: TranslateFunction, choices: string[], choicesTranslationKey: string): Array<{ value: string; text: string }> =>
   choices.map((key) => ({
     value: key,
     text: t(choicesTranslationKey + '.' + key),
   }));

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 350-350: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)


60-71: 🛠️ Refactor suggestion

Improve type safety and optional chaining

Two improvements needed:

  1. Replace generic Object return type with a specific interface
  2. Use consistent optional chaining
-function getSimpleMapping(question: Question, t: TranslateFunction): Object {
+interface QuestionMapping {
+  type: string;
+  name: string;
+  title: string;
+  description?: string;
+  choices?: Array<{ value: string; text: string }>;
+}
+
+function getSimpleMapping(question: Question, t: TranslateFunction): QuestionMapping {
   return {
     type: question.type,
     name: question.name,
     title: t(question.translationKey),
-    description: question.descriptionTranslationKey && t(question.descriptionTranslationKey),
+    description: question.descriptionTranslationKey?.t(question.descriptionTranslationKey),
     choices:
-      question.choices && question.choices.length
+      question.choices?.length
         ? translateChoices(t, question.choices, question.choicesTranslationKey!)
         : undefined,
   };
 }

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 67-67: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 60-60: Don't use 'Object' as a type.

Prefer explicitly define the object shape. This type means "any non-nullable value", which is slightly better than 'unknown', but it's still a broad type.

(lint/complexity/noBannedTypes)

shared/src/types/question.ts (2)

271-271: LGTM!

The Map implementation with string keys is correct and type-safe.


1-8: 🛠️ Refactor suggestion

Improve type safety in Question interface

The choices property using any[] reduces type safety.

 export interface Question {
   type: QuestionInputType;
   name: string;
-  choices: any[];
+  choices: (string | boolean | number)[];
   choicesTranslationKey?: string;
   translationKey: string;
   descriptionTranslationKey?: string;
 }

Likely invalid or redundant comment.

@ssandino
Copy link
Member Author

When I run your local branch and visit http://localhost:3001/en/ch/survey/responses, I get the following error:

Screenshot 2024-12-10 at 23 35 11

@Gavriil-Tzortzakis any hints, how to get it running locally? Or does it work on yours @mkue ?

@Gavriil-Tzortzakis Gavriil-Tzortzakis force-pushed the gavrill/raw-survey-aggregation branch from 7608cd5 to d9e7472 Compare December 20, 2024 19:13
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: 4

🧹 Nitpick comments (6)
website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx (3)

10-10: Consider modernizing the component implementation

Consider these improvements:

  1. Convert to a functional component using React hooks
  2. Add prop validation using PropTypes or TypeScript's required modifier
-export default class BarchartSurveyResponseComponent extends Component<{ data: ChartData[] }> {
+interface BarchartSurveyResponseProps {
+  data: ChartData[];
+}
+
+export default function BarchartSurveyResponseComponent({ data }: BarchartSurveyResponseProps) {

16-16: Add fallback color for CSS variable

Provide a fallback color in case the CSS variable is not defined.

-let fillColor = 'hsl(var(--foreground))';
+let fillColor = 'hsl(var(--foreground, 0 0% 0%))';

36-59: Consider dynamic margins for better responsiveness

The fixed right margin of 100px might not be optimal for all screen sizes. Consider making it responsive.

-margin={{ top: 20, right: 100, bottom: 20, left: 20 }}
+margin={{ 
+  top: 20,
+  right: Math.max(50, Math.min(window.innerWidth * 0.1, 100)),
+  bottom: 20,
+  left: 20 
+}}
shared/src/utils/stats/SurveyStatsCalculator.ts (2)

96-96: Improve type safety for response parameter

The response parameter is typed as any. Consider creating a union type for supported response types.

-response: any,
+response: string | string[],

50-59: Consider batch processing for large datasets

Fetching all surveys at once could lead to performance issues with large datasets. Consider implementing pagination or batch processing.

website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts (1)

67-69: Use optional chaining for better null safety

The condition can be simplified using optional chaining.

-question.choices && question.choices.length
+question.choices?.length
🧰 Tools
🪛 Biome (1.9.4)

[error] 67-67: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7608cd5 and d9e7472.

📒 Files selected for processing (13)
  • shared/locales/en/website-responses.json (1 hunks)
  • shared/locales/en/website-survey.json (1 hunks)
  • shared/locales/it/website-survey.json (1 hunks)
  • shared/locales/kri/website-survey.json (1 hunks)
  • shared/src/types/question.ts (1 hunks)
  • shared/src/utils/stats/SurveyStatsCalculator.test.ts (1 hunks)
  • shared/src/utils/stats/SurveyStatsCalculator.ts (1 hunks)
  • ui/src/components/badge.tsx (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/opacity.css (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx (1 hunks)
  • website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questionnaires.ts (4 hunks)
  • website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts (21 hunks)
✅ Files skipped from review due to trivial changes (1)
  • website/src/app/[lang]/[region]/(website)/survey/responses/opacity.css
🚧 Files skipped from review as they are similar to previous changes (6)
  • shared/locales/en/website-survey.json
  • shared/locales/en/website-responses.json
  • shared/locales/it/website-survey.json
  • shared/locales/kri/website-survey.json
  • shared/src/utils/stats/SurveyStatsCalculator.test.ts
  • website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
shared/src/utils/stats/SurveyStatsCalculator.ts

[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 41-41: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 72-72: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 80-80: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts

[error] 67-67: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 60-60: Don't use 'Object' as a type.

Prefer explicitly define the object shape. This type means "any non-nullable value", which is slightly better than 'unknown', but it's still a broad type.

(lint/complexity/noBannedTypes)


[error] 350-350: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)

🪛 Gitleaks (8.21.2)
shared/src/types/question.ts

47-47: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


235-235: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (8)
ui/src/components/badge.tsx (1)

15-15: LGTM! New accent variant follows component patterns.

The implementation is clean and consistent with existing variants. Consider verifying the visual appearance across different color modes (light/dark) to ensure proper contrast ratios.

✅ Verification successful

New accent variant is properly implemented and used across the codebase

The accent variant is well-integrated with:

  • Proper CSS custom properties defined in ui/src/globals.css
  • Consistent usage in multiple components including login and survey pages
  • Correct theme configuration in ui/tailwind.config.ts
  • Matching hover and focus states across components
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for usage of the new accent variant and related styling
# to ensure proper implementation across the codebase

# Check for usage of the accent variant
rg -l "variant=\"accent\"" --type tsx

# Check for accent color definitions in theme
rg "accent" --type css --type scss

Length of output: 129


Script:

#!/bin/bash
# Let's try without file type restrictions since the extensions might be different
# Check for usage of the accent variant
rg -l "variant=\"accent\""

# Check for accent-related styling in any file
rg "accent.*foreground|accent.*background|accent:.*bg-"

# Find all potential theme/style files
fd "theme|style|tailwind"

Length of output: 5323

website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx (1)

4-4: Verify the opacity.css dependency

The imported CSS file's contents should be reviewed to ensure it contains the necessary styles.

✅ Verification successful

CSS file contains valid gradient opacity styles for recharts

The opacity.css file exists and contains a valid CSS class that applies a gradient opacity effect to recharts components using Tailwind's @apply directive. The styles are properly scoped to .recharts-wrapper and implement a transparent-to-background gradient on the right side.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the opacity.css file exists and view its contents
cat website/src/app/[lang]/[region]/\(website\)/survey/responses/opacity.css

Length of output: 239

website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questionnaires.ts (1)

47-47: LGTM: Translation function properly passed to page components

The changes consistently pass the translation function to schoolAttendancePage and skippingMealsPage across all questionnaire types.

Also applies to: 50-50, 86-86, 89-89, 101-101, 104-104

website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts (2)

350-354: 🛠️ Refactor suggestion

Improve type safety in translateChoices

The function uses any[] and String types, which can be more specific.

-export const translateChoices = (t: TranslateFunction, choices: any[], choicesTranslationKey: String) =>
+export const translateChoices = (t: TranslateFunction, choices: string[], choicesTranslationKey: string): Array<{ value: string; text: string }> =>

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 350-350: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)


60-71: 🛠️ Refactor suggestion

Improve type safety in getSimpleMapping

The function uses Object as return type, which is too broad.

-function getSimpleMapping(question: Question, t: TranslateFunction): Object {
+interface QuestionMapping {
+  type: string;
+  name: string;
+  title: string;
+  description?: string;
+  choices?: Array<{ value: string; text: string }>;
+}
+
+function getSimpleMapping(question: Question, t: TranslateFunction): QuestionMapping {

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 67-67: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 60-60: Don't use 'Object' as a type.

Prefer explicitly define the object shape. This type means "any non-nullable value", which is slightly better than 'unknown', but it's still a broad type.

(lint/complexity/noBannedTypes)

shared/src/types/question.ts (3)

1-8: Improve type safety for Question interface

The choices property using any[] reduces type safety.

 export interface Question {
 	type: QuestionInputType;
 	name: string;
-	choices: any[];
+	choices: (string | boolean | number)[];
 	choicesTranslationKey?: string;
 	translationKey: string;
 	descriptionTranslationKey?: string;
 }

10-15: LGTM!

The enum values are well-defined and consistent.


238-271: LGTM!

The questions array and dictionary implementation provides efficient question lookup by name.

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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d9e7472 and 1de0447.

📒 Files selected for processing (13)
  • shared/locales/en/website-responses.json (1 hunks)
  • shared/locales/en/website-survey.json (1 hunks)
  • shared/locales/it/website-survey.json (1 hunks)
  • shared/locales/kri/website-survey.json (1 hunks)
  • shared/src/types/question.ts (1 hunks)
  • shared/src/utils/stats/SurveyStatsCalculator.test.ts (1 hunks)
  • shared/src/utils/stats/SurveyStatsCalculator.ts (1 hunks)
  • ui/src/components/badge.tsx (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/opacity.css (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx (1 hunks)
  • website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questionnaires.ts (4 hunks)
  • website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts (21 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • ui/src/components/badge.tsx
  • website/src/app/[lang]/[region]/(website)/survey/responses/opacity.css
  • shared/locales/en/website-responses.json
  • shared/locales/en/website-survey.json
  • shared/locales/it/website-survey.json
  • shared/locales/kri/website-survey.json
  • shared/src/utils/stats/SurveyStatsCalculator.test.ts
  • website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx
  • website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx
  • website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questionnaires.ts
🧰 Additional context used
🪛 Biome (1.9.4)
website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts

[error] 67-67: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 60-60: Don't use 'Object' as a type.

Prefer explicitly define the object shape. This type means "any non-nullable value", which is slightly better than 'unknown', but it's still a broad type.

(lint/complexity/noBannedTypes)


[error] 350-350: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)

shared/src/utils/stats/SurveyStatsCalculator.ts

[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 41-41: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 72-72: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 80-80: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🪛 Gitleaks (8.21.2)
shared/src/types/question.ts

47-47: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


235-235: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (6)
website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts (2)

350-354: Improve type safety in translateChoices

Previous review comment still applies. The function needs proper TypeScript types for parameters and return value.

🧰 Tools
🪛 Biome (1.9.4)

[error] 350-350: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)


Line range hint 77-343: LGTM! Clean and consistent implementation

The page components consistently use getSimpleMapping, making the code more maintainable and reducing duplication.

shared/src/utils/stats/SurveyStatsCalculator.ts (2)

40-41: 🛠️ Refactor suggestion

Replace 'this' with class name in static methods

Using this in static methods can be confusing. Use the class name SurveyStatsCalculator instead.

-const surveysData = await this.fetchAndProcessSurveys(firestoreAdmin, recipients);
-const { typeCounts, aggregatedData, oldestDate } = this.aggregateSurveyData(surveysData);
+const surveysData = await SurveyStatsCalculator.fetchAndProcessSurveys(firestoreAdmin, recipients);
+const { typeCounts, aggregatedData, oldestDate } = SurveyStatsCalculator.aggregateSurveyData(surveysData);

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 41-41: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


96-97: 🛠️ Refactor suggestion

Improve type safety for response parameter

Using any reduces type safety. Consider using a union type based on the supported question types.

-response: any,
+response: string | string[],

Likely invalid or redundant comment.

shared/src/types/question.ts (2)

1-8: 🛠️ Refactor suggestion

Improve type safety for Question interface

The choices property using any[] reduces type safety. Consider using a more specific type.

export interface Question {
	type: QuestionInputType;
	name: string;
-	choices: any[];
+	choices: (string | boolean | number)[];
	choicesTranslationKey?: string;
	translationKey: string;
	descriptionTranslationKey?: string;
}

Likely invalid or redundant comment.


230-236: ⚠️ Potential issue

Add choices for RANKING question type

The RANKING question type requires choices to be ranked, but the choices array is empty. Consider reusing the choices from the SPENDING question since they share the same context.

export const RANKING: Question = {
	type: QuestionInputType.RANKING,
	name: 'spendingRankedV1',
-	choices: [],
+	choices: ['education', 'food', 'housing', 'healthCare', 'mobility', 'saving', 'investment'],
	translationKey: 'survey.questions.spendingRankingTitleV1',
	descriptionTranslationKey: 'survey.questions.spendingRankingDescV1',
};

Likely invalid or redundant comment.

🧰 Tools
🪛 Gitleaks (8.21.2)

235-235: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +60 to +71
function getSimpleMapping(question: Question, t: TranslateFunction): Object {
return {
type: question.type,
name: question.name,
title: t(question.translationKey),
description: question.descriptionTranslationKey && t(question.descriptionTranslationKey),
choices:
question.choices && question.choices.length
? translateChoices(t, question.choices, question.choicesTranslationKey!)
: undefined,
};
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve type safety and null handling

Three improvements needed:

  1. Define specific return type
  2. Improve optional chaining
  3. Handle potential null choicesTranslationKey
-function getSimpleMapping(question: Question, t: TranslateFunction): Object {
+interface QuestionMapping {
+  type: string;
+  name: string;
+  title: string;
+  description?: string;
+  choices?: Array<{ value: string; text: string }>;
+}
+
+function getSimpleMapping(question: Question, t: TranslateFunction): QuestionMapping {
   return {
     type: question.type,
     name: question.name,
     title: t(question.translationKey),
-    description: question.descriptionTranslationKey && t(question.descriptionTranslationKey),
+    description: question.descriptionTranslationKey ? t(question.descriptionTranslationKey) : undefined,
     choices:
-      question.choices && question.choices.length
+      question.choices?.length
         ? translateChoices(t, question.choices, question.choicesTranslationKey!)
         : undefined,
   };
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function getSimpleMapping(question: Question, t: TranslateFunction): Object {
return {
type: question.type,
name: question.name,
title: t(question.translationKey),
description: question.descriptionTranslationKey && t(question.descriptionTranslationKey),
choices:
question.choices && question.choices.length
? translateChoices(t, question.choices, question.choicesTranslationKey!)
: undefined,
};
}
interface QuestionMapping {
type: string;
name: string;
title: string;
description?: string;
choices?: Array<{ value: string; text: string }>;
}
function getSimpleMapping(question: Question, t: TranslateFunction): QuestionMapping {
return {
type: question.type,
name: question.name,
title: t(question.translationKey),
description: question.descriptionTranslationKey ? t(question.descriptionTranslationKey) : undefined,
choices:
question.choices?.length
? translateChoices(t, question.choices, question.choicesTranslationKey!)
: undefined,
};
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 67-67: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 60-60: Don't use 'Object' as a type.

Prefer explicitly define the object shape. This type means "any non-nullable value", which is slightly better than 'unknown', but it's still a broad type.

(lint/complexity/noBannedTypes)

@Gavriil-Tzortzakis Gavriil-Tzortzakis force-pushed the gavrill/raw-survey-aggregation branch from 1de0447 to bf8d50c Compare December 22, 2024 08:43
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: 2

🧹 Nitpick comments (3)
shared/src/types/question.ts (1)

1-271: Consider grouping related questions and documenting versioning strategy

  1. Related questions (e.g., HAPPIER, HAPPIER_COMMENT) could be grouped into objects or namespaces for better organization.
  2. The 'V1' suffix suggests versioning plans. Consider documenting the versioning strategy for future maintainers.
🧰 Tools
🪛 Gitleaks (8.21.2)

47-47: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


235-235: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

shared/src/utils/stats/SurveyStatsCalculator.ts (1)

73-76: Simplify date comparison logic

The current implementation can be simplified using optional chaining and a more concise comparison.

-oldestDate =
-  survey.completed_at?.toDate() && survey.completed_at?.toDate() < oldestDate
-    ? survey.completed_at.toDate()
-    : oldestDate;
+const completedDate = survey.completed_at?.toDate();
+if (completedDate && completedDate < oldestDate) {
+  oldestDate = completedDate;
+}
website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts (1)

354-354: Use template literal for translation key

Replace string concatenation with template literal for better readability.

-text: t(choicesTranslationKey + '.' + key),
+text: t(`${choicesTranslationKey}.${key}`),
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1de0447 and bf8d50c.

📒 Files selected for processing (14)
  • shared/locales/en/website-responses.json (1 hunks)
  • shared/locales/en/website-survey.json (1 hunks)
  • shared/locales/it/website-survey.json (1 hunks)
  • shared/locales/kri/website-survey.json (1 hunks)
  • shared/src/types/question.ts (1 hunks)
  • shared/src/utils/stats/SurveyStatsCalculator.test.ts (1 hunks)
  • shared/src/utils/stats/SurveyStatsCalculator.ts (1 hunks)
  • ui/src/components/badge.tsx (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/card-tab.css (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/opacity.css (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx (1 hunks)
  • website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questionnaires.ts (4 hunks)
  • website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts (21 hunks)
✅ Files skipped from review due to trivial changes (1)
  • website/src/app/[lang]/[region]/(website)/survey/responses/card-tab.css
🚧 Files skipped from review as they are similar to previous changes (10)
  • website/src/app/[lang]/[region]/(website)/survey/responses/opacity.css
  • ui/src/components/badge.tsx
  • shared/locales/en/website-survey.json
  • shared/locales/en/website-responses.json
  • shared/src/utils/stats/SurveyStatsCalculator.test.ts
  • shared/locales/it/website-survey.json
  • shared/locales/kri/website-survey.json
  • website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx
  • website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx
  • website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questionnaires.ts
🧰 Additional context used
🪛 Biome (1.9.4)
website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts

[error] 67-67: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 60-60: Don't use 'Object' as a type.

Prefer explicitly define the object shape. This type means "any non-nullable value", which is slightly better than 'unknown', but it's still a broad type.

(lint/complexity/noBannedTypes)


[error] 350-350: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)

shared/src/utils/stats/SurveyStatsCalculator.ts

[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 41-41: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 72-72: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 80-80: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🪛 Gitleaks (8.21.2)
shared/src/types/question.ts

47-47: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


235-235: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (8)
shared/src/types/question.ts (4)

1-8: Improve type safety for Question interface

The choices property using any[] reduces type safety. Consider using a more specific type.


10-15: LGTM!

The enum is well-defined with clear, descriptive values.


230-236: Add choices for RANKING question type

The RANKING question type requires choices to be ranked, but the choices array is empty.

🧰 Tools
🪛 Gitleaks (8.21.2)

235-235: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


271-271: LGTM!

The dictionary implementation is correct and uses proper TypeScript types.

shared/src/utils/stats/SurveyStatsCalculator.ts (2)

4-4: ⚠️ Potential issue

Fix typo in constant name

SURVEY_FIRETORE_PATH should be SURVEY_FIRESTORE_PATH.

-import { Survey, SURVEY_FIRETORE_PATH, SurveyQuestionnaire, SurveyStatus } from '../../types/survey';
+import { Survey, SURVEY_FIRESTORE_PATH, SurveyQuestionnaire, SurveyStatus } from '../../types/survey';

Likely invalid or redundant comment.


92-97: 🛠️ Refactor suggestion

Improve type safety for survey response

The response parameter should have a more specific type than any.

 private static processSurveyResponse(
   aggregatedData: { [key: string]: { [key: string]: SurveyAnswersByType } },
   questionnaire: string,
   questionKey: string,
-  response: any,
+  response: string | string[],
 ): void {

Likely invalid or redundant comment.

website/src/app/[lang]/[region]/survey/[recipient]/[survey]/questions.ts (2)

350-354: 🛠️ Refactor suggestion

Improve type safety in translateChoices

The function uses any[] and uppercase String type.

-export const translateChoices = (t: TranslateFunction, choices: any[], choicesTranslationKey: String) =>
+export const translateChoices = (t: TranslateFunction, choices: string[], choicesTranslationKey: string): Array<{ value: string; text: string }> =>
   choices.map((key) => ({
     value: key,
     text: t(choicesTranslationKey + '.' + key),
   }));

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 350-350: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)


60-71: 🛠️ Refactor suggestion

Improve type safety and null handling in getSimpleMapping

The function needs proper return type definition and better null handling.

+interface QuestionMapping {
+  type: string;
+  name: string;
+  title: string;
+  description?: string;
+  choices?: Array<{ value: string; text: string }>;
+}

-function getSimpleMapping(question: Question, t: TranslateFunction): Object {
+function getSimpleMapping(question: Question, t: TranslateFunction): QuestionMapping {
   return {
     type: question.type,
     name: question.name,
     title: t(question.translationKey),
-    description: question.descriptionTranslationKey && t(question.descriptionTranslationKey),
+    description: question.descriptionTranslationKey ? t(question.descriptionTranslationKey) : undefined,
     choices:
-      question.choices && question.choices.length
+      question.choices?.length
         ? translateChoices(t, question.choices, question.choicesTranslationKey!)
         : undefined,
   };
 }

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 67-67: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 60-60: Don't use 'Object' as a type.

Prefer explicitly define the object shape. This type means "any non-nullable value", which is slightly better than 'unknown', but it's still a broad type.

(lint/complexity/noBannedTypes)

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

🧹 Nitpick comments (2)
website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx (1)

9-14: Use const for fillColor

The fillColor variable never gets reassigned.

-let fillColor = 'hsl(var(--foreground))';
+const fillColor = 'hsl(var(--foreground))';
shared/src/utils/stats/SurveyStatsCalculator.ts (1)

124-130: Add type guards for response handling

Consider adding type guards instead of type casting for better type safety.

-if (question.type === QuestionInputType.CHECKBOX) {
-  (response as string[]).forEach((value) => {
+if (question.type === QuestionInputType.CHECKBOX && Array.isArray(response)) {
+  response.forEach((value: string) => {
     questionData.answers[value] = (questionData.answers[value] || 0) + 1;
   });
-} else if (question.type === QuestionInputType.RADIO_GROUP) {
-  const responseValue = response as string;
+} else if (question.type === QuestionInputType.RADIO_GROUP && typeof response === 'string') {
-  questionData.answers[responseValue] = (questionData.answers[responseValue] || 0) + 1;
+  questionData.answers[response] = (questionData.answers[response] || 0) + 1;
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between bf8d50c and 6abd527.

📒 Files selected for processing (4)
  • shared/src/utils/stats/SurveyStatsCalculator.test.ts (1 hunks)
  • shared/src/utils/stats/SurveyStatsCalculator.ts (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx (1 hunks)
  • website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • shared/src/utils/stats/SurveyStatsCalculator.test.ts
  • website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
shared/src/utils/stats/SurveyStatsCalculator.ts

[error] 50-50: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 51-51: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 87-87: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 95-95: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (6)
website/src/app/[lang]/[region]/(website)/survey/responses/barchart-survey-response-component.tsx (3)

1-7: LGTM! Clean imports and interface definition.


15-30: Type the label props properly

Replace the any type with a proper interface for better type safety.


32-59: Well-implemented chart with accessibility considerations!

The chart implementation includes proper ARIA labels and responsive layout.

shared/src/utils/stats/SurveyStatsCalculator.ts (3)

4-4: ⚠️ Potential issue

Fix typo in import path constant

The constant SURVEY_FIRETORE_PATH is misspelled. It should be SURVEY_FIRESTORE_PATH.

-import { Survey, SURVEY_FIRETORE_PATH, SurveyQuestionnaire, SurveyStatus } from '../../types/survey';
+import { Survey, SURVEY_FIRESTORE_PATH, SurveyQuestionnaire, SurveyStatus } from '../../types/survey';

Likely invalid or redundant comment.


50-51: 🛠️ Refactor suggestion

Replace 'this' with class name in static methods

Using 'this' in static methods can be confusing. Use the class name instead.

-const surveysData = await this.fetchAndProcessSurveys(firestoreAdmin, recipients);
-const { typeCounts, aggregatedData, oldestDate } = this.aggregateSurveyData(surveysData);
+const surveysData = await SurveyStatsCalculator.fetchAndProcessSurveys(firestoreAdmin, recipients);
+const { typeCounts, aggregatedData, oldestDate } = SurveyStatsCalculator.aggregateSurveyData(surveysData);

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 50-50: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 51-51: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


60-68: ⚠️ Potential issue

Add error handling for Promise.all

The Promise.all operation could fail if any of the promises reject.

+try {
   const surveySnapshots = await Promise.all(
     recipients.docs
       .filter((recipient) => !recipient.get('test_recipient'))
       .map((recipient) =>
         firestoreAdmin
           .collection<Survey>(`${RECIPIENT_FIRESTORE_PATH}/${recipient.id}/${SURVEY_FIRETORE_PATH}`)
           .get(),
       ),
   );
+} catch (error) {
+  console.error('Failed to fetch survey snapshots:', error);
+  throw error;
+}

Likely invalid or redundant comment.

let oldestDate = new Date();

surveysData.forEach((survey) => {
if (this.isCompletedSurvey(survey)) {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve type safety and static method calls

  1. Replace 'this' with class name in static methods
  2. Add type safety for survey response handling
-if (this.isCompletedSurvey(survey)) {
+if (SurveyStatsCalculator.isCompletedSurvey(survey)) {
   Object.entries(survey.data!).forEach(([questionKey, response]) => {
-    this.processSurveyResponse(aggregatedSurveyData, questionnaire, questionKey, response);
+    SurveyStatsCalculator.processSurveyResponse(aggregatedSurveyData, questionnaire, questionKey, response);
   });

Also applies to: 94-96

🧰 Tools
🪛 Biome (1.9.4)

[error] 87-87: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

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

♻️ Duplicate comments (2)
shared/src/utils/stats/SurveyStatsCalculator.ts (2)

4-4: ⚠️ Potential issue

Fix typo in import path constant

The constant SURVEY_FIRETORE_PATH is misspelled. It should be SURVEY_FIRESTORE_PATH.


50-51: 🛠️ Refactor suggestion

Replace 'this' with class name in static methods

Using 'this' in static methods can be confusing. Use the class name instead.

🧰 Tools
🪛 Biome (1.9.4)

[error] 50-50: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 51-51: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6abd527 and d94d428.

📒 Files selected for processing (1)
  • shared/src/utils/stats/SurveyStatsCalculator.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
shared/src/utils/stats/SurveyStatsCalculator.ts

[error] 50-50: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 51-51: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 87-87: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 95-95: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (2)
shared/src/utils/stats/SurveyStatsCalculator.ts (2)

21-42: LGTM! Well-structured class implementation

The class follows good practices with immutable fields, private constructor, and clear getter methods.


111-111: 🛠️ Refactor suggestion

Replace 'any' with a specific type

Using 'any' reduces type safety. Define a union type for possible response values.

-  response: any,
+  response: string | string[],

Likely invalid or redundant comment.

Comment on lines +123 to +132
if (question.type === QuestionInputType.CHECKBOX) {
(response as string[]).forEach((value) => {
questionData.answers[value] = (questionData.answers[value] || 0) + 1;
});
} else if (question.type === QuestionInputType.RADIO_GROUP) {
const responseValue = response as string;
questionData.answers[responseValue] = (questionData.answers[responseValue] || 0) + 1;
}
questionData.total++;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for response data structure

The response data should be validated before processing to prevent runtime errors.

 if (question.type === QuestionInputType.CHECKBOX) {
+  if (!Array.isArray(response)) {
+    console.warn(`Invalid checkbox response for question ${questionKey}`);
+    return;
+  }
   (response as string[]).forEach((value) => {
     questionData.answers[value] = (questionData.answers[value] || 0) + 1;
   });
 } else if (question.type === QuestionInputType.RADIO_GROUP) {
+  if (typeof response !== 'string') {
+    console.warn(`Invalid radio response for question ${questionKey}`);
+    return;
+  }
   const responseValue = response as string;
   questionData.answers[responseValue] = (questionData.answers[responseValue] || 0) + 1;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (question.type === QuestionInputType.CHECKBOX) {
(response as string[]).forEach((value) => {
questionData.answers[value] = (questionData.answers[value] || 0) + 1;
});
} else if (question.type === QuestionInputType.RADIO_GROUP) {
const responseValue = response as string;
questionData.answers[responseValue] = (questionData.answers[responseValue] || 0) + 1;
}
questionData.total++;
}
if (question.type === QuestionInputType.CHECKBOX) {
if (!Array.isArray(response)) {
console.warn(`Invalid checkbox response for question ${questionKey}`);
return;
}
(response as string[]).forEach((value) => {
questionData.answers[value] = (questionData.answers[value] || 0) + 1;
});
} else if (question.type === QuestionInputType.RADIO_GROUP) {
if (typeof response !== 'string') {
console.warn(`Invalid radio response for question ${questionKey}`);
return;
}
const responseValue = response as string;
questionData.answers[responseValue] = (questionData.answers[responseValue] || 0) + 1;
}
questionData.total++;
}

Comment on lines +48 to +54
static async build(firestoreAdmin: FirestoreAdmin): Promise<SurveyStatsCalculator> {
const recipients = await firestoreAdmin.collection<Recipient>(RECIPIENT_FIRESTORE_PATH).get();
const surveysData = await this.fetchAndProcessSurveys(firestoreAdmin, recipients);
const { typeCounts, aggregatedData, oldestDate } = this.aggregateSurveyData(surveysData);
const data = Object.entries(typeCounts).map(([type, total]) => ({ type, total }) as SurveyStats);
return new SurveyStatsCalculator(data, aggregatedData, oldestDate);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for database operations

The build method should handle potential database errors gracefully.

 static async build(firestoreAdmin: FirestoreAdmin): Promise<SurveyStatsCalculator> {
+  try {
     const recipients = await firestoreAdmin.collection<Recipient>(RECIPIENT_FIRESTORE_PATH).get();
     const surveysData = await SurveyStatsCalculator.fetchAndProcessSurveys(firestoreAdmin, recipients);
     const { typeCounts, aggregatedData, oldestDate } = SurveyStatsCalculator.aggregateSurveyData(surveysData);
     const data = Object.entries(typeCounts).map(([type, total]) => ({ type, total }) as SurveyStats);
     return new SurveyStatsCalculator(data, aggregatedData, oldestDate);
+  } catch (error) {
+    console.error('Failed to build SurveyStatsCalculator:', error);
+    throw error;
+  }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static async build(firestoreAdmin: FirestoreAdmin): Promise<SurveyStatsCalculator> {
const recipients = await firestoreAdmin.collection<Recipient>(RECIPIENT_FIRESTORE_PATH).get();
const surveysData = await this.fetchAndProcessSurveys(firestoreAdmin, recipients);
const { typeCounts, aggregatedData, oldestDate } = this.aggregateSurveyData(surveysData);
const data = Object.entries(typeCounts).map(([type, total]) => ({ type, total }) as SurveyStats);
return new SurveyStatsCalculator(data, aggregatedData, oldestDate);
}
static async build(firestoreAdmin: FirestoreAdmin): Promise<SurveyStatsCalculator> {
try {
const recipients = await firestoreAdmin.collection<Recipient>(RECIPIENT_FIRESTORE_PATH).get();
const surveysData = await SurveyStatsCalculator.fetchAndProcessSurveys(firestoreAdmin, recipients);
const { typeCounts, aggregatedData, oldestDate } = SurveyStatsCalculator.aggregateSurveyData(surveysData);
const data = Object.entries(typeCounts).map(([type, total]) => ({ type, total }) as SurveyStats);
return new SurveyStatsCalculator(data, aggregatedData, oldestDate);
} catch (error) {
console.error('Failed to build SurveyStatsCalculator:', error);
throw error;
}
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 50-50: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 51-51: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

Copy link
Contributor

@mkue mkue left a comment

Choose a reason for hiding this comment

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

Super nice work, thanks @Gavriil-Tzortzakis :)

The only thing that I would discourage from doing is importing .css files. I feel that with Tailwind, all styling should be done inline. The styling is imo more transparent like that – even when sometimes elements have a lot of classes specified.

I added one commit where I removed the .css files and added the styling as tailwind classes instead. Also, some other small cleanups (mostly typing), but those are just nits.

I hope it's fine if I do this – instead of commenting everywhere. I feel it's a little more efficient, but please let me know if you prefer comments.

Here's the commit with my changes: 6abd527

@@ -0,0 +1,271 @@
export interface Question {
Copy link
Contributor

Choose a reason for hiding this comment

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

Very nice! Thanks for improving the types here.

@Gavriil-Tzortzakis Gavriil-Tzortzakis merged commit 8bc1a22 into main Dec 28, 2024
16 checks passed
@Gavriil-Tzortzakis Gavriil-Tzortzakis deleted the gavrill/raw-survey-aggregation branch December 28, 2024 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
surveys website Issues concerning Website
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Website]: New page for survey data
3 participants