Skip to content

JacosNet/pdr-cloud

Repository files navigation

PDR.cloud Technical Assessment

A fullstack monorepo built with Nx, Angular 18+, Angular Material, and NestJS, implementing a user management system with shared Zod validation.


Tech Stack

  • Monorepo: Nx
  • Frontend: Angular 18+, Angular Material (Material 3), SCSS
  • Backend: NestJS
  • Validation: Zod (shared between frontend and backend)
  • Language: TypeScript

Project Structure

pdr-cloud/
  apps/
    frontend/        → Angular app (port 4200)
    api/             → NestJS backend (port 3000)
  libs/
    shared/          → Shared Zod schemas, interfaces, DTOs
  nx.json
  package.json
  tsconfig.base.json

Getting Started

Prerequisites

  • Node.js 18+
  • npm 9+

Install Dependencies

npm install

Seed Initial Data

Copy the provided users.json to the API data folder:

mkdir -p apps/api/data
cp users.json apps/api/data/users.json

Start the Backend

npx nx serve api

API runs at: http://localhost:3000

Start the Frontend

npx nx serve frontend

App runs at: http://localhost:4200

Run Both Simultaneously

Open two terminal windows and run one command in each.


API Endpoints

Method Endpoint Description
GET /users Returns all users
GET /users/:id Returns user by ID
POST /users Creates a new user

Example POST Request

curl -X POST http://localhost:3000/users \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "John",
    "lastName": "Doe",
    "email": "john@doe.com",
    "phoneNumber": "123456789",
    "birthDate": "1990-01-01",
    "role": "admin"
  }'

Features

Backend (NestJS)

  • RESTful API with 3 endpoints
  • Zod validation on POST /users
  • Conditional role-based validation (see below)
  • In-memory storage with file persistence to apps/api/data/users.json
  • Simple write queue to prevent race conditions
  • CORS enabled

Frontend (Angular)

  • Material 3 custom theme with brand colors
  • Sticky top navigation bar
  • User List: Material table with pagination (25/page) and full-name search
  • User Details: Dialog showing full user info loaded from GET /users/:id
  • User Create: Reactive form with live Zod validation and snackbar feedback
  • Smiley: Pure CSS responsive smiley face at /smiley (no images or SVGs)

Shared Library (libs/shared)

  • User interface
  • userSchema — Zod schema used by both Angular and NestJS
  • CreateUserDto — inferred from Zod schema

Conditional Validation (Role-Based)

The shared Zod schema implements dynamic validation using superRefine:

Role Required Fields
admin phoneNumber + birthDate
editor phoneNumber
viewer none (beyond base fields)

This logic lives in libs/shared/src/lib/user.schema.ts and is imported by both the NestJS controller and the Angular creation form — ensuring consistent validation across the stack.


Frontend Routes

Route Component Description
/ UserListComponent Main user table with search
/smiley SmileyComponent Pure CSS responsive smiley face

Theme Colors

Token Value
primary #1da4e8
secondary #20d2a8
tertiary #e4dc46
error #d32f55

Architectural Decisions

  • Nx was chosen as the monorepo tool per the assessment's preference. It provides built-in generators for Angular and NestJS and handles cross-project references cleanly.
  • Standalone Angular components (no NgModules) were used throughout, following Angular 18+ best practices.
  • Zod over class-validator was chosen as the PDF preferred it and it works isomorphically in both Node.js and the browser, making true schema-sharing possible.
  • superRefine was used for conditional validation instead of discriminatedUnion because the role field is part of the same object — superRefine allows cross-field validation in a single schema without splitting into separate schemas.
  • Lazy-loaded dialogs (import()) are used for the details and create components to reduce initial bundle size.
  • Write queue in UsersService (backend) ensures sequential file writes to prevent data corruption under concurrent requests.
  • ChangeDetectorRef.detectChanges() is used in components that update state inside observables, preventing Angular's NG0100 ExpressionChangedAfterItHasBeenCheckedError.

Known Limitations

  • The provided users.json seed data contains intentional inconsistencies (typos like fistName, invalid emails, string IDs). These are loaded as-is and served by the API without sanitization, since they are seed/test data.
  • Authentication and authorization are not implemented — all endpoints are public.
  • The backend uses a simple synchronous write queue. For high-concurrency production use, a proper async queue or database would be recommended.
  • The Angular Material theme uses CSS custom properties (--primary, etc.) for custom colors alongside the Material 3 theme setup, since full M3 dynamic color generation requires the Material Design token pipeline.

Scripts Reference

# Serve frontend
npx nx serve frontend

# Serve backend
npx nx serve api

# Build frontend
npx nx build frontend

# Build backend
npx nx build api

# Lint frontend
npx nx lint frontend

# Lint backend
npx nx lint api

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors