Skip to Content
AssignmentsAssignment 4

Assignment 4: Full-Stack Next.js Application

Release Date: March 6, 2026

Due Date: March 22, 2026, 11:59 PM EST

Weight: 12.5% of final grade

Overview

Building on Assignment 3, you will convert the Paper Management System into a full-stack Next.js App Router application using:

  • Server Components
  • Server Actions
  • Prisma ORM
  • TypeScript
  • Tailwind CSS
  • shadcn/ui

You will:

  • Display papers from PostgreSQL
  • Create papers with existing authors on a dedicated page using Server Actions
  • Create authors on a dedicated page using Server Actions
  • Use TypeScript with Prisma-generated types for type safety
  • Apply clean and consistent styling with Tailwind CSS and shadcn/ui

Learning Objectives

After completing this assignment, you will be able to:

  • Build a full-stack Next.js application with TypeScript
  • Use Server Components to fetch and display typed data
  • Implement Server Actions for typed data mutations
  • Integrate Prisma ORM in a Next.js app
  • Apply consistent Tailwind CSS styling
  • Configure and use shadcn/ui components

Requirements

Before you start coding

After reading the assignment requirements below, pause and think about what you expect will be the hardest part of this assignment. This will help you answer Q1 in reasoning.md, which you will submit together with your code.

See the AI Usage Policy for details.

The detailed requirements can be found in the next Detailed Requirements section.

Starter Code

Download the starter code archive:

curl -o starter-code.tar.gz https://www.eecg.utoronto.ca/~cying/courses/ece1724-web/assignments/assignment-4/starter-code.tar.gz

Alternatively, download the archive here.

The archive includes:

assignment-4/ ├── app/ │ ├── papers/ │ │ └── create/ │ │ └── page.tsx # Create paper page │ ├── authors/ │ │ └── create/ │ │ └── page.tsx # Create author page │ ├── api/ │ │ └── papers/ │ │ └── route.ts # API endpoint │ ├── favicon.ico # Default favicon (auto-served by Next.js) │ ├── globals.css # Tailwind setup │ ├── layout.tsx # Root layout (DO NOT MODIFY) │ └── page.tsx # Home page ├── components/ │ ├── PaperCard.tsx # Paper display │ ├── PaperForm.tsx # Paper form for creating new paper │ ├── PaperList.tsx # Paper list │ └── CreatePaperForm.tsx # Client-side wrapper for PaperForm ├── lib/ │ ├── actions.ts # Server Actions │ ├── prisma.ts # Prisma Client (DO NOT MODIFY) │ └── ui.ts # Shared Tailwind class strings (DO NOT MODIFY) ├── prisma/ │ └── schema.prisma # From Assignment 2 ├── eslint.config.mjs # ESLint config (DO NOT MODIFY) ├── next-env.d.ts # TypeScript declarations for Next.js (DO NOT MODIFY) ├── next.config.ts # Next.js config (DO NOT MODIFY) ├── package.json # Dependencies and scripts ├── postcss.config.mjs # PostCSS config (DO NOT MODIFY) ├── prisma.config.ts # Prisma ORM config (DO NOT MODIFY) ├── tsconfig.json # TypeScript config (DO NOT MODIFY) └── tests/ ├── reset.sql # SQL script used to reset database tables └── sample.test.ts # Sample Playwright test cases
  • Next.js + TypeScript + Tailwind setup
  • Prisma configuration
  • Component skeletons with TODO comments
  • Sample Playwright tests

Do not modify:

  • app/layout.tsx
  • app/favicon.ico: Default favicon (auto-served by Next.js)
  • lib/prisma.ts and prisma.config.ts
  • Next.js/TypeScript configuration files (e.g., next*.ts, eslint.config.js, tsconfig.json)
  • lib/ui.ts: Shared Tailwind class strings to keep styling consistent across components

shadcn/ui Setup Required

The starter code does not include files generated by shadcn/ui.

You must:

  1. Initialize shadcn/ui
    • Choose Neutral as the base color
  2. Add the Button component
    • This is only component you need from shadcn/ui for this assignment

This will update app/globals.css and generate lib/utils.ts, components.json, and components/ui/button.tsx. These files are not provided. You must generate them yourself.

Paper Management

  • Display Papers: Show all papers from the database on the home page
  • Create Papers: Add new papers with existing authors via a form on /papers/create
  • Create Authors: Add new authors via a form on /authors/create

Next.js Implementation

  • Provide an API route (/api/papers) to fetch papers
  • Use Server Components for fetching and rendering papers for the home page
  • Use Server Actions for creating papers and authors with navigation to /
  • Use Prisma ORM with TypeScript types to interact with the database
  • Implement Suspense for loading states
  • Use Tailwind CSS and shadcn/ui Button component

Detailed Requirements

Home Route ("/") - Server Component

Page Layout

Home Page

In app/page.tsx

  • Main heading: Paper Management System (text-3xl font-bold)
  • Two buttons (shadcn/ui Button):
    • Create New Paper (/papers/create)
    • Create New Author (/authors/create)
  • Papers section:
    • Subheading: Papers (text-xl font-semibold)

Data Fetching

In app/page.tsx

  • Fetch papers from /api/papers

  • Return an object with:

    • papers: Array of fetched papers on success
    • error: null on success, or Error loading papers if fetch fails
  • If error occurs, display Error loading papers with

    <p data-testid="papers-error" className="text-sm text-muted-foreground"></p>

    Error Loading Papers

  • Otherwise, use PaperList to render papers, wrapped in <Suspense> with a fallback of

    <p className="text-sm">Loading papers...</p>

Paper List Display

In components/PaperList.tsx

  • Display each paper using components/PaperCard.tsx:

    • Title
      <h3 className="text-lg font-semibold leading-snug"></h3>
    • Publication venue
      <p className="text-sm text-muted-foreground"></p>
    • Year
      <p className="text-sm text-muted-foreground"></p>
    • Author names (comma-separated)
  • If there is no paper in the database, show

    <p className="text-sm text-muted-foreground">No papers found</p>

Create Paper Route (/papers/create)

Page Layout

Create New Paper

  • Heading: Create New Paper (text-2xl font-bold)
  • A Back button (shadcn/ui Button) at the top-right that links to the home page (/)
  • Fetch authors server-side in app/page.tsx with ascending id order
  • Pass authors to CreatePaperForm
  • Use CreatePaperForm (a Client Component) to render:
    • PaperForm with:

      • Title:

        <input type="text" name="title" id="title" className={inputClass} />
      • Published In: Style with className={inputClass}

      • Year: Style with className={inputClass}

      • Authors:

        <select multiple name="authorIds" id="authorIds" className={selectClass} data-testid="author-dropdown" >
        • Show No authors available in dropdown if author list is empty
    • Submit button:

      <Button data-testid="create-paper-btn" type="submit"> Create Paper </Button>

Server Action createPaper

In lib/actions.ts

  • Validation: On submit, validate in this order and display only the first error encountered:

    1. Title is required if empty string or string containing only whitespace
    2. Publication venue is required if empty string or string containing only whitespace
    3. Publication year is required if empty
    4. Valid year after 1900 is required if not an integer greater than 1900
    5. Please select at least one author if no authors selected
  • Success: Creates the paper in the database and returns (no redirect here)

Client-Side Logic

In components/CreatePaperForm.tsx

  • On success:

    1. Show Paper created successfully (data-testid="status-message", "text-sm")
    2. Wait 3 seconds
    3. Redirect to /
  • On error: Show the error message from the Server Action (data-testid="status-message", "text-sm text-red-800")

    Validation Example

Create Author Route (/authors/create)

Page Layout

Create New Author

  • Heading: Create New Author (text-2xl font-bold)
  • A Back button (shadcn/ui Button) at the top-right that links to the home page (/)
  • Form with:
    • Name:
      <input type="text" name="name" id="name" className={inputClass} />
    • Email (optional): Style with className={inputClass}
    • Affiliation (optional): Style with className={inputClass}
  • Submit button: Create Author
    • shadcn/ui Button with data-testid="create-author-btn"

Server Action createAuthor

In lib/actions.ts

  • Validation:
    • Name is required if empty string or string containing only whitespace
  • Success: Creates the author in the database and returns (no redirect here)

Client-Side Logic

In app/authors/create/page.tsx

  • On success:

    1. Show Author created successfully (data-testid="status-message", "text-sm")
    2. Wait 3 seconds
    3. Redirect to /
  • On error: Show Name is required or Error creating author (data-testid="status-message", "text-sm text-red-800")

    Validation Example 2

API Route (/api/papers)

Implement app/api/papers/route.ts

  • Use Prisma to retrieve all papers in the database with ascending id order
  • Include the authors relation in the query to fetch each paper’s associated authors
  • Return the results as a JSON response using NextResponse.json()
    • Note: In Assignment 4, the /api/papers route should return a JSON array of papers.

      This differs slightly from Assignment 2, where the API response wrapped the data in an object { papers, total, limit, offset }. Since Assignment 4 is a Next.js full-stack application, the API response format is simplified for this assignment.

  1. Configure Prisma + migration
  2. Implement API route
  3. Implement Home page
  4. Implement PaperCard and PaperList
  5. Implement create paper flow
    • In app/papers/create/page.tsx, fetch authors server-side and pass them to CreatePaperForm
    • Implement components/CreatePaperForm.tsx to handle form submission
    • Implement components/PaperForm.tsx to render the form UI and the submit button, including client-side logic for success/error messages and a 3-second redirect
    • Add the createPaper Server Action in src/lib/actions.ts with validation and paper creation logic
  6. Implement create author flow
    • Update app/authors/create/page.tsx to render the form UI and handle submission with client-side logic for success/error messages and a 3-second redirect
    • Add the createAuthor Server Action in src/lib/actions.ts with validation and author creation logic
  7. Test all flows carefully

Getting Started

  1. Download the starter code archive.

  2. After extracting the archive, install dependencies:

    npm install
  3. Create .env with your PostgreSQL credentials

    .env
    DATABASE_URL="postgresql://your-os-username@localhost:5432/paper_management?schema=public" NEXT_PUBLIC_API_BASE_URL="http://localhost:3000"
  4. Run Prisma migration

    npx prisma migrate dev --name init
  5. Generate the Prisma Client

    npx prisma generate
  6. Start the development server

    npm run dev

Testing Your Implementation

A Playwright-based test suite /tests/sample.test.ts is provided in the starter code to help you verify your implementation.

This sample test suite focuses on core functionality and correspond to approximately 70% of the autograder score. The autograder will run similar but more comprehensive tests that check all explicitly stated requirements in this handout, and it will not test anything beyond what is specified.

  1. Start Your Server

    In one terminal, from assignment-4/:

    npm run dev

    Make sure the development server is running before executing the tests.

  2. Run the Tests

    In another terminal, from assignment-4/:

    • Run the sample test suite:

      npx playwright test tests/sample.test.ts

Submission Instructions

Please follow the steps below to submit a .tar.gz archive to Quercus.

Prepare reasoning.md

In addition to the required code files, you must submit a short reflection file named reasoning.md.

  • Create a folder called assignment-4.
  • Inside the assignment-4 folder, create a file called reasoning.md.
  • Answer the three required questions described in the AI Usage Policy page.

Important Notes

  • Keep your responses brief and specific, with a total length of no more than 300 words.
  • This file is intended to capture your genuine thinking process. It is not a long writing assignment required polishing.
  • reasoning.md is worth 5% of this assignment grade. Its grading rubric is on the AI Usage Policy page.

Generate the Archive

Make your assignment-4 directory have exactly the following structure:

          • route.ts
          • page.tsx
          • page.tsx
      • favicon.ico
      • globals.css
      • layout.tsx
      • page.tsx
        • button.tsx
      • CreatePaperForm.tsx
      • PaperCard.tsx
      • PaperForm.tsx
      • PaperList.tsx
      • actions.ts
      • prisma.ts
      • ui.ts
      • utils.ts
      • schema.prisma
    • components.json
    • eslint.config.mjs
    • next-env.d.ts
    • next.config.ts
    • package.json
    • postcss.config.mjs
    • prisma.config.ts
    • tsconfig.json
    • reasoning.md

From the parent directory that contains the assignment-4 folder, create the submission archive:

tar zcvf 1234567890-1724-a4.tar.gz --exclude='.DS_Store' assignment-4
  • Replace 1234567890 with your student number.
  • The archive must contain exactly one top-level folder named assignment-4 with the required files inside.
  • Do not include node_modules/, .next/ or any build directories.

Any submission that fails to meet the specified format or structure and requires TA intervention for the autograder to work will receive a 30-point deduction.

Verify Your Submission

To avoid a 30-point deduction, use the provided Python script to verify your submission.

  1. Download verify_a4_submission.py to the same directory as your .tar.gz file:

    curl -o verify_a4_submission.py https://www.eecg.utoronto.ca/~cying/courses/ece1724-web/assignments/assignment-4/verify_a4_submission.py

    Alternatively, download the script here.

  2. Ensure you have Python installed. If not, install it from python.org .

  3. From the same directory as your 1234567890-1724-a4.tar.gz, run:

    python verify_a4_submission.py 1234567890-1724-a4.tar.gz
    • Replace 1234567890 with your student number.

Submit to Quercus

Submit your .tar.gz file to Quercus .

Note

You are allowed to submit unlimited times. Only your latest submission before the deadline will be graded. Quercus automatically appends a suffix to the file name after the first submission — this will not affect grading.

Grading Scheme (100 Points)

The total 100 points correspond to 95% of this assignment’s grade. The remaining 5% comes from your reasoning.md submission.

Grades were released on March 24. If you would like to request a remark, please do so by March 27 (last lecture), following the instructions in the syllabus.

Home Route (/): Paper List Display (34 Points)

  • Page Layout and Links (12 points)

    • Rendering (4 points): Displays Paper Management System heading (1 point), Papers subheading (1 point), Create New Paper button (1 point) and Create New Author button (1 point)
    • Navigation (4 points): Buttons navigate to /papers/create (2 points) and /authors/create (2 points)
    • Styling with Tailwind and shadcn/ui (4 points)
      • Main heading uses text-3xl font-bold (1 point)
      • Subheading uses text-xl font-semibold (1 point)
      • Create New Paper use shadcn/ui <Button> component (1 point)
      • Create New Author use shadcn/ui <Button> component (1 point)
  • Paper List Display via Server Component (22 points)

    • Loading State with Suspense (3 points): Shows Loading papers... during fetch
    • Error State (2 points): Shows Error loading papers (data-testid="papers-error") if fetch from /api/papers fails
    • Empty State (2 points): Shows No papers found when database is empty.
    • Paper Rendering (15 points): Add two papers to the database (first paper has two author; second paper has one author)
      • First paper displays correct title (2 points), venue (2 points), year (2 points), and comma-separated authors (2 points)
      • Second paper displays correct title (2 points), venue (2 points), year (2 points), and author (1 point)

Create Paper Route (/papers/create) (42 Points)

  • Page Rendering (13 points)

    • Layout (6 points): Displays Create New Paper heading (1 point), Back button (1 point), form with title (1 point), publishedIn (1 point), and year (1 point) inputs, and Create Paper button (1 point)
    • Create Paper use shadcn/ui <Button> component (1 point)
    • Author Dropdown (6 points): Renders multi-select dropdown (data-testid="author-dropdown") with all authors from DB (3 points, 3 authors, 1 point each) and shows No authors available if empty (2 points)
    • Back button navigates to homapage (1 point)
  • Paper Creation with Server Action (29 points)

    • Validation (15 points): Throws correct first-error messages from createPaper action:
      • Title is required if empty string "" (2 points)
      • Message uses "text-sm text-red-800" (1 point)
      • Title is required if string containing only whitespace " " (2 point)
      • Publication venue is required if empty string "" (2 points)
      • Publication venue is required if string containing only whitespace " " (2 points)
      • Publication year is required if empty string "" (2 points)
      • Valid year after 1900 is required if 1900 (2 points)
      • Please select at least one author if none selected (2 points)
    • Successful Creation (14 points): Creates paper in DB, shows success message, redirects after 3s
      • Shows Paper created successfully (data-testid="status-message") (2 points)
      • Paper created successfully uses "text-sm" (1 point)
      • Cretes a paper with one author: Paper in DB with correct title (1 point), publishedIn (1 point), year (1 point), and author (1 point)
      • Redirects to / after 3s (2 points)
      • Cretes a paper with two authors: Paper in DB with correct title (1 point), publishedIn (1 point), year (1 point), and authors (2 points)

Create Author Route (/authors/create) (20 Points)

  • Page Rendering (8 points)

    • Displays Create New Author heading (1 point), Back button (1 point), form with name (1 point), email (1 point), and affiliation (1 point) inputs, and Create Author button (1 point)
    • Create New Author heading uses "text-2xl font-bold" (1 point)
    • Button button navigates to homapage (1 point)
  • Author Creation with Server Action (12 points)

    • Validation (4 points):
      • Name is required if empty string "" (2 points)
      • Message uses "text-sm text-red-800" (1 point)
      • Name is required if string containing only whitespace " " (2 point)
    • Successful Creation (8 points): Creates one author in DB, shows success message, redirects after 3s
      • Shows Author created successfully (data-testid="status-message") (2 points)
      • Author created successfully uses "text-sm" (1 point)
      • Author in DB with correct name (1 point), email (1 point), and affiliation (1 point)
      • Redirects to / after 3s (2 points)

API Route (4 Points)

  • API Route (/api/papers) (4 points)
    • Returns all papers with authors in ascending id order (2 points)
    • Includes authors relation in response (2 points)
      • Test: Insert 2 papers with authors, fetch /api/papers, verify JSON response matches DB with authors array per paper

Resources

Questions?

  1. Discussion Board:

    • Post questions on course discussion board 
    • Search existing discussions first
    • Use clear titles and provide relevant code snippets
  2. Office Hours:

    • Time: Wednesdays, 4:00 PM — 5:00 PM
    • Location: Room 7206, Bahen Centre for Information Technology
  3. Tips for Getting Help:

    • Start early to allow time for questions
    • Be specific about your problem
    • Share what you’ve tried
    • Include relevant error messages
Last updated on