feat: Track application read receipts per user - #266
Conversation
Implement a per-user `application_view` system to track opened applications. Replaces the `new` stage count with a personalized "unviewed" metric to better highlight applications that require attention. Displays an unread-style dot for unopened candidates in the pipeline and tables, and logs views automatically when an application detail is opened.
|
🚅 Deployed to the reqcore-pr-266 environment in applirank
|
|
Warning Review limit reached
Next review available in: 5 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThe change adds per-user application view receipts, backfills existing applications, exposes viewed timestamps and unviewed job counts, updates dashboard unread indicators, and improves Railway preview environment linking and URL detection. ChangesApplication view tracking
Preview environment handling
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant Recruiter
participant Dashboard
participant ApplicationsAPI
participant ApplicationViews
participant Database
Recruiter->>Dashboard: open application
Dashboard->>ApplicationsAPI: request application details
ApplicationsAPI->>Database: load application
ApplicationsAPI->>ApplicationViews: recordApplicationView
ApplicationViews->>Database: upsert application_view
ApplicationsAPI-->>Dashboard: return application details
Dashboard->>Dashboard: mark application viewed locally
Dashboard-->>Recruiter: render viewed application state
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/components/ApplicationsList.vue`:
- Around line 420-427: Update the initialApplicationId handling near
selectedApplicationId so it also calls markViewedLocally for the deep-linked
application, matching the behavior of openApplication. Keep the existing direct
selection behavior and useApplicationViews integration unchanged for normal
application openings.
In `@server/api/applications/`[id].get.ts:
- Around line 70-74: Update the application-detail handler around
recordApplicationView so the non-critical receipt write is scheduled without
awaiting it during response construction. Preserve the receipt attempt while
ensuring database latency or failure cannot delay or block the detail response.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: e638561a-f766-457d-a622-b01faa4865e5
📒 Files selected for processing (14)
CHANGELOG.mdapp/components/ApplicationsList.vueapp/composables/useApplicationViews.tsapp/pages/dashboard/jobs/[id]/index.vueapp/pages/dashboard/jobs/index.vueserver/api/applications/[id].get.tsserver/api/applications/index.get.tsserver/api/jobs/index.get.tsserver/database/migrations/0065_application_view.sqlserver/database/migrations/meta/0065_snapshot.jsonserver/database/migrations/meta/_journal.jsonserver/database/schema/app.tsserver/utils/applicationViews.tstests/unit/application-views.test.ts
| // Opening the drawer renders <ApplicationDetail>, whose detail request logs the | ||
| // view server-side; mirroring it here drops the marker straight away. | ||
| const { markViewedLocally, isViewed } = useApplicationViews() | ||
|
|
||
| function openApplication(applicationId: string) { | ||
| selectedApplicationId.value = applicationId | ||
| markViewedLocally(applicationId) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Mark deep-linked applications as viewed locally.
When initialApplicationId is set, line 418 assigns the ID directly to selectedApplicationId. It does not call markViewedLocally. The opened application's existing list row can keep its unread indicator until a refetch.
Proposed fix
const { markViewedLocally, isViewed } = useApplicationViews()
+if (selectedApplicationId.value) markViewedLocally(selectedApplicationId.value)
function openApplication(applicationId: string) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Opening the drawer renders <ApplicationDetail>, whose detail request logs the | |
| // view server-side; mirroring it here drops the marker straight away. | |
| const { markViewedLocally, isViewed } = useApplicationViews() | |
| function openApplication(applicationId: string) { | |
| selectedApplicationId.value = applicationId | |
| markViewedLocally(applicationId) | |
| } | |
| // Opening the drawer renders <ApplicationDetail>, whose detail request logs the | |
| // view server-side; mirroring it here drops the marker straight away. | |
| const { markViewedLocally, isViewed } = useApplicationViews() | |
| if (selectedApplicationId.value) markViewedLocally(selectedApplicationId.value) | |
| function openApplication(applicationId: string) { | |
| selectedApplicationId.value = applicationId | |
| markViewedLocally(applicationId) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/components/ApplicationsList.vue` around lines 420 - 427, Update the
initialApplicationId handling near selectedApplicationId so it also calls
markViewedLocally for the deep-linked application, matching the behavior of
openApplication. Keep the existing direct selection behavior and
useApplicationViews integration unchanged for normal application openings.
| await recordApplicationView({ | ||
| organizationId: orgId, | ||
| applicationId: result.id, | ||
| userId: session.user.id, | ||
| }) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Do not block the detail response on the receipt write.
await recordApplicationView(...) keeps the application-detail request pending until the database write completes or fails. A slow database can therefore prevent a recruiter from opening an application.
Schedule this non-critical write after the response, or use a bounded background task mechanism that preserves the receipt attempt without extending request latency.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@server/api/applications/`[id].get.ts around lines 70 - 74, Update the
application-detail handler around recordApplicationView so the non-critical
receipt write is scheduled without awaiting it during response construction.
Preserve the receipt attempt while ensuring database latency or failure cannot
delay or block the detail response.
- Enhance workflow to support custom environment name prefixes. - Add configuration validation and explicit debugging for missing envs. - Force a redeploy when a new domain is created so the app recognizes it. - Update `isStaleInheritedPreviewUrl` to use `RAILWAY_GIT_PR_NUMBER` for more reliable detection in branched environments.
Perform the link operation during the retry loop to confirm environment accessibility and eliminate the redundant link step later in the workflow.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/railway-pr-domain.yml:
- Around line 66-67: Update the “Install Railway CLI” workflow step to install
an exact pinned `@railway/cli` version instead of the unversioned package, using
the specified version or another explicitly reviewed version.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 07f4701f-f701-4c10-868b-8d4cde818027
📒 Files selected for processing (3)
.github/workflows/railway-pr-domain.ymlserver/utils/auth.tstests/unit/preview-auth-url.test.ts
Railway clones the production service's Railway-generated domain into each new PR environment, so creating one from CI is unnecessary. Document the requirement that production keep a service domain, not just custom domains. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The receipt is written from GET /api/applications/:id, so demo-guard never sees it and the read-only demo would accumulate rows. It also broke the demo: every visitor shares demo@reqcore.com, so one visitor opening a candidate marked it read for everyone after them. Skip recording for the demo org, silently — the visitor asked for nothing, so a read-only error would be noise. A follow-up migration clears the rows 0065's backfill already wrote. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Implement a per-user
application_viewsystem to track opened applications.Replaces the
newstage count with a personalized "unviewed" metric to better highlight applications that require attention. Displays an unread-style dot for unopened candidates in the pipeline and tables, and logs views automatically when an application detail is opened.Summary
Type of change
Validation
DCO
Signed-off-by) viagit commit -sSummary by CodeRabbit