Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,14 @@ jobs:
- name: Run linter
run: npm run lint

- name: Run unit tests
run: npm run test:unit

- name: Run build
run: npm run build

- name: Install Playwright browser
run: npx playwright install --with-deps chromium

- name: Run E2E tests
run: npm run test:e2e
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ yarn-error.log*
next-env.d.ts

# playwright
/test-results/
/test-results/*
/playwright-report/
!/test-results/.gitkeep
101 changes: 81 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,97 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
# MLBoost Frontend

## Getting Started
Production-grade frontend for the MLBoost coding practice platform.

First, run the development server:
## Highlights

- LeetCode-inspired UI shell and problem workflow
- Auth-gated app routes via middleware
- Problemset filters, search, company insights, and status tracking
- Coding arena with Monaco editor, run/submit console, history replay, and editorial unlock
- Profile analytics, contest dashboard, explore/learning experiences
- Mock-first API layer with live-backend switch support
- Sentry + web-vitals instrumentation hooks

## Tech Stack

- Next.js 16 (App Router)
- React 19 + TypeScript
- Tailwind CSS 4
- Vitest + Testing Library
- Playwright

## Quick Start

```bash
npm ci
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
Open [http://localhost:3000/login](http://localhost:3000/login)

## Scripts

- `npm run dev` - start dev server
- `npm run lint` - run ESLint
- `npm run test:unit` - run Vitest tests
- `npm run test:e2e` - run Playwright tests
- `npm run build` - production build
- `npm run start` - start built app

## Environment Variables

Create `.env.local` as needed:

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
```bash
# API
NEXT_PUBLIC_API_MODE=mock # mock | live | auto
NEXT_PUBLIC_API_URL=http://localhost:5001/api
NEXT_PUBLIC_API_RETRY_COUNT=2
NEXT_PUBLIC_API_TIMEOUT_MS=8000
NEXT_PUBLIC_API_FALLBACK_TO_MOCK=true

# Analytics
NEXT_PUBLIC_ANALYTICS_ENDPOINT=

# App environment
NEXT_PUBLIC_APP_ENV=development

# Sentry (browser)
NEXT_PUBLIC_SENTRY_DSN=
NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE=0.1

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
# Sentry (server/edge)
SENTRY_DSN=
SENTRY_TRACES_SAMPLE_RATE=0.1
```

## Live API Contract (expected paths)

## Learn More
- `POST /auth/login`
- `POST /auth/signup`
- `GET /problems`
- `GET /problems/:slug`
- `POST /submissions/run`
- `POST /submissions`
- `GET /tracks`
- `GET /profile/me`

To learn more about Next.js, take a look at the following resources:
## Testing Notes

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
- Unit tests use JSDOM + local storage mocks from `src/test/setup.ts`.
- E2E tests require Playwright browsers:

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
```bash
npx playwright install chromium
```

## Deploy on Vercel
## CI

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
GitHub Actions pipeline runs:

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
1. `npm ci`
2. `npm run lint`
3. `npm run test:unit`
4. `npm run build`
5. `npx playwright install --with-deps chromium`
6. `npm run test:e2e`
6 changes: 3 additions & 3 deletions e2e/auth-flow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { expect, test } from "@playwright/test";
test("user can login and open problems page", async ({ page }) => {
await page.goto("/login");

await expect(page.getByRole("heading", { name: /sign in to continue/i })).toBeVisible();
await expect(page.getByRole("heading", { name: /mlboost/i })).toBeVisible();

await page.getByLabel("Email").fill("playwright@example.com");
await page.getByLabel("Password").fill("password123");
await page.getByRole("button", { name: "Sign in" }).click();
await page.getByTestId("auth-submit").click();

await expect(page).toHaveURL(/\/problems/);
await expect(page.getByRole("heading", { name: /^problems$/i })).toBeVisible();
await expect(page.getByRole("heading", { name: /^problemset$/i })).toBeVisible();
});
29 changes: 14 additions & 15 deletions e2e/navigation-pages.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,21 @@ test("all primary pages load and are not blank", async ({ page }) => {

await page.getByLabel("Email").fill("navigation@example.com");
await page.getByLabel("Password").fill("password123");
await page.getByRole("button", { name: "Sign in" }).click();
await page.getByTestId("auth-submit").click();

const checks = [
{ link: "Dashboard", pathSuffix: "/", heading: /^dashboard$/i },
{ link: "Problems", pathSuffix: "/problems", heading: /^problems$/i },
{ link: "Problems", pathSuffix: "/problems", heading: /^problemset$/i },
{
link: "Competitions",
link: "Contest",
pathSuffix: "/competitions",
heading: /^competitions$/i,
heading: /^contest$/i,
},
{ link: "Learn", pathSuffix: "/learn", heading: /^learn$/i },
{ link: "Progress", pathSuffix: "/progress", heading: /^progress$/i },
{ link: "Explore", pathSuffix: "/learn", heading: /^explore$/i },
{ link: "Discuss", pathSuffix: "/progress", heading: /^progress$/i },
];

for (const item of checks) {
await page.getByRole("link", { name: item.link }).click();
await page.getByRole("link", { name: new RegExp(`^${item.link}$`, "i") }).click();
await expect(page).toHaveURL(new RegExp(`${item.pathSuffix}$`));
await expect(page.getByRole("heading", { name: item.heading })).toBeVisible();
}
Expand All @@ -31,9 +30,9 @@ test("problem arena route opens from problems list", async ({ page }) => {

await page.getByLabel("Email").fill("arena@example.com");
await page.getByLabel("Password").fill("password123");
await page.getByRole("button", { name: "Sign in" }).click();
await page.getByTestId("auth-submit").click();

await page.getByRole("link", { name: "Problems" }).click();
await page.getByRole("link", { name: /^problems$/i }).click();
await page.getByText("KNN Classifier on Iris", { exact: false }).first().click();

await expect(page).toHaveURL(/\/problems\/knn-classifier-iris/);
Expand All @@ -42,16 +41,16 @@ test("problem arena route opens from problems list", async ({ page }) => {
).toBeVisible();
});

test("topics in sidebar navigate to filtered problems", async ({ page }) => {
test("category pills filter problems in problemset view", async ({ page }) => {
await page.goto("/login");

await page.getByLabel("Email").fill("topics@example.com");
await page.getByLabel("Password").fill("password123");
await page.getByRole("button", { name: "Sign in" }).click();
await page.getByTestId("auth-submit").click();

await page.getByRole("link", { name: "Learn" }).click();
await page.getByRole("link", { name: /^problems$/i }).click();
await page.getByRole("button", { name: "Data Preprocessing" }).click();

await expect(page).toHaveURL(/\/problems\?category=Data\+Preprocessing$/);
await expect(page.getByRole("heading", { name: /^problems$/i })).toBeVisible();
await expect(page.getByRole("heading", { name: /^problemset$/i })).toBeVisible();
await expect(page.getByText("Data Preprocessing", { exact: false })).toBeVisible();
});
38 changes: 0 additions & 38 deletions middleware.ts

This file was deleted.

Loading
Loading