Frederick Garcia
← Back to Home

Lumera Learning Group

Lumera Learning Group (lumera-spa)

A high-performance, modern, and SEO-optimized Single Page Application (SPA) with Server-Side Rendering (SSR) built for Lumera Learning Group. The platform offers interactive Spanish and English language learning classes for children and teenagers (ages 4–18) led by native teachers.


🌟 Project Overview

This web application serves as the main portal for Lumera Learning Group. It is built to offer a seamless, fast user experience across multiple devices, featuring:

  • Dynamic Class Scheduling: Real-time integration with the Calendly API to fetch, cache, and display available class categories.
  • Fully Translated Experience (i18n): Native support for English (en) and Spanish (es) using compiled Angular localization, complete with SEO-optimized language routing.
  • Direct Contact Forms: Client-side integration with EmailJS to securely capture parental inquiries and child age brackets.
  • Highly Optimized Performance: Leveraging viewport-based lazy loading (@defer) and Angular hydration to maximize Core Web Vitals.

πŸ› οΈ Tech Stack & Integration Ecosystem

The project utilizes a modern frontend and server architecture:

1. Core Framework & Runtime

  • Angular 21 (v21.2.0): Leverages the latest framework innovations, including standalone components, Signals, computed properties, DestroyRef, and the new @defer templating syntax.
  • Angular Hydration & SSR: Server-Side Rendering powered by @angular/ssr and Express to pre-render routes and serve fast, crawler-friendly HTML.

2. Styling & Design System

  • Tailwind CSS v4 (@tailwindcss/postcss & tailwindcss v4.1.12): Built using the new Tailwind CSS v4 @theme compiler and CSS custom properties inside src/styles.css for a custom pastel palette, smooth micro-animations, and responsive layouts.
  • Typography: Google Fonts integration (Nunito) configured globally for kid-friendly and readable interface aesthetics.

3. Third-Party API Integrations

  • Calendly API: Fetches active class events dynamically. Includes an automated warm-up cache in the services layer and launches the Calendly Booking Widget on click.
  • EmailJS (@emailjs/browser): Processes contact form submissions on the client side without needing a custom database backend, keeping operational overhead low.

4. Quality & Build Tooling

  • Vitest: Replaces Karma/Jasmine as the test runner, delivering faster execution times during unit testing.
  • Postcss & Prettier: Ensures clean, auto-formatted style assets.

πŸ“‚ Project Directory Structure

Below is the layout of the source files, reflecting a highly modular design:

lumera-spa/
β”œβ”€β”€ public/                       # Static public assets
β”‚   β”œβ”€β”€ assets/                   # Images and brand graphics
β”‚   β”œβ”€β”€ _redirects                # Netlify / CDN redirection mapping
β”‚   β”œβ”€β”€ robots.txt                # Crawler directives
β”‚   └── sitemap.xml               # SEO sitemap with alternate hreflang tags
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ app/                      # Main Angular application root
β”‚   β”‚   β”œβ”€β”€ components/           # Reusable UI components
β”‚   β”‚   β”‚   β”œβ”€β”€ benefits-section.component.ts/.html
β”‚   β”‚   β”‚   β”œβ”€β”€ class-gallery.component.ts/.html
β”‚   β”‚   β”‚   β”œβ”€β”€ contact-form.component.ts/.html
β”‚   β”‚   β”‚   β”œβ”€β”€ contact-section.component.ts/.html
β”‚   β”‚   β”‚   β”œβ”€β”€ footer.component.ts/.html
β”‚   β”‚   β”‚   β”œβ”€β”€ hero-section.component.ts/.html
β”‚   β”‚   β”‚   β”œβ”€β”€ navbar.component.ts/.html
β”‚   β”‚   β”‚   β”œβ”€β”€ testimonials-section.component.ts/.html
β”‚   β”‚   β”‚   └── whatsapp-button.component.ts/.html
β”‚   β”‚   β”œβ”€β”€ models/               # TypeScript interfaces
β”‚   β”‚   β”‚   └── calendly.model.ts
β”‚   β”‚   β”œβ”€β”€ pages/                # High-level page components (views)
β”‚   β”‚   β”‚   β”œβ”€β”€ blog/             # Individual blog post components
β”‚   β”‚   β”‚   β”œβ”€β”€ blog-hub/         # Blog listing dashboard
β”‚   β”‚   β”‚   β”œβ”€β”€ home/             # Homepage view (lazy loads sections)
β”‚   β”‚   β”‚   └── pricing/          # Product options and course pricing lists
β”‚   β”‚   β”œβ”€β”€ services/             # API and DOM utility services
β”‚   β”‚   β”‚   β”œβ”€β”€ calendly-api.service.ts
β”‚   β”‚   β”‚   └── external-scripts.service.ts
β”‚   β”‚   β”œβ”€β”€ app.config.ts         # Main CSR/Hydration configuration providers
β”‚   β”‚   β”œβ”€β”€ app.config.server.ts  # Server-specific configurations
β”‚   β”‚   β”œβ”€β”€ app.routes.ts         # Navigation routing definitions
β”‚   β”‚   β”œβ”€β”€ app.ts                # App root component (manages SEO tags & JSON-LD)
β”‚   β”‚   β”œβ”€β”€ app.html / app.css
β”‚   β”‚   └── tokens.ts             # Base URL injection tokens for SSR
β”‚   β”œβ”€β”€ environments/             # Environment configurations
β”‚   β”‚   β”œβ”€β”€ environment.ts        # Local development values (includes local tokens)
β”‚   β”‚   └── environment.prod.ts   # Production values
β”‚   β”œβ”€β”€ locale/                   # i18n XLF localization resources
β”‚   β”‚   β”œβ”€β”€ messages.xlf          # Extracted base source dictionary
β”‚   β”‚   β”œβ”€β”€ messages.en.xlf       # English translation file
β”‚   β”‚   └── messages.es.xlf       # Spanish translation file
β”‚   β”œβ”€β”€ main.ts                   # Client bootstrap entry point
β”‚   β”œβ”€β”€ main.server.ts            # Server bootstrap entry point
β”‚   β”œβ”€β”€ server.ts                 # Express SSR server setup
β”‚   └── styles.css                # Global styles and Tailwind configuration
β”œβ”€β”€ angular.json                  # Angular CLI configuration file
β”œβ”€β”€ package.json                  # Scripts & dependencies definition
β”œβ”€β”€ tailwind.config.js            # Legacy tailwind config (if backward compatibility is needed)
└── tsconfig.json                 # TypeScript compiler configuration

πŸ“Š Data Models

Calendly Integration Model (src/app/models/calendly.model.ts)

Standardized interfaces that model the payload coming from the Calendly Event Types endpoint:

export interface CalendlyEventType {
  name: string;
  description_plain: string;
  description_html: string;
  scheduling_url: string;
  duration: number;
  color: string;
  active: boolean;
}

export interface CalendlyResponse {
  collection: CalendlyEventType[];
  pagination: {
    count: number;
    next_page: string | null;
  };
}

πŸ”’ Security Configuration & Performance

The codebase adheres to strict security and frontend performance best practices:

1. Security Safeguards

  • Renderer2 Safe Injection: The application injects structured JSON-LD schemas inside the HTML <head> using Angular’s Renderer2 to prevent Cross-Site Scripting (XSS) vulnerabilities.
  • Input Sanitization: The contact form enforces size limits (maxLength validation on names, child age, email, and messages) and strict type constraints via Angular’s ReactiveFormsModule.
  • Decoupled Server Logic: Client-side integration with EmailJS and Calendly ensures that no database credentials or session tokens are exposed to the client or stored on custom databases, removing server-side database exploit vectors.
  • Platform Guarding: Services checking for DOM manipulation (such as loading Calendly scripts or tracking screen sizes) wrap executions in isPlatformBrowser checks, preventing crashes or data leakage on the SSR server layer.

2. Performance Engineering

  • Caching Layer: The CalendlyApiService caches the list of event types using RxJS shareReplay(1). When the user is on the pricing page, the service is called on initialization (ngOnInit), warming up the cache so that the widget loads instantly when requested.
  • Lazy Script Loading: Instead of blocking page load with scripts in the main index file, ExternalScriptsService dynamically mounts scripts and stylesheets (e.g. Calendly widgets) in the DOM only when the user opens the relevant section.
  • Viewport Deferrals (@defer): The footer, testimonials, contact, and benefit sections are lazy-rendered only when they enter the viewport (@defer (on viewport)), dramatically improving initial page load metrics.
  • Change Detection Tuning: Essential components use ChangeDetectionStrategy.OnPush to prevent unnecessary component tree re-renders, lowering CPU overhead.

🌐 SEO & Internationalization (i18n)

SEO is a primary pillar of the application’s implementation:

  • Canonical Linking: On navigation, the application updates the <link rel="canonical"> element in the head, pointing to the exact current page with trailing slashes to align with sitemaps and CDNs.
  • Cross-Language Mapping (hreflang): Configures <link rel="alternate" hreflang="xx"> mapping Spanish, English, and x-default (pointing to the English version) to resolve redirect issues on search engines.

2. Structured Data (JSON-LD Schemas)

  • Organization Schema: Implemented on the home view to identify the business, brand names, web URL, logo, description, and official social media properties.
  • ItemList & Course Schema: Embedded on the pricing view to structure information about child and teenage language programs, including names, providers, and prices (USD) for Google Rich Snippets.

3. Multi-Locale Builds

Because Angular compiles separate compiled static folders per language locale (/en and /es), routing is configured via Netlify redirections (public/_redirects):

/ /en/ 301
/es/* /es/index.html 200
/en/* /en/index.html 200

This forces a hard reload when switching languages in NavbarComponent, ensuring the client downloads the correct translated JavaScript bundle.


πŸ§ͺ Implementation Validation & Testing

During the validation audit of the implementation, we verified:

  1. Successful Production Compilation: The build pipeline compiles and correctly generates SSR server assets and 12 static localized paths for SSG.
  2. Vitest Execution: Unit tests are configured via Vitest. The initial test runs fail due to missing context in default boilerplate specs.

Known Testing Gaps & How to Fix Them

The boilerplate unit tests fail on:

  • Ι΅NotFound: NG0201: No provider found for ActivatedRoute: Components like App or Home import RouterLink / NavbarComponent but the test setups do not provide the routing modules.
  • Solution: Import RouterModule and provide the routing providers using provideRouter([]) inside the beforeEach block of the spec files.
  • expect(compiled.querySelector('h1')?.textContent).toContain('Hello, lumera-spa'): The test still expects the boilerplate template header, which has been removed.
  • Solution: Update the assertion to query for current components or text.

βš™οΈ Development Commands

Use the following scripts during local development and builds:

Run Local Development Server

To launch the app locally (defaults to English):

npm run dev

To run specifically in the English configuration:

npm run dev:en

Build for Production

To trigger the double compilation (en/es), pre-render static HTML views, and copy redirect/SEO rules to the public output:

npm run build

Serve Compiled SSR Build

To preview the Server-Side Rendered application on localhost (port 4000):

npm run serve

Run Vitest Unit Tests

To run Vitest specs locally:

npm run test