Coding
PromptBeginner5 minmarkdown
Markdown Converter
Agent skill for markdown-converter
21
You are building the **frontend** of an AI Audiobook Platform using **Next.js 14 (App Router) + TypeScript + TailwindCSS + shadcn/ui**.
Sign in to like and favorite skills
You are building the frontend of an AI Audiobook Platform using Next.js 14 (App Router) + TypeScript + TailwindCSS + shadcn/ui.
The backend is a FastAPI service exposing REST APIs. Your frontend must authenticate, upload books, show processing status, display chapters, and stream audio.
<audio> for playback (custom UI wrapper OK).env.localEnvironment variables
NEXT_PUBLIC_API_BASE=https://<your-backend-base-url>
app/ layout.tsx page.tsx # Landing page login/page.tsx signup/page.tsx dashboard/page.tsx # My Library upload/page.tsx # Upload flow books/[id]/page.tsx # Book details + chapters + player components/ AuthForm.tsx BookCard.tsx ChapterList.tsx AudioPlayer.tsx UploadForm.tsx Protected.tsx # gate for auth-only routes lib/ api.ts # axios instance + typed helpers auth.ts # token storage + helpers types.ts # DTOs for API responses usePlayerStore.ts # Zustand store for audio player styles/ globals.css
Auth
POST /auth/signup → body { email: string, password: string } → { token: string }POST /auth/login → body { email: string, password: string } → { token: string }Books & Tasks
POST /books/upload (multipart form) → fields: title?: string, file: File → { task_id: string }GET /status/{task_id} → { status: "queued" | "processing" | "ready" | "failed", progress?: number, book_id?: string, error?: string }GET /books → Array<{ book_id: string, title: string, status: "processing" | "ready" | "failed" }>GET /books/{book_id} → { book_id: string, title: string, status: string, chapters: Array<{ chapter_id: string, number: number, title: string }> }GET /books/{book_id}/chapters/{chapter_id}/audio → { presigned_url: string, expires_in: number }(Optional) Progress
POST /progress → body { book_id: string, chapter_id: string, position_seconds: number } → { ok: true }GET /progress?book_id=... → { chapter_id: string, position_seconds: number }All authenticated requests must include header:
Authorization: Bearer <token>
/login and /signup pages using shadcn/ui forms./dashboard.GET /books on load.processing, show progress indicator./books/[id] when status is ready./upload with UploadForm:
POST /books/upload (multipart).{ task_id } and begin polling GET /status/{task_id} every 5–10s.ready, redirect to /books/{book_id}.failed, show error toast and allow retry.GET /books/{book_id}.GET /books/{book_id}/chapters/{chapter_id}/audio.{ presigned_url } and hand off to the audio player.AudioPlayer.tsx with controls:
usePlayerStore (src + playing + position).position_seconds to POST /progress every 30s and on pause/unload./books/{book_id} load, fetch GET /progress?book_id=... and resume./login.Auth store:
{ token, setToken, clearToken }{ chapterId, src, isPlaying, position, setSrc(), play(), pause(), seek() }
lib/api.ts (specify behaviors)baseURL = process.env.NEXT_PUBLIC_API_BASEAuthorization header if token exists./login.signup(data), login(data)getBooks(), getBook(bookId)uploadBook(formData)pollStatus(taskId)getChapterAudioUrl(bookId, chapterId)saveProgress({ book_id, chapter_id, position_seconds })getProgress(bookId)lib/types.ts)export type AuthResponse = { token: string }; export type TaskStatus = "queued" | "processing" | "ready" | "failed"; export interface UploadResponse { task_id: string; } export interface StatusResponse { status: TaskStatus; progress?: number; book_id?: string; error?: string; } export interface BookListItem { book_id: string; title: string; status: "processing" | "ready" | "failed"; } export interface BookDetail { book_id: string; title: string; status: string; chapters: { chapter_id: string; number: number; title: string; }[]; } export interface PresignedUrl { presigned_url: string; expires_in: number; } export interface ProgressPayload { book_id: string; chapter_id: string; position_seconds: number; } export interface ProgressState { chapter_id: string; position_seconds: number; }
AuthForm.tsxrouter.push("/dashboard").BookCard.tsxBookListItemready, button links to /books/[id].UploadForm.tsx/books/upload (multipart)/status/{task_id} until { status: "ready", book_id }/books/{book_id}ChapterList.tsxAudioPlayer.tsx<audio> element and syncs with player store.Protected.tsx/login.Card, Button, Input, Label, Progress, Skeleton, Toast.env.local for NEXT_PUBLIC_API_BASE