Repository: guibranco/pull-request-ui
Pull request #96: Enhance UI
Author: login: guibranco
Labels: name: enhancement
description: New feature or request
name: ☑️ auto-merge
description: Automatic merging of pull requests (gstraccini-bot)
name: size/L
description: Denotes a PR that changes 100-499 lines, ignoring generated files.
name: Review effort [1-5]: 4
description: High review effort required for this pull request (effort level: 4)
name: bug fix
Created: 2025-03-10T22:21:38Z | Merged: 2025-03-10T22:26:03Z

### **User description**
## 📑 Description
Enhance UI

## ✅ Checks
- [X] My pull request adheres to the code style of this project
- [X] My code requires changes to the documentation
- [X] I have updated the documentation as required
- [X] All the tests have passed

## ☢️ Does this introduce a breaking change?
- [ ] Yes
- [X] No

---





___

### **Description**
- Enhanced UI components for better responsiveness and layout.
- Improved event handling and status checks in timeline components.
- Updated dependencies to ensure compatibility and performance.


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>RecentPullRequests.tsx</strong><dd><code>UI Improvements in Recent Pull Requests Component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

src/components/select-data/RecentPullRequests.tsx
<li>Added <code>flex-shrink-0</code> class to icons for better layout.<br> <li> Improved text truncation for owner and sender names.<br> <li> Enhanced button layout for better responsiveness.<br>


</details>


  </td>
  <td><a href="https://github.com/guibranco/pull-request-ui/pull/96/files#diff-63294ecb8a4be65fe24ab13052d83c364b00422b646918a54bf39250ef93eda2">+11/-7</a>&nbsp; &nbsp; </td>
</tr>

<tr>
  <td>
    <details>
      <summary><strong>EventList.tsx</strong><dd><code>Event List UI Enhancements</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

src/components/timeline/EventList.tsx
<li>Added total types count display in the event list.<br> <li> Improved layout for event count display.<br>


</details>


  </td>
  <td><a href="https://github.com/guibranco/pull-request-ui/pull/96/files#diff-aa5923657040216141b3b6a39a0ac624ddaf39520ceaa8f9aff3b2bbd4c6ad86">+9/-3</a>&nbsp; &nbsp; &nbsp; </td>
</tr>

<tr>
  <td>
    <details>
      <summary><strong>EventTimeline.tsx</strong><dd><code>Enhanced Event Timeline Layout</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

src/components/timeline/EventTimeline.tsx
<li>Improved layout for event sequences display.<br> <li> Added group count display for events.<br>


</details>


  </td>
  <td><a href="https://github.com/guibranco/pull-request-ui/pull/96/files#diff-155e4b6c0cac3df6696aa1e10499e01e8b67f708249fb8950460ee4465477f5f">+7/-2</a>&nbsp; &nbsp; &nbsp; </td>
</tr>

<tr>
  <td>
    <details>
      <summary><strong>MermaidDiagram.tsx</strong><dd><code>Mermaid Diagram Logic Improvements</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

src/components/timeline/MermaidDiagram.tsx
<li>Refactored event grouping and sorting logic.<br> <li> Improved actor management in diagrams.<br>


</details>


  </td>
  <td><a href="https://github.com/guibranco/pull-request-ui/pull/96/files#diff-f35022bab712420e0084d132b301dae3eea5d937996b10f8c0584551e1d3c64b">+114/-81</a></td>
</tr>

<tr>
  <td>
    <details>
      <summary><strong>TimelineViewStep.tsx</strong><dd><code>Timeline View Step Enhancements</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

src/steps/TimelineViewStep.tsx
<li>Added GitHub link for pull requests.<br> <li> Improved layout for pull request information display.<br>


</details>


  </td>
  <td><a href="https://github.com/guibranco/pull-request-ui/pull/96/files#diff-b984456da59d7d62474a49ec1a9fddc3f48362a06f4fbb1e7fe799e3ebacc5a2">+24/-12</a>&nbsp; </td>
</tr>
</table></td></tr><tr><td><strong>Bug fix
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>EventItem.tsx</strong><dd><code>Enhanced Event Status Handling</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

src/components/timeline/EventItem.tsx
<li>Added additional status checks for events.<br> <li> Improved handling of event conclusions.<br>


</details>


  </td>
  <td><a href="https://github.com/guibranco/pull-request-ui/pull/96/files#diff-2bbf2f54e4a767c50d1a3328dbb4aacfc6ad529aa86a248ce17e66ffab63bec6">+4/-2</a>&nbsp; &nbsp; &nbsp; </td>
</tr>
</table></td></tr><tr><td><strong>Dependencies
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>package-lock.json</strong><dd><code>Dependency Updates</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

package-lock.json
- Updated `typescript-eslint` to version 8.26.1.



</details>


  </td>
  <td><a href="https://github.com/guibranco/pull-request-ui/pull/96/files#diff-053150b640a7ce75eff69d1a22cae7f0f94ad64ce9a855db544dda0929316519">+86/-106</a></td>
</tr>

<tr>
  <td>
    <details>
      <summary><strong>package.json</strong><dd><code>Package Dependency Update</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

package.json
- Updated `typescript-eslint` version in dependencies.



</details>


  </td>
  <td><a href="https://github.com/guibranco/pull-request-ui/pull/96/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519">+1/-1</a>&nbsp; &nbsp; &nbsp; </td>
</tr>
</table></td></tr></tr></tbody></table>

___

> 💡 **Penify usage**:
>Comment `/help` on the PR to get a list of all available Penify tools and their descriptions



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **Chores**
  - Upgraded development tooling to ensure smoother builds.

- **Bug Fixes**
  - Enhanced event status detection for more reliable reporting.

- **New Features**
  - Improved timeline and sequence diagram views with clear event grouping and counts.
  - Added a direct external link for viewing pull requests.

- **Style**
  - Refined component layouts with better spacing and text truncation for a more responsive user interface.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

<!-- Korbit AI PR Description Start -->
## Description by Korbit AI


### What change is being made?
Upgrade `typescript-eslint` to version 8.26.1, improve layout flexibility and readability in `RecentPullRequests` component, enhance event handling and display in `EventItem`, `EventList`, `EventTimeline`, and `MermaidDiagram` with better sequencing, grouping, and participant management, and add external link functionality in `TimelineViewStep`.

### Why are these changes being made?
Updating `typescript-eslint` addresses compatibility and consistency improvements. Layout updates in `RecentPullRequests` improve responsiveness and data presentation in smaller screens. Enhancements in event components provide better clarity and organization for users reviewing timelines and sequences. Adding the GitHub link functionality directly in `TimelineViewStep` facilitates easy access to original PRs for users, promoting a more integrated experience.
```


> Is this description stale? Ask me to generate a new description by commenting `/korbit-generate-pr-description`
<!-- Korbit AI PR Description End -->


Diff:
diff --git a/package-lock.json b/package-lock.json
index d635505e..ef23db8f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -35,7 +35,7 @@
         "postcss": "^8.4.35",
         "tailwindcss": "^3.4.1",
         "typescript": "^5.8.2",
-        "typescript-eslint": "^8.3.0",
+        "typescript-eslint": "^8.26.1",
         "vite": "^6.2.1",
         "vitest": "^3.0.8"
       }
@@ -1721,20 +1721,21 @@
       "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.8.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz",
-      "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz",
+      "integrity": "sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "8.8.1",
-        "@typescript-eslint/type-utils": "8.8.1",
-        "@typescript-eslint/utils": "8.8.1",
-        "@typescript-eslint/visitor-keys": "8.8.1",
+        "@typescript-eslint/scope-manager": "8.26.1",
+        "@typescript-eslint/type-utils": "8.26.1",
+        "@typescript-eslint/utils": "8.26.1",
+        "@typescript-eslint/visitor-keys": "8.26.1",
         "graphemer": "^1.4.0",
         "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
-        "ts-api-utils": "^1.3.0"
+        "ts-api-utils": "^2.0.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1745,24 +1746,21 @@
       },
       "peerDependencies": {
         "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
-        "eslint": "^8.57.0 || ^9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.9.0"
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.8.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz",
-      "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.1.tgz",
+      "integrity": "sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.8.1",
-        "@typescript-eslint/types": "8.8.1",
-        "@typescript-eslint/typescript-estree": "8.8.1",
-        "@typescript-eslint/visitor-keys": "8.8.1",
+        "@typescript-eslint/scope-manager": "8.26.1",
+        "@typescript-eslint/types": "8.26.1",
+        "@typescript-eslint/typescript-estree": "8.26.1",
+        "@typescript-eslint/visitor-keys": "8.26.1",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -1773,22 +1771,19 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "eslint": "^8.57.0 || ^9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.9.0"
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.8.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz",
-      "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz",
+      "integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.8.1",
-        "@typescript-eslint/visitor-keys": "8.8.1"
+        "@typescript-eslint/types": "8.26.1",
+        "@typescript-eslint/visitor-keys": "8.26.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1799,15 +1794,16 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.8.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz",
-      "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz",
+      "integrity": "sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "8.8.1",
-        "@typescript-eslint/utils": "8.8.1",
+        "@typescript-eslint/typescript-estree": "8.26.1",
+        "@typescript-eslint/utils": "8.26.1",
         "debug": "^4.3.4",
-        "ts-api-utils": "^1.3.0"
+        "ts-api-utils": "^2.0.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1816,17 +1812,17 @@
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.9.0"
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.8.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz",
-      "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz",
+      "integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
@@ -1836,19 +1832,20 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.8.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz",
-      "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz",
+      "integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.8.1",
-        "@typescript-eslint/visitor-keys": "8.8.1",
+        "@typescript-eslint/types": "8.26.1",
+        "@typescript-eslint/visitor-keys": "8.26.1",
         "debug": "^4.3.4",
         "fast-glob": "^3.3.2",
         "is-glob": "^4.0.3",
         "minimatch": "^9.0.4",
         "semver": "^7.6.0",
-        "ts-api-utils": "^1.3.0"
+        "ts-api-utils": "^2.0.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1857,10 +1854,8 @@
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <5.9.0"
       }
     },
     "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
@@ -1868,6 +1863,7 @@
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
       "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "balanced-match": "^1.0.0"
       }
@@ -1877,6 +1873,7 @@
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
       "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
       "dev": true,
+      "license": "ISC",
       "dependencies": {
         "brace-expansion": "^2.0.1"
       },
@@ -1888,15 +1885,16 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.8.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz",
-      "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz",
+      "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@typescript-eslint/scope-manager": "8.8.1",
-        "@typescript-eslint/types": "8.8.1",
-        "@typescript-eslint/typescript-estree": "8.8.1"
+        "@typescript-eslint/scope-manager": "8.26.1",
+        "@typescript-eslint/types": "8.26.1",
+        "@typescript-eslint/typescript-estree": "8.26.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1906,17 +1904,19 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "eslint": "^8.57.0 || ^9.0.0"
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.9.0"
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.8.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz",
-      "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz",
+      "integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.8.1",
-        "eslint-visitor-keys": "^3.4.3"
+        "@typescript-eslint/types": "8.26.1",
+        "eslint-visitor-keys": "^4.2.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1926,18 +1926,6 @@
         "url": "https://opencollective.com/typescript-eslint"
       }
     },
-    "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
-      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
-      "dev": true,
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
     "node_modules/@vitejs/plugin-react-swc": {
       "version": "3.8.0",
       "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.8.0.tgz",
@@ -3443,16 +3431,6 @@
         "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/eslint/node_modules/@eslint/js": {
-      "version": "9.12.0",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz",
-      "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      }
-    },
     "node_modules/eslint/node_modules/escape-string-regexp": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -3851,7 +3829,8 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
       "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
-      "dev": true
+      "dev": true,
+      "license": "MIT"
     },
     "node_modules/happy-dom": {
       "version": "17.4.3",
@@ -6197,15 +6176,16 @@
       }
     },
     "node_modules/ts-api-utils": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
-      "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz",
+      "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==",
       "dev": true,
+      "license": "MIT",
       "engines": {
-        "node": ">=16"
+        "node": ">=18.12"
       },
       "peerDependencies": {
-        "typescript": ">=4.2.0"
+        "typescript": ">=4.8.4"
       }
     },
     "node_modules/ts-dedent": {
@@ -6249,14 +6229,15 @@
       }
     },
     "node_modules/typescript-eslint": {
-      "version": "8.8.1",
-      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.8.1.tgz",
-      "integrity": "sha512-R0dsXFt6t4SAFjUSKFjMh4pXDtq04SsFKCVGDP3ZOzNP7itF0jBcZYU4fMsZr4y7O7V7Nc751dDeESbe4PbQMQ==",
+      "version": "8.26.1",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.26.1.tgz",
+      "integrity": "sha512-t/oIs9mYyrwZGRpDv3g+3K6nZ5uhKEMt2oNmAPwaY4/ye0+EH4nXIPYNtkYFS6QHm+1DFg34DbglYBz5P9Xysg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/eslint-plugin": "8.8.1",
-        "@typescript-eslint/parser": "8.8.1",
-        "@typescript-eslint/utils": "8.8.1"
+        "@typescript-eslint/eslint-plugin": "8.26.1",
+        "@typescript-eslint/parser": "8.26.1",
+        "@typescript-eslint/utils": "8.26.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -6265,10 +6246,9 @@
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.9.0"
       }
     },
     "node_modules/unist-util-stringify-position": {
diff --git a/package.json b/package.json
index 37139286..cf438583 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,7 @@
     "postcss": "^8.4.35",
     "tailwindcss": "^3.4.1",
     "typescript": "^5.8.2",
-    "typescript-eslint": "^8.3.0",
+    "typescript-eslint": "^8.26.1",
     "vite": "^6.2.1",
     "vitest": "^3.0.8"
   }
diff --git a/src/components/select-data/RecentPullRequests.tsx b/src/components/select-data/RecentPullRequests.tsx
index 5d5731a7..51faaa73 100644
--- a/src/components/select-data/RecentPullRequests.tsx
+++ b/src/components/select-data/RecentPullRequests.tsx
@@ -76,7 +76,7 @@ export function RecentPullRequests({ pullRequests, onSelect, loading }: RecentPu
           >
             <div className="flex items-start justify-between mb-3">
               <div className="flex items-center space-x-2 text-sm text-gray-400">
-                <GitPullRequest className="w-4 h-4 text-green-400" />
+                <GitPullRequest className="w-4 h-4 text-green-400 flex-shrink-0" />
                 <span>#{pr.number}</span>
               </div>
               <div className="flex items-center space-x-2">
@@ -90,23 +90,27 @@ export function RecentPullRequests({ pullRequests, onSelect, loading }: RecentPu
               {pr.title}
             </h4>
             
-            <div className="flex items-center justify-between">
-              <div className="text-sm text-gray-400">
-                {pr.owner}/{pr.name}
+            <div className="flex flex-col space-y-2 sm:flex-row sm:items-center sm:justify-between sm:space-y-0">
+              <div className="text-sm text-gray-400 break-all">
+                <span className="inline-block max-w-[200px] truncate">
+                  {pr.owner}/{pr.name}
+                </span>
               </div>
               <div className="flex items-center space-x-2">
                 {pr.sender_avatar ? (
                   <img
                     src={pr.sender_avatar}
                     alt={pr.sender}
-                    className="w-6 h-6 rounded-full"
+                    className="w-6 h-6 rounded-full flex-shrink-0"
                   />
                 ) : (
-                  <div className="w-6 h-6 rounded-full bg-gray-600 flex items-center justify-center">
+                  <div className="w-6 h-6 rounded-full bg-gray-600 flex items-center justify-center flex-shrink-0">
                     <User className="w-4 h-4 text-gray-400" />
                   </div>
                 )}
-                <span className="text-sm text-gray-400">{pr.sender}</span>
+                <span className="text-sm text-gray-400 truncate max-w-[100px]">
+                  {pr.sender}
+                </span>
               </div>
             </div>
           </button>
diff --git a/src/components/timeline/EventItem.tsx b/src/components/timeline/EventItem.tsx
index 3ca2a567..eddf538c 100644
--- a/src/components/timeline/EventItem.tsx
+++ b/src/components/timeline/EventItem.tsx
@@ -49,7 +49,9 @@ export function EventItem({ event, onViewPayload }: Readonly<EventItemProps>) {
     if (!conclusion) {
       const status = 
         event.payload.workflow_run?.status ||
-        event.payload.workflow_job?.status;
+        event.payload.workflow_job?.status ||
+        event.payload.check_suite?.status ||
+        event.payload.check_run?.status;
       
       if (status) {
         switch (status.toLowerCase()) {
@@ -94,7 +96,7 @@ export function EventItem({ event, onViewPayload }: Readonly<EventItemProps>) {
     }
   };
 
-  const appData = event.payload[event.type]?.app || null;
+  const appData = event.payload[event.type]?.app;
   const conclusion = getEventConclusion(event);
 
   return (
diff --git a/src/components/timeline/EventList.tsx b/src/components/timeline/EventList.tsx
index 7bee8637..6cdc569f 100644
--- a/src/components/timeline/EventList.tsx
+++ b/src/components/timeline/EventList.tsx
@@ -44,6 +44,7 @@ export function EventList({ events, expandedItems, onToggleExpand, isExpanded, o
   );
 
   const totalEvents = events.length;
+  const totalTypes = sortedTypeEntries.length;
 
   const handleEventSelect = (event: Event) => {
     const eventId = `${event.delivery_id}-${event.type}-${event.action}`;
@@ -106,9 +107,14 @@ export function EventList({ events, expandedItems, onToggleExpand, isExpanded, o
           <h3 className="text-2xl font-medium text-gray-100">
             Event Timeline
           </h3>
-          <span className="text-gray-400 text-lg">
-            ({totalEvents} event{totalEvents !== 1 ? 's' : ''})
-          </span>
+          <div className="flex items-center space-x-2">
+            <span className="px-2.5 py-0.5 bg-blue-500/20 text-blue-400 rounded-full text-sm font-medium">
+              {totalEvents} event{totalEvents !== 1 ? 's' : ''}
+            </span>
+            <span className="px-2.5 py-0.5 bg-purple-500/20 text-purple-400 rounded-full text-sm font-medium">
+              {totalTypes} type{totalTypes !== 1 ? 's' : ''}
+            </span>
+          </div>
         </div>
         {isExpanded ? (
           <ChevronDown className="w-6 h-6 text-gray-400" />
diff --git a/src/components/timeline/EventTimeline.tsx b/src/components/timeline/EventTimeline.tsx
index b0b95f89..3f47a0df 100644
--- a/src/components/timeline/EventTimeline.tsx
+++ b/src/components/timeline/EventTimeline.tsx
@@ -120,8 +120,13 @@ export function EventTimeline({ events, onViewPayload }: Readonly<EventTimelineP
         onClick={handleToggle}
         className="flex items-center justify-between w-full text-left mb-4"
       >
-        <div className="text-lg font-medium text-gray-300">
-          Event Sequences
+        <div className="flex items-center space-x-3">
+          <div className="text-lg font-medium text-gray-300">
+            Event Sequences
+          </div>
+          <span className="px-2.5 py-0.5 bg-blue-500/20 text-blue-400 rounded-full text-sm font-medium">
+            {relevantIds.length} group{relevantIds.length !== 1 ? 's' : ''}
+          </span>
         </div>
         {isExpanded ? (
           <ChevronDown className="w-5 h-5 text-gray-400" />
diff --git a/src/components/timeline/MermaidDiagram.tsx b/src/components/timeline/MermaidDiagram.tsx
index 58e21cb6..5e0f0577 100644
--- a/src/components/timeline/MermaidDiagram.tsx
+++ b/src/components/timeline/MermaidDiagram.tsx
@@ -9,22 +9,43 @@ interface MermaidDiagramProps {
   onToggle: () => void;
 }
 
-export function MermaidDiagram({ events, isExpanded, onToggle }: MermaidDiagramProps) {
+export function MermaidDiagram({ events, isExpanded, onToggle }: Readonly<MermaidDiagramProps>) {
   const mermaidRef = useRef<HTMLDivElement>(null);
 
-  const sanitizeParticipant = (name: string): string => {
-    return name.replace(/[^\w]/g, '_').substring(0, 20);
-  };
-
   const truncateText = (text: string, maxLength: number = 30): string => {
     return text.length > maxLength ? `${text.substring(0, maxLength)}...` : text;
   };
 
-  const formatEventTime = (date: string): string => {
-    return new Date(date).toLocaleTimeString([], { 
-      hour: '2-digit',
-      minute: '2-digit'
-    });
+  const getEventTitle = (event: Event): string => {
+    if (event.payload.issue) {
+      return `Issue #${event.payload.issue.number}`;
+    }
+    if (event.payload.pull_request) {
+      return `PR #${event.payload.pull_request.number}`;
+    }
+    if (event.payload.check_run) {
+      return `Check: ${event.payload.check_run.name}`;
+    }
+    if (event.payload.workflow_run) {
+      return `Workflow: ${event.payload.workflow_run.name}`;
+    }
+    if (event.payload.workflow_job) {
+      return `Job: ${event.payload.workflow_job.name}`;
+    }
+    if (event.payload.release) {
+      return `Release: ${event.payload.release.tag_name}`;
+    }
+    return 'Event';
+  };
+
+  const getEventStatus = (event: Event): string | null => {
+    return event.payload.check_run?.conclusion ||
+      event.payload.check_suite?.conclusion ||
+      event.payload.status?.state ||
+      event.payload.review?.state ||
+      event.payload.workflow_run?.conclusion ||
+      event.payload.workflow_job?.conclusion ||
+      null;
   };
 
   const generateSequenceDiagram = useCallback(() => {
@@ -35,75 +56,92 @@ export function MermaidDiagram({ events, isExpanded, onToggle }: MermaidDiagramP
     Note over GH,PR: No events found`;
     }
 
+    // Group events by their type and ID
+    const groupedEvents = events.reduce((acc, event) => {
+      const id = event.payload.issue?.id ||
+        event.payload.pull_request?.id ||
+        event.payload.check_run?.id ||
+        event.payload.workflow_run?.id ||
+        event.payload.workflow_job?.id ||
+        event.payload.release?.id ||
+        event.delivery_id;
+
+      const key = `${event.type}_${id}`;
+      
+      if (!acc[key]) {
+        acc[key] = [];
+      }
+      acc[key].push(event);
+      return acc;
+    }, {} as Record<string, Event[]>);
+
+    // Sort events within each group by date
+    Object.values(groupedEvents).forEach(group => {
+      group.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
+    });
+
     let diagram = 'sequenceDiagram\n';
-    
-    // Add core participants
     diagram += '    participant GH as GitHub\n';
     diagram += '    participant PR as Pull Request\n';
-    
-    // Track unique actors to avoid duplicates
-    const actors = new Set<string>();
-    
-    // First pass: collect all actors (limited to first 5 unique actors)
-    let actorCount = 0;
-    for (const event of events) {
-      const sender = event.payload?.sender?.login;
-      if (sender && !actors.has(sender) && actorCount < 5) {
-        const safeSender = sanitizeParticipant(sender);
-        actors.add(sender);
-        diagram += `    participant ${safeSender} as ${truncateText(sender, 15)}\n`;
-        actorCount++;
+
+    // Create a map for actors to ensure unique IDs
+    const actorMap = new Map<string, string>();
+    let actorCounter = 0;
+
+    const getActorId = (name: string): string => {
+      if (!actorMap.has(name)) {
+        actorMap.set(name, `Actor${actorCounter++}`);
       }
-    }
+      return actorMap.get(name)!;
+    };
 
-    // Second pass: add events with proper timing
-    let lastEventTime = '';
-    events.forEach((event, index) => {
-      const eventTime = formatEventTime(event.date);
-      const sender = event.payload?.sender?.login && actors.has(event.payload.sender.login)
-        ? sanitizeParticipant(event.payload.sender.login)
-        : 'GH';
-      
-      // Truncate action text
-      const action = truncateText(
-        event.action ? `${event.type}:${event.action}` : event.type,
-        25
-      );
-      
-      diagram += `    ${sender}->PR: ${action}\n`;
-      
-      // Only add time note if it's different from the last one
-      if (eventTime !== lastEventTime) {
-        diagram += `    Note over PR: ${eventTime}\n`;
-        lastEventTime = eventTime;
+    // Add actors (limit to 5 most active)
+    const actorCounts = new Map<string, number>();
+    events.forEach(event => {
+      const sender = event.payload.sender?.login;
+      if (sender) {
+        actorCounts.set(sender, (actorCounts.get(sender) || 0) + 1);
       }
-      
-      // Add minimal payload information
-      if (event.payload) {
-        const notes = [];
-        if (event.payload.comment?.body) {
-          notes.push('Comment');
-        }
-        if (event.payload.review?.state) {
-          notes.push(event.payload.review.state);
-        }
-        if (event.payload.check_suite?.conclusion) {
-          notes.push(event.payload.check_suite.conclusion);
+    });
+
+    // Sort actors by frequency and take top 5
+    const topActors = Array.from(actorCounts.entries())
+      .sort((a, b) => b[1] - a[1])
+      .slice(0, 5)
+      .map(([actor]) => actor);
+
+    // Add actor participants
+    topActors.forEach(actor => {
+      const actorId = getActorId(actor);
+      diagram += `    participant ${actorId} as ${truncateText(actor, 15)}\n`;
+    });
+
+    // Add event groups
+    Object.values(groupedEvents).forEach(group => {
+      const title = getEventTitle(group[0]);
+      diagram += `\n    Note over GH,PR: ${title}\n`;
+
+      group.forEach(event => {
+        // Determine the actor
+        let actor = 'GH';
+        if (event.payload.sender?.login && topActors.includes(event.payload.sender.login)) {
+          actor = getActorId(event.payload.sender.login);
         }
-        
-        if (notes.length > 0) {
-          diagram += `    Note over ${sender},PR: ${truncateText(notes.join(', '), 20)}\n`;
+
+        // Create the event message
+        const message = event.action ? `${event.type}:${event.action}` : event.type;
+        diagram += `    ${actor}->>PR: ${truncateText(message, 25)}\n`;
+
+        // Add status if available
+        const status = getEventStatus(event);
+        if (status) {
+          diagram += `    Note right of PR: ${status}\n`;
         }
-      }
-      
-      // Add spacing between events
-      if (index < events.length - 1) {
-        diagram += '\n';
-      }
+      });
     });
 
     return diagram;
-  }, [events]); // Include events in the dependencies
+  }, [events]);
 
   useEffect(() => {
     if (mermaidRef.current && events.length > 0 && isExpanded) {
@@ -112,24 +150,21 @@ export function MermaidDiagram({ events, isExpanded, onToggle }: MermaidDiagramP
         theme: 'dark',
         sequence: {
           showSequenceNumbers: false,
-          actorMargin: 50,
+          actorMargin: 80,
           messageMargin: 40,
-          boxMargin: 10,
-          noteMargin: 10,
           mirrorActors: false,
-          bottomMarginAdj: 2,
+          bottomMarginAdj: 10,
           useMaxWidth: true,
-          diagramMarginX: 50,
-          diagramMarginY: 30,
+          rightAngles: true,
+          boxMargin: 10,
           boxTextMargin: 5,
-          noteMarginX: 10,
-          noteMarginY: 10,
+          noteMargin: 10,
           messageAlign: 'center',
           actorFontSize: 14,
           noteFontSize: 13,
           messageFontSize: 13,
           wrap: true,
-          maxMessageWidth: 200,
+          maxMessageWidth: 150,
         },
         themeVariables: {
           primaryColor: '#1e40af',
@@ -147,10 +182,8 @@ export function MermaidDiagram({ events, isExpanded, onToggle }: MermaidDiagramP
         }
       });
 
-      const diagram = generateSequenceDiagram();
-      
       try {
-        mermaid.render('mermaid-diagram', diagram).then((result) => {
+        mermaid.render('mermaid-diagram', generateSequenceDiagram()).then((result) => {
           if (mermaidRef.current) {
             mermaidRef.current.innerHTML = result.svg;
           }
@@ -167,7 +200,7 @@ export function MermaidDiagram({ events, isExpanded, onToggle }: MermaidDiagramP
         }
       }
     }
-  }, [events, isExpanded, generateSequenceDiagram]); // Include generateSequenceDiagram in dependencies
+  }, [events, isExpanded, generateSequenceDiagram]);
 
   return (
     <div className="bg-gray-800 rounded-lg shadow-lg w-full overflow-hidden">
diff --git a/src/steps/TimelineViewStep.tsx b/src/steps/TimelineViewStep.tsx
index eb4757db..5b5acc91 100644
--- a/src/steps/TimelineViewStep.tsx
+++ b/src/steps/TimelineViewStep.tsx
@@ -1,6 +1,6 @@
 /* eslint-disable @typescript-eslint/no-explicit-any */
 import React, { useState, useEffect, useCallback } from 'react';
-import { ArrowLeft, Loader2, GitPullRequest, GitFork } from 'lucide-react';
+import { ArrowLeft, Loader2, GitPullRequest, GitFork, ExternalLink } from 'lucide-react';
 import { MermaidDiagram } from '../components/timeline/MermaidDiagram';
 import { EventList } from '../components/timeline/EventList';
 import { PayloadModal } from '../components/timeline/PayloadModal';
@@ -150,6 +150,7 @@ export function TimelineViewStep({ apiKey, repo, pr, onBack }: Readonly<Timeline
   }
 
   const [owner, repository] = repo.split('/');
+  const githubPrUrl = `https://github.com/${owner}/${repository}/pull/${pr}`;
 
   return (
     <div className="space-y-6">
@@ -181,23 +182,28 @@ export function TimelineViewStep({ apiKey, repo, pr, onBack }: Readonly<Timeline
             </div>
             
             {pullRequestInfo && (
-              <div className="flex items-center space-x-2">
-                <GitPullRequest className="w-5 h-5 text-green-400" />
-                <span className="text-xl font-medium text-gray-100">
-                  #{pr} {pullRequestInfo.title}
-                </span>
+              <div className="flex items-center space-x-4">
+                <div className="flex items-center space-x-2">
+                  <GitPullRequest className="w-5 h-5 text-green-400" />
+                  <span className="text-xl font-medium text-gray-100">
+                    #{pr} {pullRequestInfo.title}
+                  </span>
+                </div>
+                <a
+                  href={githubPrUrl}
+                  target="_blank"
+                  rel="noopener noreferrer"
+                  className="flex items-center space-x-1 text-blue-400 hover:text-blue-300 transition-colors"
+                >
+                  <ExternalLink className="w-4 h-4" />
+                  <span className="text-sm">View on GitHub</span>
+                </a>
               </div>
             )}
           </div>
         </div>
       </div>
 
-      <MermaidDiagram 
-        events={events} 
-        isExpanded={isSequenceExpanded}
-        onToggle={handleSequenceToggle}
-      />
-      
       <EventList
         events={events}
         expandedItems={expandedItems}
@@ -206,6 +212,12 @@ export function TimelineViewStep({ apiKey, repo, pr, onBack }: Readonly<Timeline
         isExpanded={isTimelineExpanded}
         onToggle={handleTimelineToggle}
       />
+      
+      <MermaidDiagram 
+        events={events} 
+        isExpanded={isSequenceExpanded}
+        onToggle={handleSequenceToggle}
+      />
 
       {selectedPayload && (
         <PayloadModal


Reviews:
user: login: gstraccini[bot]
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T22:21:49Z
user: login: sourcery-ai[bot]
body: Hey @guibranco - I've reviewed your changes - here's some feedback:

**Overall Comments**:
- Consider extracting the Mermaid diagram configuration into a separate file or constant for better maintainability.
- The TimelineViewStep component could benefit from extracting the GitHub URL creation logic into a helper function.

<details>
<summary>Here's what I looked at during the review</summary>

- 🟢 **General issues**: all looks good
- 🟢 **Security**: all looks good
- 🟢 **Testing**: all looks good
- 🟡 **Complexity**: 1 issue found
- 🟢 **Documentation**: all looks good
</details>

***

<details>
<summary>Sourcery is free for open source - if you like our reviews please consider sharing them ✨</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://sourcery.ai)
- [Facebook](https://www.facebook.com/sharer/sharer.php?u=https://sourcery.ai)

</details>

<sub>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
</sub>
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T22:22:25Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>src/components/select-data/RecentPullRequests.tsx (3)</summary><blockquote>

`93-97`: **Consider adding a hover tooltip for truncated text.**
When using a fixed maximum width (`max-w-[200px]`) and `truncate`, users with longer repository names risk losing context. A tooltip or title attribute may help display the full repo name on hover.


You could implement a simple attribute as follows:

```diff
- <span className="inline-block max-w-[200px] truncate">
+ <span className="inline-block max-w-[200px] truncate" title={`${pr.owner}/${pr.name}`}>
```

---

`107-107`: **Add accessibility support to the fallback avatar container.**
A `<div>` without text or ARIA attributes can affect screen readers. Consider adding a descriptive label or role for better accessibility.


For example:

```diff
- <div className="w-6 h-6 rounded-full bg-gray-600 flex items-center justify-center flex-shrink-0">
+ <div
+   className="w-6 h-6 rounded-full bg-gray-600 flex items-center justify-center flex-shrink-0"
+   role="img"
+   aria-label="No avatar available"
+ >
```

---

`111-113`: **Consider a tooltip for truncated sender names.**
Long sender names are cut off at 100px width. Providing a title attribute or other hint can help users see the full identity.


```diff
- <span className="text-sm text-gray-400 truncate max-w-[100px]">
+ <span
+   className="text-sm text-gray-400 truncate max-w-[100px]"
+   title={pr.sender}
+ >
```

</blockquote></details>
<details>
<summary>src/components/timeline/MermaidDiagram.tsx (2)</summary><blockquote>

`87-129`: **Efficient actor mapping and limiting to top 5.**
This logic handles typical scenarios of many event senders. Consider making the limit configurable if needed.

---

`186-186`: **Render call seems correct; consider sanitization.**
While Mermaid generally sanitizes input, be cautious if user-supplied data can produce unexpected diagrams. A safeguard or escaping routine may be advisable.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

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

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 7644e0bde83fc56b3304254eabb0d44d95d80704 and 4eff9c5df4f2f15fb8f8ae80f0aba825799beda0.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `package-lock.json` is excluded by `!**/package-lock.json`

</details>

<details>
<summary>📒 Files selected for processing (7)</summary>

* `package.json` (1 hunks)
* `src/components/select-data/RecentPullRequests.tsx` (2 hunks)
* `src/components/timeline/EventItem.tsx` (2 hunks)
* `src/components/timeline/EventList.tsx` (2 hunks)
* `src/components/timeline/EventTimeline.tsx` (1 hunks)
* `src/components/timeline/MermaidDiagram.tsx` (5 hunks)
* `src/steps/TimelineViewStep.tsx` (4 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: pixeebot[bot]

</details>

<details>
<summary>🔇 Additional comments (16)</summary><blockquote>

<details>
<summary>package.json (1)</summary>

`42-42`: **Version update for typescript-eslint looks good.**

This update from an earlier version to `^8.26.1` brings in the latest improvements, bug fixes, and features from the typescript-eslint package, which should enhance the development experience.

</details>
<details>
<summary>src/components/timeline/EventList.tsx (2)</summary>

`47-47`: **Good addition of type count tracking.**

Adding the `totalTypes` variable provides valuable information about the event distribution, which is then used in the UI display.

---

`110-117`: **UI enhancement improves information density.**

The new flex container with styled badges for both event counts and type counts improves the UI by:
1. Providing more context about the data being displayed
2. Using consistent visual styling with the rest of the application
3. Properly handling singular/plural text variations

The purple styling for types creates good visual distinction from the blue styling for events.

</details>
<details>
<summary>src/components/timeline/EventItem.tsx (2)</summary>

`52-54`: **Improved status checking robustness.**

Expanding the status check to include additional sources (`check_suite` and `check_run`) provides more comprehensive status reporting for events. This enhancement ensures statuses are captured from all possible sources in the event payload.

---

`99-99`: **Simplified property access.**

Removing the explicit `null` fallback simplifies the code while maintaining the same functionality. The optional chaining operator (`?.`) already ensures safe property access.

</details>
<details>
<summary>src/components/timeline/EventTimeline.tsx (1)</summary>

`123-129`: **Enhanced UI with group count indicator.**

The addition of a styled badge showing the number of event groups maintains consistency with the UI patterns used elsewhere in the application. This improves the information density and provides users with additional context about the timeline data.

The flexbox layout with proper spacing and the correctly implemented pluralization logic (`{relevantIds.length !== 1 ? 's' : ''}`) shows attention to detail.

</details>
<details>
<summary>src/components/select-data/RecentPullRequests.tsx (1)</summary>

`79-79`: **Looks good for layout stability.**
The `flex-shrink-0` class will help maintain icon visibility. No issues here.

</details>
<details>
<summary>src/steps/TimelineViewStep.tsx (3)</summary>

`3-3`: **New import addition is valid.**
Including `ExternalLink` from `lucide-react` is appropriate for external navigation icons.

---

`153-153`: **Helpful construction of a direct PR URL.**
Building the `githubPrUrl` string like this is straightforward and readable.

---

`185-201`: **Good UI addition for linking to the pull request on GitHub.**
Including `noopener noreferrer` in the anchor ensures security best practices. This is a user-friendly feature that exposes the GitHub PR directly.

</details>
<details>
<summary>src/components/timeline/MermaidDiagram.tsx (6)</summary>

`12-12`: **Use of `Readonly<MermaidDiagramProps>` is commendable.**
Ensures immutability of props within the component.

---

`19-39`: **Clear event title extraction.**
The `getEventTitle` helper covers a broad range of event payloads and provides a fallback string. It looks robust and straightforward.

---

`41-49`: **Concise retrieval of status from multiple payload fields.**
This approach is readable and covers various event contexts. No issues found.

---

`59-82`: **Well-structured grouping by type and ID.**
Falling back to `event.delivery_id` ensures each event is placed in some group. Implementation looks correct.

---

`132-144`: **Event message and status annotation look solid.**
Truncation is sensible for diagram clarity. The approach is well-executed.

---

`153-153`: **Mermaid configuration adjustments are appropriate.**
Increasing the actor margin and message width should improve the diagram’s readability.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T22:26:01Z
user: login: korbit-ai[bot]
body: ### Review by Korbit AI

###### Korbit automatically attempts to detect when you fix issues in new commits.

| Category | Issue | Fix Detected |
|----------|-------|----------|
| <sub>![Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub> | Unsafe Date Parsing ▹ [view]( https://github.com/guibranco/pull-request-ui/pull/96#discussion_r1988105621 ) ||
| <sub>![Performance](https://img.shields.io/badge/Performance-4f46e5)</sub> | Inefficient Actor Counting ▹ [view]( https://github.com/guibranco/pull-request-ui/pull/96#discussion_r1988105626 ) ||






<details>
  <summary>Files scanned</summary>
&#8203;



| File Path | Reviewed |
| ------------------ | -------------- |
| src/components/timeline/EventTimeline.tsx | ✅ |
| src/components/select-data/RecentPullRequests.tsx | ✅ |
| src/components/timeline/EventItem.tsx | ✅ |
| src/components/timeline/EventList.tsx | ✅ |
| src/steps/TimelineViewStep.tsx | ✅ |
| src/components/timeline/MermaidDiagram.tsx | ✅ |


> Explore our [documentation](https://docs.korbit.ai) to understand the languages and file types [we support](https://docs.korbit.ai/configuration/supported-languages) and the [files we ignore](https://docs.korbit.ai/configuration/ignoring-files).

</details>




> Need a new review? Comment `/korbit-review` on this PR and I'll review your latest changes.
>
> <details>
>   <summary>Korbit Guide: Usage and Customization</summary>
>
> ## Interacting with Korbit
> - You can manually ask Korbit to review your PR using the `/korbit-review` command in a comment at the root of your PR.
> - You can ask Korbit to generate a new PR description using the `/korbit-generate-pr-description` command in any comment on your PR.
> - Too many Korbit comments? I can resolve all my comment threads if you use the `/korbit-resolve` command in any comment on your PR.
> - On any given comment that Korbit raises on your pull request, you can have a discussion with Korbit by replying to the comment.
> - Help train Korbit to improve your reviews by giving a 👍 or 👎 on the comments Korbit posts.
>
> ## Customizing Korbit
> - Check out our [docs](https://docs.korbit.ai/configuration/organization-settings) on how you can make Korbit work best for you and your team.
> - Customize Korbit for your organization through the [Korbit Console](https://app.korbit.ai/23a4dc05-fc49-49db-bcd6-68b2dff127e6/settings).
>
>
> ### Current Korbit Configuration
>
>
>
> <details>
>   <summary>General Settings</summary>
> &#8203;
>
> | Setting          | Value       |
> | ---------------- | ----------- |
> | Review Schedule | Automatic excluding drafts |
> | Max Issue Count | 10 |
> | Automatic PR Descriptions | ✅ |
>
> </details>
>
>
>
> <details>
>   <summary>Issue Categories</summary>
> &#8203;
>
> | Category          | Enabled       |
> | ---------------- | ----------- |
> | Documentation | ✅ |
> | Logging | ✅ |
> | Error Handling | ✅ |
> | Readability | ✅ |
> | Design | ✅ |
> | Performance | ✅ |
> | Security | ✅ |
> | Functionality | ✅ |
>
> </details>
>
>
> ## Feedback and Support
> - [Tell us what you think of Korbit](https://rfn13ctpmt0.typeform.com/to/DOoeLyoN)
> - [Schedule a call with our team](https://meetings.salesloft.com/korbittechnologies/korbitai)
> - Email us @ support@korbit.ai
> </details>

> [!NOTE]
> Korbit Pro is free for open source projects 🎉
> ###### Looking to add Korbit to your team? Get started with a free 2 week trial [here](https://www.korbit.ai?utm_source=github_open_source_messaging)


state: COMMENTED
author_association: NONE
submitted_at: 2025-03-10T22:26:04Z

Review comments:
diff_hunk: @@ -35,75 +56,92 @@ export function MermaidDiagram({ events, isExpanded, onToggle }: MermaidDiagramP
     Note over GH,PR: No events found`;
     }
 
+    // Group events by their type and ID
+    const groupedEvents = events.reduce((acc, event) => {
+      const id = event.payload.issue?.id ||
+        event.payload.pull_request?.id ||
+        event.payload.check_run?.id ||
+        event.payload.workflow_run?.id ||
+        event.payload.workflow_job?.id ||
+        event.payload.release?.id ||
+        event.delivery_id;
+
+      const key = `${event.type}_${id}`;
+      
+      if (!acc[key]) {
+        acc[key] = [];
+      }
+      acc[key].push(event);
+      return acc;
+    }, {} as Record<string, Event[]>);
+
+    // Sort events within each group by date
+    Object.values(groupedEvents).forEach(group => {
+      group.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
+    });
+
     let diagram = 'sequenceDiagram\n';
path: src/components/timeline/MermaidDiagram.tsx
user: login: sourcery-ai[bot]
body: **issue (complexity):** Consider extracting the grouping/sorting logic and actor mapping into separate helper functions to simplify the `generateSequenceDiagram` function and improve readability by reducing its complexity and nesting levels, without altering its functionality or behavior..

Consider extracting the grouping/sorting logic and actor mapping out of `generateSequenceDiagram`. This will simplify the main function’s flow and improve readability without changing functionality.

For example, you might extract a helper to group and sort events:

```ts
const groupAndSortEvents = (events: Event[]): Record<string, Event[]> => {
  const grouped = events.reduce((acc, event) => {
    const id =
      event.payload.issue?.id ||
      event.payload.pull_request?.id ||
      event.payload.check_run?.id ||
      event.payload.workflow_run?.id ||
      event.payload.workflow_job?.id ||
      event.payload.release?.id ||
      event.delivery_id;
    const key = `${event.type}_${id}`;
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(event);
    return acc;
  }, {} as Record<string, Event[]>);

  Object.values(grouped).forEach(group =>
    group.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
  );
  return grouped;
};
```

Similarly, factor out actor mapping into its own helper:

```ts
const getTopActors = (events: Event[], limit = 5): { actorMap: Map<string, string>; topActors: string[] } => {
  const actorCounts = new Map<string, number>();
  events.forEach(event => {
    const sender = event.payload.sender?.login;
    if (sender) {
      actorCounts.set(sender, (actorCounts.get(sender) || 0) + 1);
    }
  });

  const topActors = Array.from(actorCounts.entries())
    .sort(([, countA], [, countB]) => countB - countA)
    .slice(0, limit)
    .map(([actor]) => actor);

  const actorMap = new Map<string, string>();
  let actorCounter = 0;
  topActors.forEach(actor => {
    actorMap.set(actor, `Actor${actorCounter++}`);
  });

  return { actorMap, topActors };
};
```

Then, in your `generateSequenceDiagram` function, call these helpers to build the diagram. This keeps each piece focused, reducing nesting and complexity while retaining all behavior.
created_at: 2025-03-10T22:22:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -35,75 +56,92 @@ export function MermaidDiagram({ events, isExpanded, onToggle }: MermaidDiagramP
     Note over GH,PR: No events found`;
     }
 
+    // Group events by their type and ID
+    const groupedEvents = events.reduce((acc, event) => {
+      const id = event.payload.issue?.id ||
+        event.payload.pull_request?.id ||
+        event.payload.check_run?.id ||
+        event.payload.workflow_run?.id ||
+        event.payload.workflow_job?.id ||
+        event.payload.release?.id ||
+        event.delivery_id;
+
+      const key = `${event.type}_${id}`;
+      
+      if (!acc[key]) {
+        acc[key] = [];
+      }
+      acc[key].push(event);
+      return acc;
+    }, {} as Record<string, Event[]>);
+
+    // Sort events within each group by date
+    Object.values(groupedEvents).forEach(group => {
+      group.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
path: src/components/timeline/MermaidDiagram.tsx
user: login: korbit-ai[bot]
body: ### Unsafe Date Parsing <sub>![category Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>

<details>
  <summary>Tell me more</summary>

###### What is the issue?
Direct usage of Date.getTime() without validating the date string could cause runtime errors if the date string is invalid.

###### Why this matters
Invalid date strings will result in NaN when parsed, leading to inconsistent sorting behavior or potential application crashes.

###### Suggested change ∙ *Feature Preview*
Add date validation and fallback handling:
```typescript
const getValidDate = (dateStr: string): number => {
  const date = new Date(dateStr);
  return isNaN(date.getTime()) ? 0 : date.getTime();
};

group.sort((a, b) => getValidDate(a.date) - getValidDate(b.date));
```


</details>

<sub>

[![Report a problem with this comment](https://img.shields.io/badge/Report%20a%20problem%20with%20this%20comment-gray.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmNWVjMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS10cmlhbmdsZS1hbGVydCI+PHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIi8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4=)](https://app.korbit.ai/feedback/23a4dc05-fc49-49db-bcd6-68b2dff127e6/0904d3a9-5237-46e3-8c2f-5a306c948b87?suggestedFixEnabled=true)

💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>

<!--- korbi internal id:3be2c208-2f9a-4675-9a10-3a5d4d698b90 -->

created_at: 2025-03-10T22:26:04Z
author_association: NONE
diff_hunk: @@ -35,75 +56,92 @@
     Note over GH,PR: No events found`;
     }
 
+    // Group events by their type and ID
+    const groupedEvents = events.reduce((acc, event) => {
+      const id = event.payload.issue?.id ||
+        event.payload.pull_request?.id ||
+        event.payload.check_run?.id ||
+        event.payload.workflow_run?.id ||
+        event.payload.workflow_job?.id ||
+        event.payload.release?.id ||
+        event.delivery_id;
+
+      const key = `${event.type}_${id}`;
+      
+      if (!acc[key]) {
+        acc[key] = [];
+      }
+      acc[key].push(event);
+      return acc;
+    }, {} as Record<string, Event[]>);
+
+    // Sort events within each group by date
+    Object.values(groupedEvents).forEach(group => {
+      group.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
+    });
+
     let diagram = 'sequenceDiagram\n';
-    
-    // Add core participants
     diagram += '    participant GH as GitHub\n';
     diagram += '    participant PR as Pull Request\n';
-    
-    // Track unique actors to avoid duplicates
-    const actors = new Set<string>();
-    
-    // First pass: collect all actors (limited to first 5 unique actors)
-    let actorCount = 0;
-    for (const event of events) {
-      const sender = event.payload?.sender?.login;
-      if (sender && !actors.has(sender) && actorCount < 5) {
-        const safeSender = sanitizeParticipant(sender);
-        actors.add(sender);
-        diagram += `    participant ${safeSender} as ${truncateText(sender, 15)}\n`;
-        actorCount++;
+
+    // Create a map for actors to ensure unique IDs
+    const actorMap = new Map<string, string>();
+    let actorCounter = 0;
+
+    const getActorId = (name: string): string => {
+      if (!actorMap.has(name)) {
+        actorMap.set(name, `Actor${actorCounter++}`);
       }
-    }
+      return actorMap.get(name)!;
+    };
 
-    // Second pass: add events with proper timing
-    let lastEventTime = '';
-    events.forEach((event, index) => {
-      const eventTime = formatEventTime(event.date);
-      const sender = event.payload?.sender?.login && actors.has(event.payload.sender.login)
-        ? sanitizeParticipant(event.payload.sender.login)
-        : 'GH';
-      
-      // Truncate action text
-      const action = truncateText(
-        event.action ? `${event.type}:${event.action}` : event.type,
-        25
-      );
-      
-      diagram += `    ${sender}->PR: ${action}\n`;
-      
-      // Only add time note if it's different from the last one
-      if (eventTime !== lastEventTime) {
-        diagram += `    Note over PR: ${eventTime}\n`;
-        lastEventTime = eventTime;
+    // Add actors (limit to 5 most active)
+    const actorCounts = new Map<string, number>();
+    events.forEach(event => {
+      const sender = event.payload.sender?.login;
+      if (sender) {
+        actorCounts.set(sender, (actorCounts.get(sender) || 0) + 1);
       }
-      
-      // Add minimal payload information
-      if (event.payload) {
-        const notes = [];
-        if (event.payload.comment?.body) {
-          notes.push('Comment');
-        }
-        if (event.payload.review?.state) {
-          notes.push(event.payload.review.state);
-        }
-        if (event.payload.check_suite?.conclusion) {
-          notes.push(event.payload.check_suite.conclusion);
+    });
path: src/components/timeline/MermaidDiagram.tsx
user: login: korbit-ai[bot]
body: ### Inefficient Actor Counting <sub>![category Performance](https://img.shields.io/badge/Performance-4f46e5)</sub>

<details>
  <summary>Tell me more</summary>

###### What is the issue?
Using forEach for counting actor occurrences is less efficient than using reduce, which would avoid multiple Map operations.

###### Why this matters
Multiple Map operations (get and set) for each event increase computational overhead, especially with large event sets.

###### Suggested change ∙ *Feature Preview*
Replace forEach with reduce to minimize Map operations:
```typescript
const actorCounts = events.reduce((counts, event) => {
  const sender = event.payload.sender?.login;
  if (sender) {
    counts.set(sender, (counts.get(sender) || 0) + 1);
  }
  return counts;
}, new Map<string, number>());
```


</details>

<sub>

[![Report a problem with this comment](https://img.shields.io/badge/Report%20a%20problem%20with%20this%20comment-gray.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmNWVjMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS10cmlhbmdsZS1hbGVydCI+PHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIi8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4=)](https://app.korbit.ai/feedback/23a4dc05-fc49-49db-bcd6-68b2dff127e6/5c38eb1d-088f-441a-970e-40831a1d99a8?suggestedFixEnabled=true)

💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>

<!--- korbi internal id:804fd0a5-adc8-4c46-8aee-f42a7501b403 -->

created_at: 2025-03-10T22:26:04Z
author_association: NONE

PR comments:
user: login: sourcery-ai[bot]
created_at: 2025-03-10T22:21:42Z
body: <!-- Generated by sourcery-ai[bot]: start review_guide -->

## Reviewer's Guide by Sourcery

This pull request enhances the UI by improving the Mermaid diagram generation, adding a link to view the pull request on GitHub, enhancing the display of recent pull requests, and improving event status handling.

#### Sequence diagram for event processing

```mermaid
sequenceDiagram
    participant GH as GitHub
    participant PR as Pull Request
    participant Actor0 as user1
    participant Actor1 as user2

    Note over GH,PR: Issue #123
    Actor0->>PR: IssuesEvent:opened
    Note right of PR: null

    Note over GH,PR: PR #456
    Actor1->>PR: PullRequestEvent:closed
    Note right of PR: success

    Note over GH,PR: Check: MyCheck
    GH->>PR: CheckRunEvent:completed
    Note right of PR: success

    Note over GH,PR: Workflow: MyWorkflow
    GH->>PR: WorkflowRunEvent:completed
    Note right of PR: success

    Note over GH,PR: Job: MyJob
    GH->>PR: WorkflowJobEvent:completed
    Note right of PR: success

    Note over GH,PR: Release: v1.0.0
    GH->>PR: ReleaseEvent:published
    Note right of PR: null
```

### File-Level Changes

| Change | Details | Files |
| ------ | ------- | ----- |
| Improved Mermaid diagram generation to group and sort events, limit actors, and display event statuses. | <ul><li>Grouped events by type and ID to consolidate related events.</li><li>Sorted events within each group by date for chronological order.</li><li>Limited the number of displayed actors to the top 5 most active to reduce diagram clutter.</li><li>Added event statuses to the diagram to provide more context.</li><li>Implemented a function to extract event titles for better labeling.</li><li>Used a map to ensure unique actor IDs for the diagram.</li><li>Truncated long text strings to prevent overflow issues.</li><li>Refactored the diagram generation logic for better readability and maintainability.</li></ul> | `src/components/timeline/MermaidDiagram.tsx` |
| Added a link to view the pull request on GitHub in the timeline view. | <ul><li>Introduced a `githubPrUrl` variable to construct the URL.</li><li>Added an external link icon and text to the pull request information section.</li><li>Implemented styling for the link to match the existing UI.</li></ul> | `src/steps/TimelineViewStep.tsx` |
| Enhanced the display of recent pull requests with improved layout and information. | <ul><li>Added `flex-shrink-0` to the pull request icon to prevent it from shrinking.</li><li>Implemented a flex column layout for smaller screens and a flex row layout for larger screens.</li><li>Added `break-all` to the repository name to prevent overflow.</li><li>Truncated the repository name and sender name to prevent long text from breaking the layout.</li><li>Added `flex-shrink-0` to the avatar to prevent it from shrinking.</li></ul> | `src/components/select-data/RecentPullRequests.tsx` |
| Improved the event list display with total events and types counts. | <ul><li>Added a display of the total number of event types.</li><li>Added styling to the event and type counts to improve visual appeal.</li><li>Used `sortedTypeEntries.length` to get the total number of event types.</li></ul> | `src/components/timeline/EventList.tsx` |
| Enhanced the event timeline component with group counts. | <ul><li>Added a display of the number of event groups.</li><li>Added styling to the group count to improve visual appeal.</li></ul> | `src/components/timeline/EventTimeline.tsx` |
| Improved event status handling in the EventItem component. | <ul><li>Added `check_suite` and `check_run` status to the event status check.</li></ul> | `src/components/timeline/EventItem.tsx` |

---

<details>
<summary>Tips and commands</summary>

#### Interacting with Sourcery

- **Trigger a new review:** Comment `@sourcery-ai review` on the pull request.
- **Continue discussions:** Reply directly to Sourcery's review comments.
- **Generate a GitHub issue from a review comment:** Ask Sourcery to create an
  issue from a review comment by replying to it. You can also reply to a
  review comment with `@sourcery-ai issue` to create an issue from it.
- **Generate a pull request title:** Write `@sourcery-ai` anywhere in the pull
  request title to generate a title at any time. You can also comment
  `@sourcery-ai title` on the pull request to (re-)generate the title at any time.
- **Generate a pull request summary:** Write `@sourcery-ai summary` anywhere in
  the pull request body to generate a PR summary at any time exactly where you
  want it. You can also comment `@sourcery-ai summary` on the pull request to
  (re-)generate the summary at any time.
- **Generate reviewer's guide:** Comment `@sourcery-ai guide` on the pull
  request to (re-)generate the reviewer's guide at any time.
- **Resolve all Sourcery comments:** Comment `@sourcery-ai resolve` on the
  pull request to resolve all Sourcery comments. Useful if you've already
  addressed all the comments and don't want to see them anymore.
- **Dismiss all Sourcery reviews:** Comment `@sourcery-ai dismiss` on the pull
  request to dismiss all existing Sourcery reviews. Especially useful if you
  want to start fresh with a new review - don't forget to comment
  `@sourcery-ai review` to trigger a new review!
- **Generate a plan of action for an issue:** Comment `@sourcery-ai plan` on
  an issue to generate a plan of action for it.

#### Customizing Your Experience

Access your [dashboard](https://app.sourcery.ai) to:
- Enable or disable review features such as the Sourcery-generated pull request
  summary, the reviewer's guide, and others.
- Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.

#### Getting Help

- [Contact our support team](mailto:support@sourcery.ai) for questions or feedback.
- Visit our [documentation](https://docs.sourcery.ai) for detailed guides and information.
- Keep in touch with the Sourcery team by following us on [X/Twitter](https://x.com/SourceryAI), [LinkedIn](https://www.linkedin.com/company/sourcery-ai/) or [GitHub](https://github.com/sourcery-ai).

</details>

<!-- Generated by sourcery-ai[bot]: end review_guide -->
author_association: CONTRIBUTOR
user: login: coderabbitai[bot]
created_at: 2025-03-10T22:21:46Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
This pull request updates several components across the project. It bumps the `typescript-eslint` version in **package.json**. In the UI components, styling and layout adjustments have been made for pull request displays and timeline views. The timeline components now include expanded event status checks, new variables for counting event types and groups, while the sequence diagram logic in the MermaidDiagram component has been refactored with new helper functions and grouping logic. Additionally, a GitHub pull request URL has been integrated into the TimelineViewStep component.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `package.json` | Updated `"typescript-eslint"` version from `^8.3.0` to `^8.26.1` in devDependencies. |
| `src/.../RecentPullRequests.tsx`, `src/.../TimelineViewStep.tsx` | Adjusted styling and layout for pull request displays; added a GitHub PR link in TimelineViewStep and updated spacing for better responsiveness. |
| `src/.../EventItem.tsx`, `src/.../EventList.tsx`, `src/.../EventTimeline.tsx` | Enhanced timeline components by expanding event status checks, introducing new count variables, and updating JSX structures to display event types and group counts. |
| `src/.../MermaidDiagram.tsx` | Refactored diagram rendering: updated prop types to readonly, removed obsolete functions, added helper functions for event titles and statuses, improved event grouping, sorting, and actor counting, with updated configuration settings. |

## Sequence Diagram(s)
```mermaid
sequenceDiagram
    participant U as User
    participant TV as TimelineViewStep
    participant EL as EventList
    participant MD as MermaidDiagram
    U->>TV: Open Timeline View
    TV->>TV: Construct GitHub PR URL
    TV->>EL: Render event list with count details
    EL->>TV: Return event data
    TV->>MD: Render sequence diagram
    MD->>MD: Process events (group, sort, generate titles/status)
    MD->>TV: Diagram ready
    TV->>U: Display updated timeline view
```

## Suggested labels
`enhancement`, `size/L`, `Review effort [1-5]: 4`, `documentation`

## Suggested reviewers
- gstraccini
- sourcery-ai

## Poem
> I’m a bunny with code in my paws,  
> Hopping through updates without a pause.  
> New features and layouts make me twirl,  
> In a world of changes, I give a happy whirl.  
> With a twitch of my nose and a skip so free,  
> I celebrate these updates — oh joy for me!  
> 🐇✨

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=guibranco/pull-request-ui&utm_content=96):

- 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](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) 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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: socket-security[bot]
created_at: 2025-03-10T22:22:02Z
body: <!-- overview-comment -->
**Updated and removed dependencies detected.** Learn more about [Socket for GitHub ↗︎](https://socket.dev?utm_medium=gh)


| Package | New capabilities | Transitives | Size | Publisher |
|:--- |:--- |:--- |:--- |:--- |
| [npm/typescript-eslint@8.8.1](https://socket.dev/npm/package/typescript-eslint/overview/8.8.1) ➜ [8.26.1](https://socket.dev/npm/package/typescript-eslint/overview/8.26.1) | Transitive: <a href="https://socket.dev/npm/package/typescript-eslint/alerts/8.26.1?tab=dependencies&alert_name=envVars">environment</a>, <a href="https://socket.dev/npm/package/typescript-eslint/alerts/8.26.1?tab=dependencies&alert_name=filesystemAccess">filesystem</a> | <a href="https://socket.dev/npm/package/typescript-eslint/dependencies/8.26.1">`+39`</a> | 3.66 MB |  |



[**View full report↗︎**](https://socket.dev/dashboard/org/guibranco/diff/bec9dc8b-c38a-4ba0-9d19-a07fcac539c5/00104395-2b5b-4c4c-9ea3-5ac2347c77aa)
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-03-10T22:22:05Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=guibranco_pull-request-ui&pullRequest=96) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=guibranco_pull-request-ui&pullRequest=96&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=guibranco_pull-request-ui&pullRequest=96&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=guibranco_pull-request-ui&pullRequest=96&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=guibranco_pull-request-ui&pullRequest=96&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=guibranco_pull-request-ui&pullRequest=96&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=guibranco_pull-request-ui&pullRequest=96)


author_association: NONE
user: login: penify-dev[bot]
created_at: 2025-03-10T22:22:12Z
body: ## PR Review 🔍

<table>
<tr>
<tr><td> ⏱️&nbsp;<strong>Estimated&nbsp;effort&nbsp;to&nbsp;review [1-5]</strong></td><td>

4, because the changes span multiple files and involve UI enhancements, event handling improvements, and dependency updates. The complexity of ensuring responsiveness and layout consistency across components adds to the review effort.


</td></tr>
<tr><td> 🧪&nbsp;<strong>Relevant tests</strong></td><td>

No


</td></tr>
<tr><td rowspan=2> ⚡&nbsp;<strong>Possible issues</strong></td>
<td>

<strong>UI Consistency:</strong> Ensure that the new UI components maintain visual consistency with existing components.</td></tr>
<tr>
<td>

<strong>Responsiveness:</strong> Verify that the changes do not negatively impact the responsiveness on various screen sizes.</td></tr>
<tr><td> 🔒&nbsp;<strong>Security concerns</strong></td><td>

No

</td></tr>
</table>

author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-10T22:22:17Z
body: <!-- add-pr-comment:secrets-result -->

**Infisical secrets check:** ✅ No secrets leaked!

<details>
  <summary>💻 Scan logs</summary>

  ```txt
  10:22PM INF scanning for exposed secrets...
10:22PM INF 88 commits scanned.
10:22PM INF scan completed in 298ms
10:22PM INF no leaks found

  ```
</details>
author_association: NONE
user: login: penify-dev[bot]
created_at: 2025-03-10T22:22:30Z
body: ## PR Code Suggestions ✨

<table><thead><tr><td>Category</td><td align=left>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </td><td align=center>Score</td></tr><tbody><tr><td rowspan=2><strong>Performance</strong></td>
<td>



<details><summary>Verify memoization of the diagram generation function to optimize performance</summary>

___


**Ensure that the <code>generateSequenceDiagram</code> function is memoized correctly to prevent <br>unnecessary recalculations when dependencies have not changed.**

[src/components/timeline/MermaidDiagram.tsx [144-145]](https://github.com/guibranco/pull-request-ui/pull/96/files#diff-f35022bab712420e0084d132b301dae3eea5d937996b10f8c0584551e1d3c64bR144-R145)

```diff
 const generateSequenceDiagram = useCallback(() => {
+  // function logic
+}, [events, isExpanded]); // Ensure all relevant dependencies are included
 
```



<details><summary><b>Suggestion importance[1-10]: 9</b></summary>

Why: Correct memoization is crucial for performance, especially in a function that generates a diagram based on potentially large datasets. This suggestion addresses an important optimization.

</details></details></td><td align=center>9

</td></tr><tr><td>



<details><summary>Use a Map for grouping events to enhance performance</summary>

___


**Optimize the <code>groupedEvents</code> logic by using a <code>Map</code> instead of an object to improve <br>performance and memory usage when grouping events.**

[src/components/timeline/MermaidDiagram.tsx [60-76]](https://github.com/guibranco/pull-request-ui/pull/96/files#diff-f35022bab712420e0084d132b301dae3eea5d937996b10f8c0584551e1d3c64bR60-R76)

```diff
-const groupedEvents = events.reduce((acc, event) => {
+const groupedEvents = new Map<string, Event[]>();
+events.forEach(event => {
   const id = event.payload.issue?.id ||
     event.payload.pull_request?.id ||
     event.payload.check_run?.id ||
     event.payload.workflow_run?.id ||
     event.payload.workflow_job?.id ||
     event.payload.release?.id ||
     event.delivery_id;
   const key = `${event.type}_${id}`;
-  if (!acc[key]) {
-    acc[key] = [];
+  if (!groupedEvents.has(key)) {
+    groupedEvents.set(key, []);
   }
-  acc[key].push(event);
-  return acc;
-}, {} as Record<string, Event[]>);
+  groupedEvents.get(key)!.push(event);
+});
 
```



<details><summary><b>Suggestion importance[1-10]: 7</b></summary>

Why: Using a `Map` can enhance performance and memory usage, especially with a large number of events. This is a solid performance improvement.


</details></details></td><td align=center>7

</td></tr><tr><td rowspan=1><strong>Possible bug</strong></td>
<td>



<details><summary>Handle undefined event status cases to avoid returning null</summary>

___


**Ensure that the <code>getEventStatus</code> function handles cases where none of the properties are <br>defined, to avoid returning <code>null</code> which may lead to unexpected behavior in the diagram.**

[src/components/timeline/MermaidDiagram.tsx [41-48]](https://github.com/guibranco/pull-request-ui/pull/96/files#diff-f35022bab712420e0084d132b301dae3eea5d937996b10f8c0584551e1d3c64bR41-R48)

```diff
 return event.payload.check_run?.conclusion ||
   event.payload.check_suite?.conclusion ||
   event.payload.status?.state ||
   event.payload.review?.state ||
   event.payload.workflow_run?.conclusion ||
   event.payload.workflow_job?.conclusion ||
-  null;
+  'Unknown Status';
 
```



<details><summary><b>Suggestion importance[1-10]: 8</b></summary>

Why: The suggestion addresses a potential bug where returning `null` could lead to unexpected behavior in the diagram. Providing a default status improves robustness.


</details></details></td><td align=center>8

</td></tr><tr><td rowspan=1><strong>Maintainability</strong></td>
<td>



<details><summary>Rename the function to better reflect its purpose</summary>

___


**Consider using a more descriptive name for the <code>getEventTitle</code> function to clarify its <br>purpose, as it currently may not convey the specific context of the event title being <br>generated.**

[src/components/timeline/MermaidDiagram.tsx [19]](https://github.com/guibranco/pull-request-ui/pull/96/files#diff-f35022bab712420e0084d132b301dae3eea5d937996b10f8c0584551e1d3c64bR19-R19)

```diff
-const getEventTitle = (event: Event): string => {
+const generateEventTitleFromPayload = (event: Event): string => {
 
```



<details><summary><b>Suggestion importance[1-10]: 6</b></summary>

Why: While renaming the function can improve readability, the current name is not misleading. This is a minor improvement in maintainability.


</details></details></td><td align=center>6

</td></tr></tr></tbody></table>
author_association: CONTRIBUTOR