Skip to content

Refactored nested ternary expressions #3096

Merged
kasya merged 8 commits intoOWASP:mainfrom
anurag2787:refactor-nested-ternary
Jan 2, 2026
Merged

Refactored nested ternary expressions #3096
kasya merged 8 commits intoOWASP:mainfrom
anurag2787:refactor-nested-ternary

Conversation

@anurag2787
Copy link
Contributor

Description

Refactored nested ternary expressions into explicit conditional statements to improve readability and maintainability.

Resolves #2938

Checklist

  • Required: I read and followed the contributing guidelines
  • Required: I ran make check-test locally and all tests passed
  • I used AI for code, documentation, or tests in this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

Summary by CodeRabbit

  • Refactor
    • Standardized issue status badges and PR badge behavior across the mentorship UI for consistent labels and styling.
    • Simplified and clarified deadline display logic for more predictable outputs.
    • Consolidated assign-button tooltip handling for consistent titles during loading/assigning states.
    • Centralized the API keys page rendering into a content-type dispatch to uniformly handle loading, error, empty, and table states.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Refactors nested ternary logic into explicit conditional code on two frontend pages: moves deadline/PR/assign-button logic into helpers and derived status variables on the module issue details page; centralizes API-keys UI state into a ContentType-driven dispatch on the settings page.

Changes

Cohort / File(s) Summary
Module Issue Details Page Refactor
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx
Replaces nested ternaries with explicit conditionals: extracts deadline formatting into a statusText + color pair; adds issueStatusClass / issueStatusLabel; introduces getPRStatus for PR badge color/label and getAssignButtonTitle for assign-button title; updates badge and assign-button rendering to use these helpers.
API Keys Page — ContentType Abstraction
frontend/src/app/settings/api-keys/page.tsx
Introduces ContentType (error/loading/empty/table), adds getContentType() and a content-component dispatch; extracts ErrorState, EmptyState, ApiKeysTable components and uses LoadingSpinner for loading state, replacing prior inline conditional rendering.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • arkid15r
  • kasya

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Refactored nested ternary expressions' accurately and concisely describes the main change across both modified files.
Description check ✅ Passed The description directly relates to the changeset by explaining the refactoring of nested ternary expressions into explicit conditional statements.
Linked Issues check ✅ Passed The PR successfully addresses issue #2938 by extracting nested ternary operations into explicit conditional logic in both files, improving code readability and satisfying the typescript:S3358 rule.
Out of Scope Changes check ✅ Passed All changes in both files are directly scoped to refactoring nested ternary expressions and improving related code structure without introducing unrelated functionality.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c459e66 and c0a4506.

📒 Files selected for processing (1)
  • frontend/src/app/settings/api-keys/page.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-21T17:04:48.154Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.

Applied to files:

  • frontend/src/app/settings/api-keys/page.tsx
🧬 Code graph analysis (1)
frontend/src/app/settings/api-keys/page.tsx (2)
frontend/src/types/apiKey.ts (1)
  • ApiKey (9-15)
frontend/src/types/button.ts (1)
  • Button (4-9)
🔇 Additional comments (5)
frontend/src/app/settings/api-keys/page.tsx (5)

8-8: LGTM! Necessary imports for the refactoring.

The React import supports the React.ReactNode type annotation in getContentComponents, and the LoadingSpinner addresses the previous concern about using the full-page skeleton for in-card loading states.

Also applies to: 17-17


23-34: LGTM! Clean extraction of UI states.

These presentational components align with kasya's suggestion to separate content states, improving readability and maintainability.


36-77: LGTM! Proper null safety and clean component structure.

The use of (data?.apiKeys ?? []) on Line 54 correctly addresses the previous null-safety concern by combining optional chaining with nullish coalescing, ensuring safe iteration even when data is undefined.


79-89: LGTM! Successfully implements kasya's suggested pattern.

This map-based approach replaces the previous inline case statement, significantly improving readability. The use of LoadingSpinner at Line 86 also addresses the earlier concern about using ApiKeysSkeleton for in-card loading states.

Based on kasya's feedback from past reviews.


154-156: LGTM! Clean implementation of the map-based rendering pattern.

The content type is computed once (Line 154), the component map is built once with runtime values (Line 155), and rendering is a simple lookup (Line 289). This successfully eliminates nested ternaries while following kasya's suggestion.

Based on kasya's feedback from past reviews.

Also applies to: 289-289


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
frontend/src/app/settings/api-keys/page.tsx (1)

73-88: Type and helper defined inside component body.

The ContentType type and getContentType function are defined inside the component. While this works, consider moving the type definition outside the component for clarity and potential reuse. The function itself is fine here since it depends on component state.

🔎 Suggested improvement
+type ContentType = 'error' | 'loading' | 'empty' | 'table'
+
 export default function Page() {
   // ... state declarations ...

-  type ContentType = 'error' | 'loading' | 'empty' | 'table'
-
   const getContentType = (): ContentType => {
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx (1)

141-151: !issueId check is unreachable.

At line 450 where getAssignButtonTitle is called, the component has already passed the early returns (lines 99-104). Given skip: !issueId on the query (line 66), if issueId is missing, the query result would lead to the !issue early return. The !issueId branch in this helper will never execute.

Consider simplifying:

🔎 Simplified version
  const getAssignButtonTitle = (assigning: boolean) => {
-   let title: string
-   if (!issueId) {
-     title = 'Loading issue…'
-   } else if (assigning) {
-     title = 'Assigning…'
-   } else {
-     title = 'Assign to this user'
-   }
-   return title
+   return assigning ? 'Assigning…' : 'Assign to this user'
  }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7caf577 and a2ae13f.

📒 Files selected for processing (2)
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx
  • frontend/src/app/settings/api-keys/page.tsx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: adithya-naik
Repo: OWASP/Nest PR: 1894
File: frontend/src/components/TopContributorsList.tsx:74-74
Timestamp: 2025-07-28T14:51:14.736Z
Learning: In the OWASP/Nest project, the maintainer adithya-naik prefers not to create separate components for code that's only used in two specific cases, following the YAGNI principle to avoid over-engineering when the duplication is limited and manageable.
🧬 Code graph analysis (2)
frontend/src/app/settings/api-keys/page.tsx (3)
frontend/src/components/skeletons/ApiKeySkelton.tsx (1)
  • ApiKeysSkeleton (1-96)
frontend/src/types/apiKey.ts (1)
  • ApiKey (9-15)
frontend/src/types/button.ts (1)
  • Button (4-9)
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx (2)
backend/tests/apps/github/models/issue_test.py (1)
  • issue (26-28)
backend/apps/github/models/common.py (1)
  • title (40-44)
🔇 Additional comments (6)
frontend/src/app/settings/api-keys/page.tsx (1)

241-281: Switch-based rendering achieves the PR objective.

The switch statement effectively replaces the nested ternary expressions with explicit conditional logic, improving readability and maintainability as intended by issue #2938.

frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx (5)

39-61: Deadline formatting refactored correctly.

The if-else chains for statusText and color are clearer than nested ternaries. Logic is correct.


112-123: Issue status derivation is clear and correct.

The explicit if-else structure makes the status logic easy to follow and maintain.


125-139: getPRStatus helper is well-structured.

Clear conditional logic for PR status. The colors (#8657E5, #DA3633, #238636) match those used in issueStatusClass above, maintaining visual consistency.


386-396: PR status badge rendering works correctly.

The IIFE pattern to destructure getPRStatus result is consistent with similar usage in the component (lines 256-259).


449-451: Assign button title helper usage is clean.

The refactor successfully eliminates inline conditional logic for the button title.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 30, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
frontend/src/app/settings/api-keys/page.tsx (2)

74-86: Strengthen the empty-state check to prevent potential runtime errors.

The final else branch returns 'table' without verifying that data?.apiKeys actually exists. If data is present but data.apiKeys is undefined or null (e.g., due to a backend schema issue), the code would return 'table' and then attempt to map over undefined at line 252, causing a runtime error.

🔎 Suggested improvement to handle edge cases defensively
  const getContentType = (): ContentType => {
    if (error) {
      return 'error'
-   } else if (loading) {
+   }
+   if (loading) {
      return 'loading'
-   } else if (data?.apiKeys && data.apiKeys.length === 0) {
+   }
+   if (!data?.apiKeys || data.apiKeys.length === 0) {
      return 'empty'
-   } else {
-     return 'table'
    }
+   return 'table'
  }

This revision:

  • Treats missing or undefined apiKeys as empty rather than attempting to render a table
  • Removes unnecessary else keywords since each branch returns

222-282: Consider extracting the IIFE into a separate render function for improved readability.

While the IIFE pattern works, it's not idiomatic in React and can reduce code readability. Extracting the switch logic into a dedicated render function would align better with React conventions and make the code more maintainable.

🔎 Suggested refactor to extract content rendering

Define a helper function before the return statement:

const renderContent = () => {
  switch (contentType) {
    case 'error':
      return (
        <div className="rounded-md bg-red-50 p-4 text-red-700 dark:bg-red-900/20 dark:text-red-400">
          Error loading API keys
        </div>
      )
    case 'loading':
      return <LoadingSpinner />
    case 'empty':
      return (
        <div className="rounded-md bg-gray-50 p-8 text-center text-gray-500 dark:bg-gray-800/50 dark:text-gray-400">
          You don't have any API keys yet.
        </div>
      )
    case 'table':
      return (
        <div className="overflow-x-auto">
          <table className="w-full border-collapse">
            {/* table content */}
          </table>
        </div>
      )
    default:
      return null
  }
}

Then in the JSX, simply call:

-          {(() => {
-            switch (contentType) {
-              case 'error':
-                return (...)
-              // ... other cases
-            }
-          })()}
+          {renderContent()}

This approach is more conventional and easier to follow for React developers.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74d22da and d4f077a.

📒 Files selected for processing (1)
  • frontend/src/app/settings/api-keys/page.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/app/settings/api-keys/page.tsx (2)
frontend/src/types/apiKey.ts (1)
  • ApiKey (9-15)
frontend/src/types/button.ts (1)
  • Button (4-9)
🔇 Additional comments (1)
frontend/src/app/settings/api-keys/page.tsx (1)

17-17: LGTM! Addresses previous review feedback.

The LoadingSpinner import is appropriate for the inline loading state and correctly addresses the past review concern about using the full-page ApiKeysSkeleton inside the card.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 30, 2025
@anurag2787 anurag2787 marked this pull request as ready for review December 30, 2025 11:41
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
frontend/src/app/settings/api-keys/page.tsx (2)

74-74: Consider moving ContentType outside the component.

Defining the ContentType type inside the component function is valid but moving it to module scope would improve organization and enable potential reuse across files.

🔎 Proposed refactor

Move the type definition above the component:

+type ContentType = 'error' | 'loading' | 'empty' | 'table'
+
 export default function Page() {
   // ... 
-  type ContentType = 'error' | 'loading' | 'empty' | 'table'

222-282: Refactor successfully eliminates nested ternary.

The switch-based approach clearly separates the four rendering states and successfully addresses the PR objective of replacing nested ternary expressions with explicit conditionals. The logic is correct and all cases are properly handled.

The IIFE wrapper ((() => { switch... })()) is valid but adds an extra layer. Consider extracting to a named function for cleaner code:

🔎 Optional refactor
+ const renderContent = () => {
+   switch (contentType) {
+     case 'error':
+       return (
+         <div className="rounded-md bg-red-50 p-4 text-red-700 dark:bg-red-900/20 dark:text-red-400">
+           Error loading API keys
+         </div>
+       )
+     case 'loading':
+       return <LoadingSpinner />
+     case 'empty':
+       return (
+         <div className="rounded-md bg-gray-50 p-8 text-center text-gray-500 dark:bg-gray-800/50 dark:text-gray-400">
+           You don't have any API keys yet.
+         </div>
+       )
+     case 'table':
+       return (
+         <div className="overflow-x-auto">
+           <table className="w-full border-collapse">
+             {/* ... table content ... */}
+           </table>
+         </div>
+       )
+     default:
+       return null
+   }
+ }
+
  <SecondaryCard>
    {/* ... */}
-   {(() => {
-     switch (contentType) {
-       // ... cases ...
-     }
-   })()}
+   {renderContent()}
  </SecondaryCard>
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4f077a and 38f38a5.

📒 Files selected for processing (1)
  • frontend/src/app/settings/api-keys/page.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/app/settings/api-keys/page.tsx (2)
frontend/src/types/apiKey.ts (1)
  • ApiKey (9-15)
frontend/src/types/button.ts (1)
  • Button (4-9)
🔇 Additional comments (1)
frontend/src/app/settings/api-keys/page.tsx (1)

17-17: LGTM!

The LoadingSpinner import appropriately addresses the previous feedback about using a lightweight loading indicator instead of the full-page ApiKeysSkeleton.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 30, 2025
coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 30, 2025
Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

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

@anurag2787 thanks for working on this. Left a suggestion/question!

Comment on lines 222 to 283
{(() => {
switch (contentType) {
case 'error':
return (
<div className="rounded-md bg-red-50 p-4 text-red-700 dark:bg-red-900/20 dark:text-red-400">
Error loading API keys
</div>
)
case 'loading':
return <LoadingSpinner />
case 'empty':
return (
<div className="rounded-md bg-gray-50 p-8 text-center text-gray-500 dark:bg-gray-800/50 dark:text-gray-400">
You don't have any API keys yet.
</div>
)
case 'table':
return (
<div className="overflow-x-auto">
<table className="w-full border-collapse">
<thead>
<tr className="border-b-1 border-b-gray-200 dark:border-b-gray-700">
<th className="py-3 text-left font-semibold">Name</th>
<th className="py-3 text-left font-semibold">ID</th>
<th className="py-3 text-left font-semibold">Created</th>
<th className="py-3 text-left font-semibold">Expires</th>
<th className="py-3 text-right font-semibold">Actions</th>
</tr>
</thead>
<tbody>
{(data?.apiKeys ?? []).map((key: ApiKey) => (
<tr
key={key.uuid}
className="border-b border-b-gray-200 dark:border-b-gray-700"
>
<td className="py-3">{key.name}</td>
<td className="py-3 font-mono text-sm">{key.uuid}</td>
<td className="py-3">{format(new Date(key.createdAt), 'PP')}</td>
<td className="py-3">
{key.expiresAt ? format(new Date(key.expiresAt), 'PP') : 'Never'}
</td>
<td className="py-3 text-right">
<Button
variant="light"
size="sm"
onPress={() => setKeyToRevoke(key)}
className="text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20"
>
<FaTrash />
</Button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)
default:
return null
}
})()}
</SecondaryCard>
Copy link
Collaborator

Choose a reason for hiding this comment

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

@anurag2787 I'm not sure I like using case inside a template. It is a bit hard to read..

What if we put those into separate "components" in this file and use a map to match with contentState. And the in the template we'll just get the right component based on contentState?

Something like this (just an example):

// 1. Define your components (separate functions)
const ErrorState = () => (
  <div className="rounded-md bg-red-50 p-4 text-red-700 dark:bg-red-900/20 dark:text-red-400">
    Error loading API keys
  </div>
)

const EmptyState = () => (
  <div className="rounded-md bg-gray-50 p-8 text-center text-gray-500 dark:bg-gray-800/50 dark:text-gray-400">
    You don't have any API keys yet.
  </div>
)

const ApiKeysTable = ({ data, onRevoke }) => (
  <div className="overflow-x-auto">
    {/* your table JSX */}
  </div>
)

// 2. Map them (inside your main component)
const contentComponents: Record<ContentType, () => JSX.Element> = {
  error: () => <ErrorState />,
  loading: () => <LoadingSpinner />,
  empty: () => <EmptyState />,
  table: () => <ApiKeysTable data={data} onRevoke={setKeyToRevoke} />
}

// 3. Render (in your JSX)
{contentComponents[contentType]()}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
frontend/src/app/settings/api-keys/page.tsx (1)

131-152: Clean content-type dispatch pattern.

This approach aligns well with the suggested pattern from previous review and effectively replaces nested ternaries with a declarative component map. The logic is clear and maintainable.

One optional refinement: consider moving ContentType outside the component to avoid type redefinition on each render and improve reusability.

🔎 Optional: Extract type outside component
+type ContentType = 'error' | 'loading' | 'empty' | 'table'
+
 export default function Page() {
   // ... state declarations ...

-  type ContentType = 'error' | 'loading' | 'empty' | 'table'
-
   const getContentType = (): ContentType => {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e7789b and c459e66.

📒 Files selected for processing (1)
  • frontend/src/app/settings/api-keys/page.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-21T17:04:48.154Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.

Applied to files:

  • frontend/src/app/settings/api-keys/page.tsx
🧬 Code graph analysis (1)
frontend/src/app/settings/api-keys/page.tsx (2)
frontend/src/types/apiKey.ts (1)
  • ApiKey (9-15)
frontend/src/types/button.ts (1)
  • Button (4-9)
🔇 Additional comments (4)
frontend/src/app/settings/api-keys/page.tsx (4)

8-8: LGTM on imports.

The React import supports the React.ReactNode type annotation, and LoadingSpinner appropriately replaces the full-page ApiKeysSkeleton for in-card loading states as suggested in previous review.

Also applies to: 18-18


24-78: Well-structured extraction of UI state components.

The separation into ErrorState, EmptyState, and ApiKeysTable follows the pattern suggested in previous review and improves readability. The null-safe access at line 55 with (data?.apiKeys ?? []).map(...) properly addresses the earlier concern about potential runtime errors when apiKeys is undefined.


286-286: LGTM!

The invocation contentComponents[contentType]() is clean and achieves the PR objective of eliminating nested ternary expressions in favor of a more readable dispatch pattern.


233-235: Correct handling of initial vs. subsequent loading states.

Using ApiKeysSkeleton for initial page load (when !data) while LoadingSpinner handles in-card refetch loading properly addresses the UI layout concern from previous review.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 31, 2025
@anurag2787 anurag2787 requested a review from kasya December 31, 2025 05:41
@sonarqubecloud
Copy link

Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

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

@anurag2787 thanks for working on this 👍🏼

@kasya kasya enabled auto-merge January 2, 2026 00:22
@kasya kasya added this pull request to the merge queue Jan 2, 2026
Merged via the queue into OWASP:main with commit af05575 Jan 2, 2026
25 checks passed
arkid15r pushed a commit that referenced this pull request Jan 2, 2026
* Removed nested ternary

* fixed coderabbit review

* fixed soanrqube warning

* fixed warning

* Added check

* Fixed review

* Fixed sonarqube warning
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extract nested ternary operations into independent statements

2 participants