Skip to content

[Dashboard] Adds app store Descriptions and images#1041

Merged
madster456 merged 27 commits intodevfrom
dashboard/apps/storeDesc
Dec 4, 2025
Merged

[Dashboard] Adds app store Descriptions and images#1041
madster456 merged 27 commits intodevfrom
dashboard/apps/storeDesc

Conversation

@madster456
Copy link
Copy Markdown
Collaborator

@madster456 madster456 commented Dec 3, 2025

Summary by CodeRabbit

  • New Features

    • Interactive screenshot gallery with clickable thumbnails and left/right navigation
    • Full-screen screenshot preview modal with keyboard support (arrow keys, Escape)
    • App tiles are now clickable to open app details
  • Content

    • Enriched app store descriptions and expanded screenshot sets for multiple apps
  • Style

    • Updated dialog and layout styling for improved visual hierarchy and responsive behavior

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


Note

Adds rich app store descriptions and screenshots across apps, plus a full-screen screenshot preview with keyboard navigation and minor dialog layout tweaks.

  • UI/UX
    • Interactive Preview: Full-screen screenshot preview modal with arrow/Escape key support in components/app-store-entry.tsx.
    • Layout: Makes DialogContent in .../@modal/(.)apps/[appId]/page-client.tsx a flex column container.
  • Content & Data
    • App Descriptions: Populates storeDescription for multiple apps in lib/apps-frontend.tsx.
    • Screenshots:
      • Adds getScreenshots helper to generate image paths.
      • Expands screenshots for numerous apps (e.g., authentication, teams, rbac, payments, emails, data-vault, webhooks, vercel).
      • Updates AppFrontend.screenshots type to (string | StaticImageData)[].
    • Removes fallback copy; always renders storeDescription in AppStoreEntry.

Written by Cursor Bugbot for commit b5e3b8a. This will update automatically on new commits. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Dec 3, 2025

Skipped: No reviewable files found.

@vercel
Copy link
Copy Markdown

vercel Bot commented Dec 3, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
stack-backend Ready Ready Preview Comment Dec 4, 2025 6:48pm
stack-dashboard Ready Ready Preview Comment Dec 4, 2025 6:48pm
stack-demo Ready Ready Preview Comment Dec 4, 2025 6:48pm
stack-docs Ready Ready Preview Comment Dec 4, 2025 6:48pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 3, 2025

Caution

Review failed

The pull request is closed.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a thumbnail gallery with fullscreen preview and keyboard navigation; changes screenshot data to accept StaticImageData or string and enriches descriptions; converts a dialog container to a vertical flex layout; makes app tiles clickable; and refactors/memoizes prefetch components and UrlPrefetcher.

Changes

Cohort / File(s) Summary
Modal Layout & Styling
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/@modal/(.)apps/[appId]/page-client.tsx
Adds flex flex-col to DialogContent, converting the dialog container to a vertical flex layout (styling-only).
Interactive Screenshot Gallery & Preview
apps/dashboard/src/components/app-store-entry.tsx
Replaces static description area with a horizontal thumbnail scroller and clickable thumbnails, adds preview modal (Dialog, DialogContent, DialogTitle), preview state (previewIndex), keyboard navigation (ArrowLeft/ArrowRight/Escape), prev/next controls, close button, image counter, scroll helpers, and updates titleComponent to TitleComponent = "h1".
Screenshot Types & Descriptions
apps/dashboard/src/lib/apps-frontend.tsx
Adds getScreenshots() helper, changes AppFrontend.screenshots type to `(string
Clickable App Tiles
apps/dashboard/src/components/app-square.tsx
Uses useProjectId() to compute appDetailsPath, wraps tile in a Link to make the tile clickable, moves hover actions to a separate row, and preserves enable/disable flows.
Prefetch Refactor (components)
apps/dashboard/src/lib/prefetch/hook-prefetcher.tsx
Extracts memoized PrefetchMany, PrefetchCallback, and PrefetchCallbackInner components, wraps callbacks in ErrorBoundary/Suspense, and simplifies HookPrefetcher to render the new components while preserving global prefetch counting.
UrlPrefetcher Memoization
apps/dashboard/src/lib/prefetch/url-prefetcher.tsx
Wraps UrlPrefetcher with React.memo, uses useMemo for callback creation, and changes export from a plain function to a memoized const.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Thumbs as Thumbnails/Scroller
    participant AppStore as AppStoreEntry
    participant Modal as Preview Modal
    participant Keyboard as Keyboard Events

    User->>Thumbs: click thumbnail (index)
    Thumbs->>AppStore: onClick(index)
    AppStore->>AppStore: set previewIndex, open Dialog
    AppStore->>Modal: render image at previewIndex

    User->>Keyboard: press ArrowRight / ArrowLeft / Escape
    Keyboard->>AppStore: navigatePreview(+1/-1) or close
    AppStore->>Modal: update image or close Dialog

    User->>AppStore: click next/prev buttons
    AppStore->>AppStore: update previewIndex
    AppStore->>Modal: re-render image

    User->>AppStore: click thumbnail scroll controls
    AppStore->>Thumbs: programmatic scroll
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay attention to keyboard event lifecycle and accessibility in apps/dashboard/src/components/app-store-entry.tsx.
  • Verify the StaticImageData typing and downstream usage in apps/dashboard/src/lib/apps-frontend.tsx.
  • Inspect global counter and error handling in apps/dashboard/src/lib/prefetch/hook-prefetcher.tsx.
  • Confirm memoization in apps/dashboard/src/lib/prefetch/url-prefetcher.tsx preserves intended render/caching semantics.
  • Validate routing via useProjectId() and Link behavior in apps/dashboard/src/components/app-square.tsx.

Possibly related PRs

Poem

🐰
I hopped along the thumbnail row,
Pushed a pic — the modal stole the show.
Arrows dance left, arrows dance right,
Screenshots glow in soft, moonlight.
Tiny paws cheer: preview delight! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the primary changes: adding app store descriptions and images to the dashboard.
Description check ✅ Passed The description is comprehensive, well-structured with clear sections (UI/UX, Content & Data), and provides sufficient detail about all major changes made in the pull request.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2076b2 and fa8b395.

📒 Files selected for processing (1)
  • apps/dashboard/src/components/app-square.tsx (4 hunks)

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

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/dashboard/src/components/app-store-entry.tsx (1)

45-62: Consider removing redundant Escape key handling.

The Dialog component already handles Escape key via onOpenChange (line 162), so the manual Escape handling at line 55-57 is redundant. The Dialog's built-in behavior is sufficient.

Apply this diff to simplify the keyboard handler:

   useEffect(() => {
     if (previewIndex === null) return;
 
     const handleKeyDown = (e: KeyboardEvent) => {
       if (e.key === 'ArrowLeft') {
         e.preventDefault();
         navigatePreview('prev');
       } else if (e.key === 'ArrowRight') {
         e.preventDefault();
         navigatePreview('next');
-      } else if (e.key === 'Escape') {
-        setPreviewIndex(null);
       }
     };
 
     window.addEventListener('keydown', handleKeyDown);
     return () => window.removeEventListener('keydown', handleKeyDown);
   }, [previewIndex, navigatePreview]);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8e78cf and d2ca747.

⛔ Files ignored due to path filters (38)
  • apps/dashboard/public/storeDesc-api-keys-1.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-auth-1.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-auth-2.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-auth-3.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-auth-4.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-auth-5.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-auth-6.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-data-vault-1.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-data-vault-2.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-data-vault-3.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-data-vault-4.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-emails-1.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-emails-2.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-emails-3.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-emails-4.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-emails-5.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-emails-6.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-emails-7.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-emails-8.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-payments-1.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-payments-2.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-payments-3.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-payments-4.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-payments-5.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-payments-6.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-payments-7.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-rbac-1.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-rbac-2.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-rbac-3.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-rbac-4.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-teams-1.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-teams-2.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-teams-3.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-teams-4.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-vercel-1.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-vercel-2.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-webhooks-1.png is excluded by !**/*.png
  • apps/dashboard/public/storeDesc-webhooks-2.png is excluded by !**/*.png
📒 Files selected for processing (3)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/@modal/(.)apps/[appId]/page-client.tsx (1 hunks)
  • apps/dashboard/src/components/app-store-entry.tsx (4 hunks)
  • apps/dashboard/src/lib/apps-frontend.tsx (10 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: For blocking alerts and errors, never use toast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast. Don't delay the action with a pre-transition; apply transitions after the action instead
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use runAsynchronously or runAsynchronouslyWithAlert instead for asynchronous error handling
When creating hover transitions, avoid hover-enter transitions and just use hover-exit transitions (e.g., transition-colors hover:transition-none)
Use ES6 maps instead of records wherever possible

Files:

  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/@modal/(.)apps/[appId]/page-client.tsx
  • apps/dashboard/src/components/app-store-entry.tsx
  • apps/dashboard/src/lib/apps-frontend.tsx
apps/{backend,dashboard}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

NEVER use Next.js dynamic functions if you can avoid them. Instead, prefer using a client component and prefer usePathname instead of await params

Files:

  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/@modal/(.)apps/[appId]/page-client.tsx
  • apps/dashboard/src/components/app-store-entry.tsx
  • apps/dashboard/src/lib/apps-frontend.tsx
🧠 Learnings (1)
📚 Learning: 2025-11-28T21:21:39.123Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.123Z
Learning: Applies to apps-{frontend,config}.ts{,x} : To update the list of apps available, edit `apps-frontend.tsx` and `apps-config.ts`

Applied to files:

  • apps/dashboard/src/components/app-store-entry.tsx
  • apps/dashboard/src/lib/apps-frontend.tsx
🧬 Code graph analysis (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/@modal/(.)apps/[appId]/page-client.tsx (1)
packages/stack-ui/src/components/ui/dialog.tsx (1)
  • DialogContent (126-126)
apps/dashboard/src/components/app-store-entry.tsx (2)
docs/src/components/icons.tsx (2)
  • ChevronLeft (313-315)
  • ChevronRight (309-311)
packages/stack-ui/src/components/ui/dialog.tsx (3)
  • Dialog (123-123)
  • DialogContent (126-126)
  • DialogTitle (132-132)
🔇 Additional comments (7)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/@modal/(.)apps/[appId]/page-client.tsx (1)

28-28: LGTM!

The flex layout classes enable proper vertical stacking of the AppStoreEntry content within the modal.

apps/dashboard/src/lib/apps-frontend.tsx (3)

37-37: LGTM!

Extending the type to support both string paths and StaticImageData provides flexibility for different image loading strategies.


77-284: LGTM!

The store descriptions are well-written, consistent, and provide clear value propositions for each app. The use of ' for apostrophes in JSX is correct.


11-14: All referenced screenshot files exist in the public directory—no action required.

Verification confirms that all 38 screenshot files referenced by the getScreenshots helper are present in apps/dashboard/public/ and match the expected naming pattern and counts for each app (auth: 6, teams: 4, rbac: 4, api-keys: 1, payments: 7, emails: 8, data-vault: 4, webhooks: 2, vercel: 2).

apps/dashboard/src/components/app-store-entry.tsx (3)

22-42: LGTM!

The state management and navigation logic are well-structured. The use of useCallback prevents unnecessary re-renders of the navigation handler.


123-141: LGTM!

The scroll controls use opacity transitions appropriately, appearing on group hover. The transition-opacity follows the pattern of transitioning on exit (when hover ends).


161-222: LGTM!

The modal implementation is well-structured with proper accessibility labels, keyboard navigation, and responsive image handling. The use of DialogTitle with sr-only provides screen reader context while keeping the UI clean.

Comment thread apps/dashboard/src/components/app-store-entry.tsx Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/dashboard/src/components/app-store-entry.tsx (1)

199-212: Critical: Screenshots are not clickable — the preview modal can never be opened.

The modal implementation exists (lines 231-292) with keyboard navigation and controls, but there's no way to open it. The screenshot elements lack an onClick handler to set previewIndex, so it remains null forever.

Add click handlers to make screenshots open the preview modal:

               {appFrontend.screenshots.map((screenshot: string, index: number) => (
-                  <div
+                  <button
                     key={index}
-                    className="relative h-64 w-96 rounded-xl shadow-lg flex-shrink-0 overflow-hidden border border-gray-200 dark:border-gray-800"
+                    onClick={() => setPreviewIndex(index)}
+                    className="relative h-64 w-96 rounded-xl shadow-lg flex-shrink-0 overflow-hidden border border-gray-200 dark:border-gray-800 cursor-pointer hover:ring-2 hover:ring-blue-500 transition-shadow hover:transition-none focus:outline-none focus:ring-2 focus:ring-blue-500"
+                    aria-label={`View ${app.displayName} screenshot ${index + 1}`}
                   >
                     <Image
                       src={screenshot}
                       alt={`${app.displayName} screenshot ${index + 1}`}
                       fill
                       className="object-cover select-none"
                       draggable={false}
                     />
-                  </div>
+                  </button>
               ))}
♻️ Duplicate comments (1)
apps/dashboard/src/components/app-store-entry.tsx (1)

243-276: Adjust hover transitions on modal buttons to follow coding guidelines.

The close, previous, and next buttons use transition-colors without hover:transition-none. Per coding guidelines, hover transitions should snap instantly on hover and animate on exit.

                 <button
                   onClick={() => setPreviewIndex(null)}
-                  className="absolute top-4 right-4 z-50 bg-white/10 hover:bg-white/20 p-2 rounded-full transition-colors"
+                  className="absolute top-4 right-4 z-50 bg-white/10 hover:bg-white/20 p-2 rounded-full transition-colors hover:transition-none"
                   aria-label="Close preview"
                 >
                   <button
                     onClick={() => navigatePreview('prev')}
-                    className="absolute left-4 top-1/2 -translate-y-1/2 z-50 bg-white/10 hover:bg-white/20 p-3 rounded-full transition-colors"
+                    className="absolute left-4 top-1/2 -translate-y-1/2 z-50 bg-white/10 hover:bg-white/20 p-3 rounded-full transition-colors hover:transition-none"
                     aria-label="Previous screenshot"
                   >
                   <button
                     onClick={() => navigatePreview('next')}
-                    className="absolute right-4 top-1/2 -translate-y-1/2 z-50 bg-white/10 hover:bg-white/20 p-3 rounded-full transition-colors"
+                    className="absolute right-4 top-1/2 -translate-y-1/2 z-50 bg-white/10 hover:bg-white/20 p-3 rounded-full transition-colors hover:transition-none"
                     aria-label="Next screenshot"
                   >

Based on learnings, hover transitions should snap on hover-enter and animate on hover-exit.

🧹 Nitpick comments (2)
apps/dashboard/src/lib/apps-frontend.tsx (2)

39-39: The StaticImageData type is currently unused.

While the type signature now allows (string | StaticImageData)[], all current calls to getScreenshots() return only string[]. If you plan to support imported image objects in the future, this is fine; otherwise, you could keep the simpler string[] type for now.


222-222: Empty descriptions for alpha-stage apps.

The apps tv-mode, catalyst, neon, and convex still have empty storeDescription fields. If these apps are not yet ready for public promotion (all are marked as "alpha" in the config), this is acceptable. Otherwise, consider adding descriptions to maintain consistency.

Also applies to: 246-246, 260-260, 274-274

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2ca747 and 564f93d.

📒 Files selected for processing (2)
  • apps/dashboard/src/components/app-store-entry.tsx (3 hunks)
  • apps/dashboard/src/lib/apps-frontend.tsx (10 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: For blocking alerts and errors, never use toast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast. Don't delay the action with a pre-transition; apply transitions after the action instead
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use runAsynchronously or runAsynchronouslyWithAlert instead for asynchronous error handling
When creating hover transitions, avoid hover-enter transitions and just use hover-exit transitions (e.g., transition-colors hover:transition-none)
Use ES6 maps instead of records wherever possible

Files:

  • apps/dashboard/src/lib/apps-frontend.tsx
  • apps/dashboard/src/components/app-store-entry.tsx
apps/{backend,dashboard}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

NEVER use Next.js dynamic functions if you can avoid them. Instead, prefer using a client component and prefer usePathname instead of await params

Files:

  • apps/dashboard/src/lib/apps-frontend.tsx
  • apps/dashboard/src/components/app-store-entry.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-28T21:21:39.123Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.123Z
Learning: Applies to apps-{frontend,config}.ts{,x} : To update the list of apps available, edit `apps-frontend.tsx` and `apps-config.ts`

Applied to files:

  • apps/dashboard/src/lib/apps-frontend.tsx
  • apps/dashboard/src/components/app-store-entry.tsx
📚 Learning: 2025-11-28T21:21:39.123Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.123Z
Learning: Applies to **/*.{ts,tsx} : When creating hover transitions, avoid hover-enter transitions and just use hover-exit transitions (e.g., `transition-colors hover:transition-none`)

Applied to files:

  • apps/dashboard/src/components/app-store-entry.tsx
📚 Learning: 2025-11-28T21:21:39.123Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.123Z
Learning: Applies to **/*.{ts,tsx} : Keep hover/click transitions snappy and fast. Don't delay the action with a pre-transition; apply transitions after the action instead

Applied to files:

  • apps/dashboard/src/components/app-store-entry.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/lib/apps-frontend.tsx (1)
packages/stack-shared/src/apps/apps-config.ts (2)
  • AppId (48-48)
  • ALL_APPS (50-141)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: docker
  • GitHub Check: all-good
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: restart-dev-and-test-with-custom-base-port
  • GitHub Check: restart-dev-and-test
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: setup-tests
🔇 Additional comments (5)
apps/dashboard/src/components/app-store-entry.tsx (2)

36-62: LGTM!

The navigatePreview callback and keyboard event handler are well-implemented with proper boundary checks and cleanup.


231-292: LGTM!

The modal implementation is well-structured with proper accessibility (sr-only title, aria-labels), correct open/close state management, and appropriate conditional rendering of navigation buttons at boundary conditions.

apps/dashboard/src/lib/apps-frontend.tsx (3)

80-86: LGTM! Well-crafted descriptions.

The store descriptions for Authentication, Teams, and RBAC are clear, informative, and user-focused. They effectively communicate the purpose and value of each app.

Also applies to: 96-102, 112-118


127-133: LGTM! Excellent descriptions across all major apps.

The store descriptions for API Keys, Payments, Emails, Email API, Data Vault, Webhooks, and Launch Checklist are comprehensive and user-friendly. They consistently explain features and benefits while maintaining a professional tone.

Also applies to: 144-150, 162-168, 177-183, 192-198, 207-213, 231-237


13-16: All screenshot files are present and accounted for.

The helper function correctly generates dynamic screenshot paths, and all referenced files exist in apps/dashboard/public/. Each app's screenshot count matches its getScreenshots() call:

  • auth (6), teams (4), rbac (4), api-keys (1), payments (7), emails (8), data-vault (4), webhooks (2), vercel (2)

The implementation is correct with no missing assets.

Comment thread apps/dashboard/src/components/app-store-entry.tsx
Comment thread apps/dashboard/src/components/app-store-entry.tsx
Copy link
Copy Markdown

@vercel vercel Bot left a comment

Choose a reason for hiding this comment

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

Additional Suggestion:

Screenshot thumbnails are missing click handlers to open the preview modal, breaking the "clickable thumbnails" feature mentioned in the PR description.

View Details
📝 Patch Details
diff --git a/apps/dashboard/src/components/app-store-entry.tsx b/apps/dashboard/src/components/app-store-entry.tsx
index 0235f10e..c2f14228 100644
--- a/apps/dashboard/src/components/app-store-entry.tsx
+++ b/apps/dashboard/src/components/app-store-entry.tsx
@@ -199,7 +199,8 @@ export function AppStoreEntry({
                 {appFrontend.screenshots.map((screenshot: string, index: number) => (
                   <div
                     key={index}
-                    className="relative h-64 w-96 rounded-xl shadow-lg flex-shrink-0 overflow-hidden border border-gray-200 dark:border-gray-800"
+                    onClick={() => setPreviewIndex(index)}
+                    className="relative h-64 w-96 rounded-xl shadow-lg flex-shrink-0 overflow-hidden border border-gray-200 dark:border-gray-800 cursor-pointer hover:shadow-xl transition-shadow"
                   >
                     <Image
                       src={screenshot}

Analysis

Screenshot thumbnails missing click handlers to open preview modal

What fails: AppStoreEntry component screenshot thumbnails cannot be clicked to open the full-screen preview modal. The thumbnails are rendered but have no click event handlers, making the preview modal inaccessible to users. Users can only navigate screenshots with arrow keys if they find another way to trigger the modal (which is currently impossible from the UI).

How to reproduce:

  1. Navigate to the app store entry page for any app with screenshots
  2. Attempt to click on any thumbnail in the "Preview" section
  3. Observe that nothing happens - the modal does not open

Result: Clicking thumbnails has no effect. The modal remains closed.

Expected: Clicking a thumbnail should set previewIndex to that thumbnail's index, opening the full-screen preview modal displaying that screenshot. Users should then be able to navigate between screenshots using arrow keys or the on-screen navigation buttons.

Root cause: During the dashboard redesign (commit c2992f3), the screenshot thumbnail elements were refactored from <button> elements with onClick={() => setPreviewIndex(index)} handlers to plain <div> elements without click handlers. This regression removed the only user-accessible way to open the screenshot preview modal, which was introduced in commit 9c4a8a4 ("Support image slideshow, and open image in modals").

Fix: Restored the onClick={() => setPreviewIndex(index)} handler to the thumbnail container and added cursor-pointer and hover effects for better UX.

Comment thread apps/dashboard/src/components/app-store-entry.tsx
Comment thread apps/dashboard/src/components/app-store-entry.tsx
Copy link
Copy Markdown

@vercel vercel Bot left a comment

Choose a reason for hiding this comment

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

Additional Suggestion:

Screenshot thumbnails are not clickable - the preview modal cannot be opened because the screenshot divs are missing onClick handlers to set the preview index.

View Details
📝 Patch Details
diff --git a/apps/dashboard/src/components/app-store-entry.tsx b/apps/dashboard/src/components/app-store-entry.tsx
index 0235f10e..c2f14228 100644
--- a/apps/dashboard/src/components/app-store-entry.tsx
+++ b/apps/dashboard/src/components/app-store-entry.tsx
@@ -199,7 +199,8 @@ export function AppStoreEntry({
                 {appFrontend.screenshots.map((screenshot: string, index: number) => (
                   <div
                     key={index}
-                    className="relative h-64 w-96 rounded-xl shadow-lg flex-shrink-0 overflow-hidden border border-gray-200 dark:border-gray-800"
+                    onClick={() => setPreviewIndex(index)}
+                    className="relative h-64 w-96 rounded-xl shadow-lg flex-shrink-0 overflow-hidden border border-gray-200 dark:border-gray-800 cursor-pointer hover:shadow-xl transition-shadow"
                   >
                     <Image
                       src={screenshot}

Analysis

Screenshot thumbnails missing onClick handlers in AppStoreEntry component

What fails: The screenshot preview gallery in apps/dashboard/src/components/app-store-entry.tsx (lines 200-212) renders thumbnail divs without onClick handlers, making it impossible to open the full-screen preview modal. The previewIndex state remains null, and the Dialog component (line 232) never opens because it's conditioned on previewIndex !== null.

How to reproduce:

  1. Navigate to a page that uses the AppStoreEntry component with screenshots
  2. Attempt to click on any screenshot thumbnail
  3. The full-screen preview modal does not open

Result: Clicking thumbnails has no effect. The preview modal never opens. Users cannot view full-size screenshots.

Expected: Clicking any thumbnail should open a full-screen preview modal displaying that screenshot, with navigation controls to browse through other screenshots, as evidenced by the modal implementation (lines 232-292) that includes navigation buttons, keyboard handlers, and image display functionality.

Root cause: The setPreviewIndex(index) function is never called from user interaction. Code review shows setPreviewIndex is only called in:

  • Initial state declaration (line 23, set to null)
  • Navigation within modal (line 41, requires previewIndex !== null as precondition)
  • Close handlers (lines 56, 244)

The thumbnail divs lack the critical onClick={() => setPreviewIndex(index)} handler needed to open the modal initially.

Fix: Added onClick={() => setPreviewIndex(index)} to each thumbnail div and improved UX with cursor-pointer hover:shadow-xl transition-shadow CSS classes to indicate interactivity.

…ent in Link to navigate to the app details page, Triggers existing intercept route to open modal, enable/disable button still works
… to open lightbox preview, add left/right scroll buttons that appear on hover. Attach ref to scrollable container for button nav
…chMany outside HookPrefetcher for stable component ref. Wrap with memo() to skip re-renders when props unchanged, extract PrefetchCallback to isolate renders per callback
…ith memo() to prevent re-renders on context changes. Use useMemo() for callbacks array to maintain stable ref
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/dashboard/src/components/app-store-entry.tsx (1)

221-234: Type annotation may conflict with updated screenshots type.

The AI summary indicates screenshots type was changed to (string | StaticImageData)[], but line 221 explicitly types the parameter as string. This will cause a TypeScript error if screenshots contain StaticImageData objects.

Apply this diff to fix the type:

-                {appFrontend.screenshots.map((screenshot: string, index: number) => (
+                {appFrontend.screenshots.map((screenshot, index) => (

Letting TypeScript infer the type from the array is cleaner and avoids potential mismatches.

♻️ Duplicate comments (2)
apps/dashboard/src/components/app-store-entry.tsx (2)

199-214: Adjust hover transition on scroll buttons per coding guidelines.

The transition-opacity causes the buttons to fade in smoothly on hover enter. Per coding guidelines, hover-enter transitions should snap instantly while hover-exit transitions should animate.

Apply this diff:

               <button
                 onClick={() => scrollScreenshots('left')}
-                className="absolute left-0 top-1/2 -translate-y-1/2 z-10 bg-white/90 dark:bg-gray-900/90 hover:bg-white dark:hover:bg-gray-800 p-2 rounded-full shadow-lg border border-gray-200 dark:border-gray-700 opacity-0 group-hover/screenshots:opacity-100 transition-opacity focus:opacity-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
+                className="absolute left-0 top-1/2 -translate-y-1/2 z-10 bg-white/90 dark:bg-gray-900/90 hover:bg-white dark:hover:bg-gray-800 p-2 rounded-full shadow-lg border border-gray-200 dark:border-gray-700 opacity-0 group-hover/screenshots:opacity-100 transition-opacity group-hover/screenshots:transition-none focus:opacity-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
                 aria-label="Scroll left"
               >

Same change needed for the right scroll button on line 210.


222-226: Add hover:transition-none to thumbnail buttons.

Per coding guidelines, hover transitions should snap instantly on enter and only animate on exit.

-                    className="relative h-64 w-96 rounded-xl shadow-lg flex-shrink-0 overflow-hidden border border-gray-200 dark:border-gray-800 cursor-pointer hover:ring-2 hover:ring-blue-500/50 transition-shadow focus:outline-none focus:ring-2 focus:ring-blue-500"
+                    className="relative h-64 w-96 rounded-xl shadow-lg flex-shrink-0 overflow-hidden border border-gray-200 dark:border-gray-800 cursor-pointer hover:ring-2 hover:ring-blue-500/50 transition-shadow hover:transition-none focus:outline-none focus:ring-2 focus:ring-blue-500"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 564f93d and b2076b2.

📒 Files selected for processing (4)
  • apps/dashboard/src/components/app-square.tsx (4 hunks)
  • apps/dashboard/src/components/app-store-entry.tsx (5 hunks)
  • apps/dashboard/src/lib/prefetch/hook-prefetcher.tsx (1 hunks)
  • apps/dashboard/src/lib/prefetch/url-prefetcher.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: For blocking alerts and errors, never use toast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast. Don't delay the action with a pre-transition; apply transitions after the action instead
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use runAsynchronously or runAsynchronouslyWithAlert instead for asynchronous error handling
When creating hover transitions, avoid hover-enter transitions and just use hover-exit transitions (e.g., transition-colors hover:transition-none)
Use ES6 maps instead of records wherever possible

Files:

  • apps/dashboard/src/lib/prefetch/url-prefetcher.tsx
  • apps/dashboard/src/lib/prefetch/hook-prefetcher.tsx
  • apps/dashboard/src/components/app-square.tsx
  • apps/dashboard/src/components/app-store-entry.tsx
apps/{backend,dashboard}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

NEVER use Next.js dynamic functions if you can avoid them. Instead, prefer using a client component and prefer usePathname instead of await params

Files:

  • apps/dashboard/src/lib/prefetch/url-prefetcher.tsx
  • apps/dashboard/src/lib/prefetch/hook-prefetcher.tsx
  • apps/dashboard/src/components/app-square.tsx
  • apps/dashboard/src/components/app-store-entry.tsx
🧠 Learnings (4)
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.

Applied to files:

  • apps/dashboard/src/lib/prefetch/url-prefetcher.tsx
📚 Learning: 2025-11-28T21:21:39.142Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.142Z
Learning: Applies to apps-{frontend,config}.ts{,x} : To update the list of apps available, edit `apps-frontend.tsx` and `apps-config.ts`

Applied to files:

  • apps/dashboard/src/components/app-square.tsx
  • apps/dashboard/src/components/app-store-entry.tsx
📚 Learning: 2025-11-28T21:21:39.142Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.142Z
Learning: Applies to **/*.{ts,tsx} : When creating hover transitions, avoid hover-enter transitions and just use hover-exit transitions (e.g., `transition-colors hover:transition-none`)

Applied to files:

  • apps/dashboard/src/components/app-store-entry.tsx
📚 Learning: 2025-11-28T21:21:39.142Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.142Z
Learning: Applies to **/*.{ts,tsx} : Keep hover/click transitions snappy and fast. Don't delay the action with a pre-transition; apply transitions after the action instead

Applied to files:

  • apps/dashboard/src/components/app-store-entry.tsx
🧬 Code graph analysis (3)
apps/dashboard/src/lib/prefetch/url-prefetcher.tsx (2)
packages/stack-shared/src/utils/urls.tsx (1)
  • url (278-280)
apps/dashboard/src/lib/prefetch/hook-prefetcher.tsx (1)
  • HookPrefetcher (48-77)
apps/dashboard/src/components/app-square.tsx (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (2)
  • useProjectId (46-53)
  • useAdminApp (29-44)
apps/dashboard/src/components/link.tsx (1)
  • Link (22-45)
apps/dashboard/src/components/app-store-entry.tsx (2)
docs/src/components/icons.tsx (2)
  • ChevronLeft (313-315)
  • ChevronRight (309-311)
packages/stack-ui/src/components/ui/dialog.tsx (3)
  • Dialog (123-123)
  • DialogContent (126-126)
  • DialogTitle (132-132)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: restart-dev-and-test-with-custom-base-port
  • GitHub Check: docker
  • GitHub Check: setup-tests
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: all-good
  • GitHub Check: Vercel Agent Review
🔇 Additional comments (10)
apps/dashboard/src/lib/prefetch/hook-prefetcher.tsx (1)

15-46: LGTM on the memoization refactor.

Extracting PrefetchMany, PrefetchCallback, and PrefetchCallbackInner as stable component references is a good approach for preventing unnecessary reconciliation. The try/finally pattern in PrefetchCallbackInner correctly maintains the counter state even when callbacks suspend or throw.

Minor note: using array index as key (line 19) is acceptable here since the callbacks array is typically static per URL pattern.

apps/dashboard/src/lib/prefetch/url-prefetcher.tsx (1)

264-278: LGTM on the memoization improvements.

Wrapping UrlPrefetcher with memo and using useMemo for callbacks is a sensible optimization. The dependency array [url] is correct, and combining this with the key={url.toString()} on HookPrefetcher ensures proper remounting when URLs change while avoiding unnecessary recomputation when the parent re-renders with the same href.

apps/dashboard/src/components/app-store-entry.tsx (5)

6-9: LGTM!

Imports are well-organized and appropriate for the component's functionality. The state setup with screenshotContainerRef and previewIndex is correctly initialized.


36-42: LGTM!

The navigatePreview function is properly memoized with correct dependencies and handles boundary conditions safely.


44-62: LGTM!

Keyboard navigation is well-implemented with proper event listener cleanup and correct dependency tracking.


243-252: LGTM!

The description section is cleanly implemented with proper prose styling for rich content rendering.


254-315: Well-structured preview modal with good accessibility.

The modal implementation is comprehensive:

  • Proper ARIA labeling with sr-only DialogTitle
  • Keyboard navigation support (handled by the useEffect above)
  • Conditional prev/next buttons based on bounds
  • Image counter for orientation

One minor note: The modal control buttons (close, prev, next) at lines 268, 283, and 294 also have transition-colors without hover:transition-none, following the same pattern flagged elsewhere.

apps/dashboard/src/components/app-square.tsx (3)

1-1: LGTM! Correct use of useProjectId for client component.

The use of useProjectId() (which internally uses usePathname) aligns with the coding guideline to prefer usePathname over await params in client components. The path construction is clean and correct.

Also applies to: 57-57, 63-63


103-135: LGTM! Clean navigation structure.

Wrapping the card content in a Link with absolute inset-0 makes the entire tile clickable and provides a clear navigation target. The structure works well with the absolutely-positioned button overlay that follows.


137-158: LGTM! Hover actions properly layered above the Link.

The restructured hover actions with z-10 positioning ensure the Enable/Disable button remains clickable above the Link wrapper. The sibling relationship (rather than nesting inside the Link) is the correct approach, making the e.preventDefault()/e.stopPropagation() in handleToggleEnabled technically redundant but harmless as defensive code.

href={appDetailsPath}
className={cn(
"absolute inset-0 flex flex-col items-center p-3 sm:p-4 rounded-xl sm:rounded-2xl transition-all duration-200 cursor-default overflow-hidden",
"absolute inset-0 flex flex-col items-center p-3 sm:p-4 rounded-xl sm:rounded-2xl transition-all duration-200 cursor-pointer overflow-hidden",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix hover transitions to follow coding guidelines.

The hover transitions should use the hover-exit only pattern for snappier interactions. Currently, transition-all duration-200 applies on both hover-enter and hover-exit.

As per coding guidelines: When creating hover transitions, avoid hover-enter transitions and just use hover-exit transitions (e.g., transition-colors hover:transition-none).

Apply this diff to fix the hover transitions:

         className={cn(
-          "absolute inset-0 flex flex-col items-center p-3 sm:p-4 rounded-xl sm:rounded-2xl transition-all duration-200 cursor-pointer overflow-hidden",
+          "absolute inset-0 flex flex-col items-center p-3 sm:p-4 rounded-xl sm:rounded-2xl transition-all duration-200 hover:transition-none cursor-pointer overflow-hidden",
           "bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-800",
           "hover:border-gray-300 dark:hover:border-gray-700 hover:shadow-lg",
         <div className={cn(
-          "absolute inset-x-0 bottom-3 sm:bottom-4 flex justify-center gap-2 transition-all duration-200 z-10",
+          "absolute inset-x-0 bottom-3 sm:bottom-4 flex justify-center gap-2 transition-all duration-200 hover:transition-none z-10",
           (isHovered || isProcessing) ? "opacity-100 translate-y-0" : "opacity-0 translate-y-2 pointer-events-none"
         )}>

Also applies to: 140-140

🤖 Prompt for AI Agents
In apps/dashboard/src/components/app-square.tsx around lines 106 and 140, the
element currently uses "transition-all duration-200" which applies on both
hover-enter and hover-exit; replace that with the hover-exit pattern by using
"transition-colors duration-200 hover:transition-none" (or equivalent: use a
specific transition property like transition-colors plus hover:transition-none)
so the transition only runs on hover-exit for snappier interactions.

@madster456 madster456 merged commit 2796f93 into dev Dec 4, 2025
6 of 16 checks passed
@madster456 madster456 deleted the dashboard/apps/storeDesc branch December 4, 2025 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants