-
Notifications
You must be signed in to change notification settings - Fork 0
Phase 2: split export layer + fix PDF truncation #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| # Interview Lab — Thorough Fix Plan | ||
|
|
||
| > Audited 2026-07-19. A sequenced, merge-ready plan. Each phase: **goal → files → changes → verification**. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Remove the future-dated documentation claims. Both documents claim work occurred on July 19, 2026, but the current date is July 18, 2026.
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| > Phases are independently shippable; **Phase 0 must land first**. | ||
|
|
||
| --- | ||
|
|
||
| ## Phase 0 — Reconcile Reality & Delete Dead Code | ||
| *Why first: docs/architecture are wrong. Fix the map before the journey.* | ||
|
|
||
| ### 0.1 Documentation truth-up | ||
| **Files:** `AGENTS.md`, `README.md`, `REDESIGN-PLAN.md`, `REMEDIATION_PLAN.md`, `docs/architecture.md` | ||
| - Rewrite AGENTS.md "Design System" section: replace *"Ethereal Glass / OLED black / sky `#007EFF`"* with the live **"Field Manual" light theme** (`#FAFAF7` bg, `#FF6B35` accent, `FieldCard`/`FieldButton`). | ||
| - README Tech Stack: change `UI` row to "Tailwind v4 + Field Manual design system"; fix `Export` row ("pdfkit" → "pdfkit + manual PDF builder" or remove pdfkit after Phase 3). | ||
| - `REMEDIATION_PLAN.md`: delete false claim that `downloads/[id]/route.ts` is 879 lines (it's 53). | ||
| - Mark `REDESIGN-PLAN.md` as **superseded** (glass redesign was reverted) — or delete it. | ||
|
|
||
| ### 0.2 Delete dead code | ||
| | File | Reason | | ||
| |------|--------| | ||
| | `src/lib/browser-llm-integration.ts` (338 lines) | Not imported anywhere; fake rule-based AI | | ||
| | `src/components/interview-lab/UpgradeModal.tsx` | Stub, no-op | | ||
| | `src/components/interview-lab/SubscriptionBanner.tsx` | Stub, no-op | | ||
| | `src/lib/pricing.ts` (tier config) — *optional* | Only powers stubs; keep if PricingPage ever real | | ||
|
Comment on lines
+19
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Add the required blank line before the table. The table needs a blank line after the preceding paragraph to satisfy Markdown linting. 🧰 Tools🪛 markdownlint-cli2 (0.23.0)[warning] 19-19: Tables should be surrounded by blank lines (MD058, blanks-around-tables) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| **Verify:** `grep -r "browser-llm-integration\|UpgradeModal\|SubscriptionBanner" src` → no results; `bun run build` passes. | ||
|
|
||
| --- | ||
|
|
||
| ## Phase 1 — SOLID: AI Layer Refactor (SRP + OCP + DIP) | ||
| *Highest leverage: 4 routes share ~75-line duplicated JSON-extract loop, each calls the SDK directly.* | ||
|
|
||
| ### 1.1 `src/lib/ai/client.ts` — AIProvider abstraction (DIP) | ||
| ```ts | ||
| export interface AIProvider { | ||
| complete(system: string, user: string, opts?: { schema?: ZodSchema; signal?: AbortSignal }): Promise<unknown>; | ||
| } | ||
| export class ZAIProvider implements AIProvider { /* timeout 30s, abort, retry-once */ } | ||
| export const ai: AIProvider = new ZAIProvider(); | ||
| ``` | ||
| - Add `AbortSignal.timeout(30000)` + `try/catch` around `ZAI.create().chat.completions.create`. | ||
|
|
||
| ### 1.2 `src/lib/ai/handlers.ts` — `createAIHandler` factory (OCP) | ||
| ```ts | ||
| export function createAIHandler<T>(buildPrompt, schema: ZodSchema<T>) { | ||
| return async (req) => { /* auth → call ai.complete with schema → validate → return */ }; | ||
| } | ||
| ``` | ||
| - Centralize `extractJsonArray`/`extractJsonObject` parsing into tested util `src/lib/ai/json.ts`. | ||
|
|
||
| ### 1.3 Split routes | ||
| - `src/lib/ai/coach.ts`, `resume.ts`, `cover-letter.ts`, `assessment.ts` — each exports `buildPrompt()` + a zod `schema`. | ||
| - Refactor `src/app/api/ai/{coach,resume-review,cover-letter,assessment-score}/route.ts` to ~15 lines each calling `createAIHandler`. | ||
|
|
||
| ### 1.4 Add zod schemas | ||
| - `src/lib/ai/schemas.ts`: `CoachFeedbackSchema`, `ResumeReviewSchema`, `CoverLetterSchema`, `AssessmentScoreSchema` (replaces runtime `as` casts). | ||
|
|
||
| **Verify:** `__tests__/ai/client.test.ts` (mock `z-ai-web-dev-sdk`), `handlers.test.ts` (valid + malformed JSON → 500), `bun run test`. | ||
|
|
||
| --- | ||
|
|
||
| ## Phase 2 — SOLID: Export Layer (SRP) | ||
| ### 2.1 `src/lib/export/docx.ts` | ||
| - Move `generateDocx` out of `export/route.ts` into its own module. | ||
|
|
||
| ### 2.2 `src/lib/export/pdf.ts` — replace hand-rolled byte builder | ||
| - Use already-installed `pdfkit` (currently unused). Fixes **silent truncation** (`if (y < 50) break`) by paginating. | ||
| - Add `content` size guard (reject > 50k chars) to prevent abuse. | ||
|
|
||
| ### 2.3 `src/app/api/export/route.ts` | ||
| - Becomes ~12 lines: auth → dispatch to `docx.ts` / `pdf.ts`. | ||
|
|
||
| **Verify:** `export.test.ts` generates real .docx/.pdf, asserts multi-page content isn't truncated. | ||
|
|
||
| --- | ||
|
|
||
| ## Phase 3 — SOLID: Entitlement Honesty (LSP) | ||
| ### 3.1 `src/lib/subscription/entitlement.ts` | ||
| ```ts | ||
| export interface EntitlementService { canAccess(feature): boolean; tier: string; } | ||
| export class FreeEntitlement implements EntitlementService { canAccess() { return true; } } | ||
| ``` | ||
| - `subscription-guard.ts` returns real interface; `use-subscription.ts` returns `FreeEntitlement` honestly (remove fake `-1`/no-op that *implies* gating). | ||
|
|
||
| **Verify:** `subscription.test.ts`. | ||
|
|
||
| --- | ||
|
|
||
| ## Phase 4 — Component Decomposition (SRP) | ||
| ### 4.1 `MockInterview.tsx` (618 →) | ||
| `src/components/interview-lab/mock-interview/{Setup,ActiveSession,Complete,useInterview}.tsx` | ||
|
|
||
| ### 4.2 Standardize error/empty states | ||
| - `ResumeLab` raw red `<div>` → `Alert` component (already built in `ui/`). | ||
| - Add skeletons to `MockInterview`/`ResumeLab` to match `DashboardView`. | ||
|
|
||
| ### 4.3 A11y fix | ||
| - `QuestionBank`: remove nested interactive (inner "Practice" button inside `role="button"` div) → make card a real `<article>` with a separate button. | ||
|
|
||
| **Verify:** `bun run lint`, component render tests. | ||
|
|
||
| --- | ||
|
|
||
| ## Phase 5 — Infra & Correctness | ||
| ### 5.1 Rate limiter (`middleware.ts`) | ||
| - Replace in-memory `Map` with **Upstash Redis** (`@upstash/ratelimit`) so it works across Vercel serverless. | ||
| - Trust `x-forwarded-for` only behind Vercel proxy; add `x-vercel-ip` fallback. | ||
|
|
||
| ### 5.2 Font drift (`globals.css`) | ||
| - `JetBrains Mono`/`Inter` declared but unused → align to Space Grotesk + Plus Jakarta Sans (per spec) or update spec. | ||
|
|
||
| ### 5.3 `db.ts` | ||
| - Gate `log: ['query']` behind explicit `LOG_QUERIES=1` env (confirm off in prod). | ||
|
|
||
| --- | ||
|
|
||
| ## Effort & Sequencing | ||
|
|
||
| | Phase | Work | Est. | Risk | | ||
| |-------|------|------|------| | ||
| | 0 | Doc truth-up + dead-code deletion | 1 day | 🟢 none | | ||
| | 1 | AI layer SOLID refactor + zod | 4 days | 🟡 medium (test coverage needed) | | ||
| | 2 | Export layer split + pdfkit | 1 day | 🟡 medium | | ||
| | 3 | Entitlement honesty | 1 day | 🟢 low | | ||
| | 4 | Component decomposition + UI polish | 4 days | 🟡 medium | | ||
| | 5 | Rate limiter + fonts + db | 2 days | 🟡 medium | | ||
|
|
||
| **Total: ~13 dev-days.** Phases are independently shippable; Phase 0 must land first. | ||
|
|
||
| --- | ||
|
|
||
| ## Suggested first PR | ||
| **"Phase 0: Reconcile docs & remove dead code"** — smallest diff, zero behavioral risk, unblocks everything. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,14 +37,14 @@ for aspiring Amazon Virtual Assistants. | |
| | Layer | Technology | | ||
| |-------|-----------| | ||
| | **Framework** | Next.js 16.1.1 (App Router, standalone) | | ||
| | **Runtime** | Bun | | ||
| | **Database** | Prisma v6 + SQLite (dev) / PostgreSQL (prod) | | ||
| | **Runtime** | Bun (CI uses npm) | | ||
| | **Database** | Prisma v6 + PostgreSQL (PostgreSQL only) | | ||
| | **Auth** | Custom JWT sessions (jose) + HttpOnly cookies | | ||
| | **UI** | Tailwind CSS v4 + custom glass design system | | ||
| | **UI** | Tailwind CSS v4 + "Field Manual" light design system | | ||
| | **Icons** | Phosphor Icons (light weight) | | ||
| | **Fonts** | Space Grotesk (headings) + Plus Jakarta Sans (body) | | ||
| | **AI** | Z AI Web Dev SDK for coaching, scoring, content generation | | ||
| | **Export** | docx, PDF (pdfkit), Excel (exceljs) | | ||
| | **Export** | docx, PDF (manual builder / pdfkit), Excel (exceljs) | | ||
|
Comment on lines
+40
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== files ==\n'
git ls-files | rg '^(README\.md|AGENTS\.md|REMEDIATION_PLAN\.md|remediation_plan\.md|package\.json|bun\.lockb|bun\.lock|package-lock\.json|pnpm-lock\.yaml|yarn.lock|\.github/workflows/.*)$'
printf '\n== README snippets ==\n'
sed -n '1,220p' README.md
printf '\n== AGENTS.md ==\n'
if [ -f AGENTS.md ]; then sed -n '1,220p' AGENTS.md; fi
printf '\n== REMEDIATION_PLAN.md ==\n'
if [ -f REMEDIATION_PLAN.md ]; then sed -n '1,220p' REMEDIATION_PLAN.md; fi
printf '\n== package.json ==\n'
if [ -f package.json ]; then sed -n '1,240p' package.json; fi
printf '\n== ci/workflow refs to bun/npm ==\n'
rg -n '\b(bun|npm|pnpm|yarn)\b' .github/workflows package.json README.md AGENTS.md REMEDIATION_PLAN.md 2>/dev/null || trueRepository: projectamazonph/Interview-lab Length of output: 25475 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== .github/workflows/ci.yml ==\n'
sed -n '1,220p' .github/workflows/ci.yml
printf '\n== package-lock root metadata ==\n'
python3 - <<'PY'
import json
from pathlib import Path
p = Path("package-lock.json")
data = json.loads(p.read_text())
print(data.get("name"))
print(data.get("lockfileVersion"))
print("packages-root:", data.get("packages", {}).get("", {}).keys())
PYRepository: projectamazonph/Interview-lab Length of output: 2896 Align the package-manager guidance with CI README.md (and AGENTS.md) still point contributors to Bun, but CI runs 🤖 Prompt for AI Agents |
||
|
|
||
| ## 🚀 Getting Started | ||
|
|
||
|
|
@@ -90,7 +90,7 @@ src/ | |
| │ │ ├── assessments/ # Practice test assessments | ||
| │ │ ├── profile/ # User profile (GET/PUT) | ||
| │ │ ├── dashboard/ # Dashboard aggregation | ||
| │ │ ├── subscription/ # Checkout, status, usage, webhook | ||
| │ │ ├── subscription/ # Removed (product is free) | ||
| │ │ ├── resume/ # Resume upload + AI review | ||
| │ │ ├── cover-letter/ # Cover letter generation | ||
| │ │ ├── ai/ # AI endpoints (coach, scoring, resume, cover) | ||
|
|
@@ -100,17 +100,17 @@ src/ | |
| │ │ └── export/ # DOCX/PDF/Excel export | ||
| │ └── [pages]/ # Page routes | ||
| ├── components/ | ||
| │ ├── interview-lab/ # Page components (glass design system) | ||
| │ └── ui/ # Shared primitives (glass-card, glass-button, etc.) | ||
| │ ├── interview-lab/ # Page components (Field Manual design system) | ||
| │ └── ui/ # Shared primitives (field-card, field-button, etc.) | ||
| ├── lib/ | ||
| │ ├── auth-helpers.ts # Server-side JWT + header auth | ||
| │ ├── pricing.ts # Tier configs (Free/Starter/Pro) and limits | ||
| │ ├── subscription-guard.ts # Feature access checks per tier | ||
| │ ├── pricing.ts # Tier configs (currently unused — product is free) | ||
| │ ├── subscription-guard.ts # Feature access checks (returns allowed: true) | ||
| │ ├── types.ts # TypeScript interfaces | ||
| │ └── utils.ts # cn() utility | ||
| └── hooks/ # Custom React hooks | ||
|
|
||
| prisma/ # Prisma schema (SQLite/PostgreSQL) | ||
| prisma/ # Prisma schema (PostgreSQL) | ||
| __tests__/ # Test suites | ||
| ├── api/ # API route unit tests (218 tests) | ||
| │ ├── interview-session.test.ts | ||
|
|
@@ -133,7 +133,7 @@ docs/ # PRD, architecture, feature specs | |
| bun test | ||
|
|
||
| # Run specific test file | ||
| bun test __tests__/api/subscription.test.ts | ||
| bun test __tests__/api/resume-coverletter.test.ts | ||
|
|
||
| # Run API tests only | ||
| bun test __tests__/api/ | ||
|
|
@@ -149,9 +149,8 @@ bun test __tests__/api/ | |
| | `auth-login.test.ts` | 26 | Login flow, rate limiting, session, email sanitization | | ||
| | `assessments.test.ts` | 20 | Assessment list/get/submit, answer key stripping | | ||
| | `profile-dashboard.test.ts` | 23 | Profile whitelisting, sanitization, dashboard stats | | ||
| | `subscription.test.ts` | 31 | Pricing logic, checkout, tier validation, payments | | ||
| | `resume-coverletter.test.ts` | 25 | Resume CRUD, cover-letter tones, truth flags | | ||
| | **Total** | **218** | **All API routes, all green** | | ||
| | **Total** | **187** | **All API routes, all green** | | ||
|
Comment on lines
152
to
+153
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Reconcile the test counts. The updated total is 187, but the project tree still claims 218 API tests and lists 🤖 Prompt for AI Agents |
||
|
|
||
| Tests use in-memory stubs — no database or server required. | ||
|
|
||
|
|
@@ -161,27 +160,27 @@ Tests use in-memory stubs — no database or server required. | |
| |---------|-------------| | ||
| | `bun run dev` | Dev server with logging | | ||
| | `bun run build` | Production build (standalone) | | ||
| | `bun run db:push` | Push Prisma schema to SQLite | | ||
| | `bun run db:push` | Push Prisma schema to PostgreSQL | | ||
| | `bun run db:generate` | Generate Prisma client | | ||
| | `bun run test` | Run test suite | | ||
| | `bun run lint` | ESLint check | | ||
|
|
||
| ## 🎨 Design System — Ethereal Glass | ||
| ## 🎨 Design System — "Field Manual" (light theme) | ||
|
|
||
| The app uses a premium dark-mode glass aesthetic: | ||
| > The earlier "Ethereal Glass" dark design was reverted. The live app uses a clean light "Field Manual" system. | ||
|
|
||
| - **Palette:** OLED black (`#050505`) with radial gradient orbs, glass-morphism cards, `backdrop-blur-xl` | ||
| - **Colors:** Indigo/violet accents, emerald success, amber warnings, rose danger | ||
| - **Motion:** Custom cubic-bezier curves, Framer Motion scroll reveals, staggered animations | ||
| - **Components:** Double-bezel glass cards, pill CTAs, glass inputs/badges | ||
| - **Palette:** Warm off-white (`#FAFAF7`) with subtle grain/border texture, primary orange (`#FF6B35`) accents | ||
| - **Colors:** Ink scale for text, emerald success, amber warnings, rose danger | ||
| - **Motion:** Framer Motion scroll reveals, stagger, hover physics (no custom cubic-beziers) | ||
| - **Components:** `FieldCard` (solid surface, thin border, soft shadow), `FieldButton` (default/outline/ghost), `FieldBadge`, `Alert` | ||
| - **Icons:** Phosphor Icons in `weight="light"` — no thick-stroked icons | ||
| - **Typography:** Space Grotesk for headings, Plus Jakarta Sans for body | ||
|
Comment on lines
+168
to
177
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Mark the redesign-plan link as historical. The Field Manual section correctly says the Ethereal Glass design was reverted, but the documentation index still describes 🤖 Prompt for AI Agents |
||
|
|
||
| ## 🔐 Environment Variables | ||
|
|
||
| | Variable | Description | | ||
| |----------|-------------| | ||
| | `DATABASE_URL` | SQLite connection string (`file:./dev.db`) or PostgreSQL URL | | ||
| | `DATABASE_URL` | PostgreSQL connection URL | | ||
| | `JWT_SECRET` | Secret for signing JWT session tokens | | ||
| | `NEXT_PUBLIC_APP_URL` | App base URL | | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: projectamazonph/Interview-lab
Length of output: 20065
🏁 Script executed:
Repository: projectamazonph/Interview-lab
Length of output: 173
Align the PDF export docs with the shipped implementation.
AGENTS.mdandREADME.mdshould describe the custom paginated PDF builder insrc/lib/export/pdf.tsconsistently; leaveFIX-PLAN.mdas future work if you still want a pdfkit migration plan.📍 Affects 3 files
AGENTS.md#L27-L27(this comment)FIX-PLAN.md#L66-L68README.md#L47-L47🤖 Prompt for AI Agents