Summary
All backend APIs are complete (Threads CRUD, Replies CRUD, Votes, Me). Auth + onboarding flow is fully wired (Better Auth with GitHub OAuth, auto-generated temp username for OAuth users, onboarding page for username selection).
The remaining work is frontend pages. The home page (_app/index.tsx) is still the TanStack Start boilerplate. No thread detail, create thread, or user profile pages exist yet.
Status Overview
✅ Done
- Backend: Threads (CRUD + list + top), Replies (CRUD + nested), Votes, Me
- Auth: Better Auth + GitHub OAuth + email/password
- Auth flow: sign-in page → OAuth callback → temp username auto-generated → onboarding page → redirect to home
- DB schemas: all tables with correct columns, indexes, relations
- Shared DTOs & constants: all input/output schemas via
@trid/shared
- oRPC client utils:
threadORPC, replyORPC, voteORPC via @orpc/tanstack-query
- Layout: Root, Auth layout, App layout (sidebar + navbar)
- UI components: button, card, sidebar, input-group, field, spinner, sonner, skeleton, theme provider
❌ Not Started (this issue)
- Home page (thread listing)
- Thread detail page
- Create thread page
- User profile page
1. Home Page — Thread Listing (_app/index.tsx)
Route: / (existing _app/index.tsx, replace boilerplate content)
API: threadORPC.list.useInfiniteQuery() (or orpc.threads.list)
Input schema: listThreadsInputSchema:
{ feed: "discover" | "popular" | "latest", limit: number, cursor?: string }
Output schema: listThreadsOutputSchema:
{ items: Array<{ id, author, title, slug, status, votesScoresCount, repliesCount, lastActivityAt, createdAt, uservote }>, nextCursor: string | null }
Requirements
- Feed tabs: 3 tabs — Discover, Popular, Latest
- Active tab highlighted, click to switch feed
- URL search param
?feed=discover for shareable state
- Default:
discover
- Thread cards: Each card shows:
- Author avatar + name + username
- Title (link to thread detail)
- Vote count with upvote/downvote buttons (if authenticated)
- Reply count
- Relative time ("2h ago", "3d ago")
- Cursor pagination: "Load more" button at bottom
- Loading skeleton while fetching
- "No more threads" when
nextCursor is null
- Empty state: "No threads yet" with CTA to create first thread
- Error state: Retry button on fetch failure
- Vote interaction: Click upvote/downvote →
voteORPC.thread.useMutation() → optimistically update UI
Key oRPC hooks
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
threadORPC.list.useInfiniteQuery(
{ feed: selectedFeed, limit: 20 },
{ getNextPageParam: (lastPage) => lastPage.nextCursor },
)
const voteMutation = voteORPC.thread.useMutation()
Thread card component
Props: thread: ThreadListItem
- Link to
/$slug for title
- Author row (avatar + name + username + time)
- Vote button group (upvote/downvote count)
- Reply count icon
2. Thread Detail Page
Route: /_app/threads/$slug (new file: apps/web/src/routes/_app/threads/$slug.tsx)
API:
orpc.threads.get({ slug }) → threadDetailOutputSchema
orpc.replies.list({ threadSlug, sort, limit, cursor }) → listRepliesOutputSchema
Requirements
- Thread content: Full thread view with title, author info, content body, timestamps
- Vote buttons: Upvote/downvote on thread + each reply
- Reply count + sort dropdown: Sort by Top / Latest / Oldest (
?sort=top)
- Reply list:
- Each reply shows author, content, vote count, timestamp
- Nested replies (depth-based indentation)
- "Load more" pagination
- Reply form at bottom (authenticated only)
- Create reply form: Textarea + submit button
- Thread actions: Edit/delete buttons if current user is author
- 404 handling: "Thread not found" when slug doesn't exist
- Loading state: Skeleton loader
- Delete confirmation: Dialog/modal before soft-delete
Key oRPC hooks
const { data: thread } = orpc.threads.get.useQuery({ slug })
const { data: replies, fetchNextPage } =
replyORPC.list.useInfiniteQuery(
{ threadSlug: slug, sort: selectedSort, limit: 20 },
{ getNextPageParam: (lastPage) => lastPage.nextCursor },
)
const createReplyMutation = replyORPC.create.useMutation()
const voteMutation = voteORPC.reply.useMutation()
3. Create Thread Page
Route: /_app/threads/new (new file: apps/web/src/routes/_app/threads/new.tsx)
Protected: Redirect to sign-in if not authenticated
API: orpc.threads.create({ title, content })
Requirements
- Form: Title input + content textarea (markdown or plain text)
- Validation: Title 1-300 chars, content min 1 char
- Submit:
orpc.threads.create.useMutation() → on success, navigate to /$slug
- Error handling: Show validation errors inline, API errors as toast
- Cancel/back button: Navigate back to home
- Loading state: Disable submit button + spinner while submitting
4. User Profile Page
Route: /_app/users/$username (new file: apps/web/src/routes/_app/users/$username.tsx)
API:
orpc.me for current user's own profile
orpc.threads.list({ feed: "latest", author: username }) for user's threads
Requirements
- Profile header: Avatar, name, username, bio, pronouns, location, join date
- Banner image if set
- User's threads tab: List of threads by this user
- Edit profile button (only if current user's own profile)
- Navigate to settings page
- Loading & error states
- 404 handling for non-existent username
5. Polish & Cross-cutting
- Mobile responsive: All pages should work on mobile (sidebar drawer, full-width content)
- Relative time: Consistent time display across all pages (use
@trid/shared/utils if exists, or a simple helper)
- Optimistic updates for votes to feel instant
- Error boundaries for each route
- Empty states for lists with zero items
- Keyboard accessibility for vote buttons, reply forms, navigation
Implementation Order
- Home page — Thread Listing (highest priority, landing page)
- Thread Detail Page (core user flow)
- Create Thread Page (content creation)
- User Profile Page (identity & history)
- Polish & cross-cutting fixes
Each page is independently buildable since the backend is fully ready.
Tech Notes
- Use
@orpc/tanstack-query hooks (threadORPC.list.useInfiniteQuery(), etc.) — already configured in apps/web/src/libs/orpc.ts
- Use TanStack Router file-based routes — create new files under
apps/web/src/routes/_app/
- Use
@trid/ui components (Button, Card, Skeleton, etc.)
- Use
authClient from #/libs/auth-client for auth actions
- Use
sonner toast for notifications
- TanStack Router search params for feed/sort/cursor state in URL
- Vote mutations should cache-invalidate the relevant thread query after success
Summary
All backend APIs are complete (Threads CRUD, Replies CRUD, Votes, Me). Auth + onboarding flow is fully wired (Better Auth with GitHub OAuth, auto-generated temp username for OAuth users, onboarding page for username selection).
The remaining work is frontend pages. The home page (
_app/index.tsx) is still the TanStack Start boilerplate. No thread detail, create thread, or user profile pages exist yet.Status Overview
✅ Done
@trid/sharedthreadORPC,replyORPC,voteORPCvia@orpc/tanstack-query❌ Not Started (this issue)
1. Home Page — Thread Listing (
_app/index.tsx)Route:
/(existing_app/index.tsx, replace boilerplate content)API:
threadORPC.list.useInfiniteQuery()(ororpc.threads.list)Input schema:
listThreadsInputSchema:Output schema:
listThreadsOutputSchema:Requirements
?feed=discoverfor shareable statediscovernextCursoris nullvoteORPC.thread.useMutation()→ optimistically update UIKey oRPC hooks
Thread card component
Props:
thread: ThreadListItem/$slugfor title2. Thread Detail Page
Route:
/_app/threads/$slug(new file:apps/web/src/routes/_app/threads/$slug.tsx)API:
orpc.threads.get({ slug })→threadDetailOutputSchemaorpc.replies.list({ threadSlug, sort, limit, cursor })→listRepliesOutputSchemaRequirements
?sort=top)Key oRPC hooks
3. Create Thread Page
Route:
/_app/threads/new(new file:apps/web/src/routes/_app/threads/new.tsx)Protected: Redirect to sign-in if not authenticated
API:
orpc.threads.create({ title, content })Requirements
orpc.threads.create.useMutation()→ on success, navigate to/$slug4. User Profile Page
Route:
/_app/users/$username(new file:apps/web/src/routes/_app/users/$username.tsx)API:
orpc.mefor current user's own profileorpc.threads.list({ feed: "latest", author: username })for user's threadsRequirements
5. Polish & Cross-cutting
@trid/shared/utilsif exists, or a simple helper)Implementation Order
Each page is independently buildable since the backend is fully ready.
Tech Notes
@orpc/tanstack-queryhooks (threadORPC.list.useInfiniteQuery(), etc.) — already configured inapps/web/src/libs/orpc.tsapps/web/src/routes/_app/@trid/uicomponents (Button, Card, Skeleton, etc.)authClientfrom#/libs/auth-clientfor auth actionssonnertoast for notifications