Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds a typed apps directory and data layer, new apps listing and statically-generated app detail pages with structured metadata, client browsing and deploy UI (analytics + deploy POST), a stubbed deploy API endpoint, structured-data helpers, and an "Apps" nav item. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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: 1
🧹 Nitpick comments (2)
apps/site/src/components/apps/app-deploy-button.tsx (1)
15-28: 💤 Low valueConsider simplifying the useEffect dependency array.
The dependency array lists individual app properties (
app.category, app.kind, app.name, ...), but conceptually you want to track when viewing a different app. Usingapp.slugalone (the unique identifier) would be more precise and avoid spurious re-fires if other properties change while viewing the same app.♻️ Simplified dependency
useEffect(() => { posthog.capture("site:app_detail_viewed", { app_slug: app.slug, app_name: app.name, app_kind: app.kind, app_category: app.category, listing_status: app.status, source_type: app.source, }); - }, [app.category, app.kind, app.name, app.slug, app.source, app.status]); + }, [app.slug]);Since this is static data and
appwon't mutate during the component lifecycle, both approaches work correctly in practice.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/site/src/components/apps/app-deploy-button.tsx` around lines 15 - 28, AppDetailTracker is re-running posthog.capture whenever any listed app property changes; simplify the useEffect dependency to only track when a different app is viewed by using the unique identifier (app.slug) instead of all properties. Update the dependency array in the useEffect inside AppDetailTracker to [app.slug] (keeping the posthog.capture call as-is) so the event fires only when the viewed app changes; ensure app.slug is present and stable for this component.apps/site/src/app/api/apps/deploy/route.ts (1)
7-18: 💤 Low valueConsider tightening input validation for robustness.
The type assertion
as { slug?: string }on line 8 assumes the parsed body conforms to this shape. If someone sends{ slug: 123 }(number instead of string), the subsequent check!slugwould pass (truthy), but the app lookup might behave unexpectedly. Since this is a placeholder endpoint returning 501 anyway, this is low priority.🛡️ Optional: Add explicit type check
try { const body = (await request.json()) as { slug?: string }; - slug = body.slug; + slug = typeof body.slug === "string" ? body.slug : undefined; } catch {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/site/src/app/api/apps/deploy/route.ts` around lines 7 - 18, The current parsing uses a loose type assertion for the request body and assigns to slug (from request.json()), allowing non-string values like numbers to slip through; update the handler in route.ts to explicitly validate that the parsed body has a string slug (e.g., parse via await request.json(), check typeof body?.slug === 'string' and only assign slug when true) and return the existing NextResponse.json 400 error for malformed or missing slug values before proceeding to the 501 response; ensure checks reference the same slug variable and keep behavior unchanged for valid string slugs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/site/src/components/apps/apps-directory.tsx`:
- Around line 258-263: The analytics call is using a stale filteredApps.length
(which is derived from deferredSearch) in the onChange handler; update
trackFilterChange to pass the correct count by computing the new result count
from the current apps list using nextSearch (same filter logic used to derive
filteredApps/deferredSearch) before calling trackFilterChange, and apply the
same change to the kind/category handlers (they also call trackFilterChange with
filteredApps.length) or refactor the filtering into a shared function (e.g.,
computeFilteredApps(search, kind, category) used by setSearch, setKind,
setCategory) and use its returned length when calling trackFilterChange, keeping
calls to setSearch/updateUrl unchanged.
---
Nitpick comments:
In `@apps/site/src/app/api/apps/deploy/route.ts`:
- Around line 7-18: The current parsing uses a loose type assertion for the
request body and assigns to slug (from request.json()), allowing non-string
values like numbers to slip through; update the handler in route.ts to
explicitly validate that the parsed body has a string slug (e.g., parse via
await request.json(), check typeof body?.slug === 'string' and only assign slug
when true) and return the existing NextResponse.json 400 error for malformed or
missing slug values before proceeding to the 501 response; ensure checks
reference the same slug variable and keep behavior unchanged for valid string
slugs.
In `@apps/site/src/components/apps/app-deploy-button.tsx`:
- Around line 15-28: AppDetailTracker is re-running posthog.capture whenever any
listed app property changes; simplify the useEffect dependency to only track
when a different app is viewed by using the unique identifier (app.slug) instead
of all properties. Update the dependency array in the useEffect inside
AppDetailTracker to [app.slug] (keeping the posthog.capture call as-is) so the
event fires only when the viewed app changes; ensure app.slug is present and
stable for this component.
🪄 Autofix (Beta)
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b6cb4add-dd74-4696-8933-12e344d5389f
📒 Files selected for processing (8)
apps/site/src/app/api/apps/deploy/route.tsapps/site/src/app/apps/[slug]/page.tsxapps/site/src/app/apps/page.tsxapps/site/src/app/layout.tsxapps/site/src/components/apps/app-deploy-button.tsxapps/site/src/components/apps/apps-directory.tsxapps/site/src/data/apps.tsapps/site/src/lib/structured-data.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/site/src/components/apps/apps-directory.tsx`:
- Around line 256-266: The search Input and the filter controls (the Input with
value/search and the filter buttons rendered in this component) lack accessible
names and state; add an explicit accessible name to the search box (e.g.,
aria-label="Search apps" or a visually hidden <label> tied to the Input) and
expose the selected kind/category state to assistive tech by using aria-pressed
on each filter button or converting the controls to a proper radio/tab pattern
(role="tablist"/role="tab" or role="radiogroup"/role="radio") so the current
selection is programmatically exposed; update the components where setSearch,
updateUrl, and trackFilterChange are used to keep behavior identical while
adding the aria attributes and ensure keyboard focus/enter/space activate
filters consistently (refer to the Input with value={search} and the filter
button rendering logic for places to add aria-label and aria-pressed or switch
to role-based semantics).
🪄 Autofix (Beta)
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2723f9e5-6dda-41fb-9f87-430683ae5024
📒 Files selected for processing (3)
apps/site/src/app/apps/[slug]/page.tsxapps/site/src/app/apps/page.tsxapps/site/src/components/apps/apps-directory.tsx
✅ Files skipped from review due to trivial changes (1)
- apps/site/src/app/apps/[slug]/page.tsx
| <Input | ||
| value={search} | ||
| onChange={(event: ChangeEvent<HTMLInputElement>) => { | ||
| const nextSearch = event.target.value; | ||
| setSearch(nextSearch); | ||
| updateUrl(pathname, router, kind, category, nextSearch); | ||
| trackFilterChange(kind, category, nextSearch, filteredApps.length); | ||
| }} | ||
| placeholder="Search AI agents, internal tools, waitlists, webhook apps..." | ||
| className="w-full" | ||
| /> |
There was a problem hiding this comment.
Add accessible labeling and state to the filter controls.
The search field currently relies on placeholder text, and the selected kind/category is only communicated visually. That makes the filter bar much harder to use with assistive tech. Give the input an accessible name and expose the active filter state with aria-pressed (or a radios/tabs pattern).
Suggested change
<div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_auto]">
<Input
+ aria-label="Search apps"
value={search}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
const nextSearch = event.target.value;
setSearch(nextSearch);
updateUrl(pathname, router, kind, category, nextSearch);
@@
{kindOptions.map((option) => (
<Button
key={option.value}
+ aria-pressed={kind === option.value}
variant={kind === option.value ? "ppg" : "default-stronger"}
size="lg"
onClick={() => {
@@
<button
key={option}
type="button"
+ aria-pressed={category === option}
onClick={() => {
setCategory(option);
updateUrl(pathname, router, kind, option, search);Also applies to: 270-301, 308-344
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/site/src/components/apps/apps-directory.tsx` around lines 256 - 266, The
search Input and the filter controls (the Input with value/search and the filter
buttons rendered in this component) lack accessible names and state; add an
explicit accessible name to the search box (e.g., aria-label="Search apps" or a
visually hidden <label> tied to the Input) and expose the selected kind/category
state to assistive tech by using aria-pressed on each filter button or
converting the controls to a proper radio/tab pattern (role="tablist"/role="tab"
or role="radiogroup"/role="radio") so the current selection is programmatically
exposed; update the components where setSearch, updateUrl, and trackFilterChange
are used to keep behavior identical while adding the aria attributes and ensure
keyboard focus/enter/space activate filters consistently (refer to the Input
with value={search} and the filter button rendering logic for places to add
aria-label and aria-pressed or switch to role-based semantics).
There was a problem hiding this comment.
♻️ Duplicate comments (1)
apps/site/src/components/apps/apps-directory.tsx (1)
270-280:⚠️ Potential issue | 🟠 Major | ⚡ Quick winExpose filter controls with proper accessible labeling/state.
The search control still relies on placeholder text, and selected filter state is not programmatically exposed. This remains an accessibility gap.
Suggested fix
<div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_auto]"> <Input + aria-label="Search apps" value={search} @@ {kindOptions.map((option) => ( <Button key={option.value} + aria-pressed={kind === option.value} variant={kind === option.value ? "ppg" : "default-stronger"} size="lg" @@ {categories.map((option) => ( <button key={option} type="button" + aria-pressed={category === option} onClick={() => {Also applies to: 283-287, 322-358
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/site/src/components/apps/apps-directory.tsx` around lines 270 - 280, The search Input currently relies on placeholder text and the filter controls do not expose selected state; update the Input usage to include an explicit accessible label (e.g., aria-label="Search apps" or visually hidden <label> tied to the Input) and ensure the filter controls (the components that call setSearch, updateUrl, trackFilterChange and compute filteredApps) expose their selection state via appropriate ARIA attributes (aria-pressed, role="radio"/"tab", aria-selected, or aria-checked as appropriate) and keyboard focus handling; update the Input props and each filter control component to provide these attributes and ensure their state reflects the current kind/category/search so assistive tech can programmatically read the selected filters and current search value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@apps/site/src/components/apps/apps-directory.tsx`:
- Around line 270-280: The search Input currently relies on placeholder text and
the filter controls do not expose selected state; update the Input usage to
include an explicit accessible label (e.g., aria-label="Search apps" or visually
hidden <label> tied to the Input) and ensure the filter controls (the components
that call setSearch, updateUrl, trackFilterChange and compute filteredApps)
expose their selection state via appropriate ARIA attributes (aria-pressed,
role="radio"/"tab", aria-selected, or aria-checked as appropriate) and keyboard
focus handling; update the Input props and each filter control component to
provide these attributes and ensure their state reflects the current
kind/category/search so assistive tech can programmatically read the selected
filters and current search value.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: db5b8142-4d6d-4992-8b17-6a56af9fef39
📒 Files selected for processing (2)
apps/site/src/app/apps/page.tsxapps/site/src/components/apps/apps-directory.tsx
Why this exists
This PR creates the new
site/appssurface for Prisma Compute: a discoverable directory of deployable apps that people can actually use, not just a gallery of starter templates.The product direction is inspired by Railway's deploy directory, but shifts the emphasis in a few important ways:
What ships in this PR
New
/appsdirectory landing page/appsApp detail pages for SEO and discovery
/apps/[slug]Seeded app registry and listing model
apps/site/src/data/apps.tsprisma/appsor a GitHub topic:Submission and source model
prisma/appsDeploy integration seam
POST /api/apps/deployas the future one-click deployment entry pointpending_integrationresponse todayAnalytics instrumentation
Site integration
/appsinto the main site navigation/computemarketing pageRailway-inspired UX decisions carried over
Railway-inspired choices intentionally changed
What is intentionally stubbed for now
prisma/appsor GitHub-topic discoveryVerification
NEXT_DOCS_ORIGIN=https://docs.prisma.io NEXT_BLOG_ORIGIN=https://blog.prisma.io corepack pnpm --filter site types:checkFollow-up work after this lands
prisma/appsand/or GitHub topic metadataSummary by CodeRabbit
New Features
Data
API
SEO / Structured Data
Analytics
Chores