Aastrika Sphere — Angular 21 Learning Management System frontend for a healthcare training platform offering free CNE courses with INC certification.
- Live platform: https://sphere.aastrika.org
- Angular: 21.2.7 | Node: 20 (managed via NVS) | Package manager: Yarn
# 1. Switch to Node 20 (required)
nvs use 20
# 2. Install dependencies
yarn install
# 3. Start dev server (localhost:3000)
yarn startThe dev proxy in
proxy/localhost.proxy.jsonroutes/apis/**to the production backend atsphere.aastrika.org. Update the hardcoded auth cookie inproxy/localhost.proxy.jsonwhen it expires.
| Command | Description |
|---|---|
yarn start |
Dev server on :3000 with live proxy |
yarn run build |
Production build (gzip + brotli compressed) |
yarn run build:local |
Production build without compression |
yarn run build:fast |
Dev build with vendor chunk (faster iteration) |
yarn test |
Jest single run |
yarn run test-watch |
Jest watch mode |
yarn run test-coverage |
Jest with coverage report |
yarn run lint |
ESLint check |
yarn run lint:fix |
ESLint auto-fix |
yarn run tailwind |
Watch TailwindCSS and recompile |
yarn run tailwind:build |
One-time TailwindCSS build |
eagle-fusion/
├── src/
│ ├── app/
│ │ ├── component/ # Root-level UI components
│ │ │ ├── root/ # RootComponent — app shell, layout orchestrator
│ │ │ ├── app-nav-bar/ # Authenticated top & bottom navbar
│ │ │ ├── app-public-nav-bar/ # Pre-login public navigation
│ │ │ ├── app-footer/ # Footer
│ │ │ ├── login/ # Login UI
│ │ │ ├── downtime-banner/ # Maintenance banner
│ │ │ ├── downtime-full/ # Full-screen maintenance page
│ │ │ └── my-courses/ # Enrolled courses tab
│ │ ├── routes/ # Feature modules (lazy-loaded)
│ │ │ ├── app-toc/ # Course Table of Contents
│ │ │ ├── web-course-view/ # Course viewing
│ │ │ ├── search/ # Search module
│ │ │ ├── profile-view/ # User profile
│ │ │ ├── organisations/ # Org-specific pages
│ │ │ ├── public/ # Public pages (home, about, blog, FAQ)
│ │ │ └── [15+ more...]
│ │ ├── guards/ # Route guards (auth, feature flags)
│ │ ├── services/ # App-level services (28 total)
│ │ ├── constants/ # API endpoint constants
│ │ └── models/ # Shared TypeScript models
│ ├── assets/ # i18n translation files
│ ├── fusion-assets/ # Images, icons, fonts
│ ├── styles/ # Shared SCSS (ws-vars, ws-mixins, ws-common)
│ ├── main.ts # Bootstrap entry point
│ └── index.html
│
├── project/ws/
│ ├── app/src/lib/routes/ # Feature modules (search, toc, org, profile...)
│ ├── viewer/src/lib/ # Content viewers (PDF, video, SCORM, quiz)
│ └── author/src/lib/ # Content authoring (rarely modified)
│
├── library/ws-widget/
│ ├── collection/src/lib/ # Shared widgets and reusable components
│ ├── resolver/src/lib/ # Widget resolution engine
│ └── utils/src/lib/ # Core utility services
│
└── proxy/localhost.proxy.json # Dev proxy configuration
index.html
└── main.ts → platformBrowserDynamic().bootstrapModule(AppModule)
│
├── APP_INITIALIZER (3 tokens, run in parallel)
│ ├── initializeCompetencyConfig() — loads from localStorage
│ ├── appInitializer(InitService) — auth + config fetch
│ └── initTranslate(TranslateService) — loads i18n
│
└── bootstraps RootComponent (<ws-root>)
│
├── constructor — platform check, domain detection,
│ router events subscription, SSO setup
├── ngOnInit — route subscriptions, SEO, telemetry,
│ downtime init, layout flags
└── router-outlet — lazy-loaded feature routes
See docs/ROOT_COMPONENT.md for the full bootstrapping flow and template reference.
| Service | Location | Responsibility |
|---|---|---|
ConfigurationsService |
@ws-widget/utils |
User profile, locale, feature flags — global state |
ValueService |
@ws-widget/utils |
Responsive breakpoints; isMobile() signal, isXSmall$ observable |
InitService |
src/app/services/ |
Auth init, config fetch, user data loading at startup |
ThemeService |
src/app/services/ |
Dark/light mode toggle, org color variables (signal-based) |
SeoService |
src/app/services/ |
Updates page title, meta tags, OG tags on every navigation |
PlaylistService |
src/app/services/ |
Fetches home page config, org details, footer config |
DowntimeConfigService |
src/app/services/ |
Maintenance windows and theme overrides |
UserDataCacheService |
src/app/services/ |
Session-storage cache for user profile (6-hour TTL) |
TelemetryService |
@ws-widget/utils |
Impression, audit, event tracking (Sunbird telemetry) |
AuthKeycloakService |
@ws-widget/utils |
Keycloak SSO authentication |
| Alias | Resolves to |
|---|---|
@ws-widget/collection |
library/ws-widget/collection |
@ws-widget/utils |
library/ws-widget/utils |
@ws-widget/resolver |
library/ws-widget/resolver |
@ws/app |
project/ws/app/src/public-api |
@ws/viewer |
project/ws/viewer |
- No semicolons — enforced by ESLint
- Single quotes for strings
- 2-space indent, max line length 140 characters
- Component selector prefix:
ws-app-| Directive prefix:ws(camelCase) standalone: false— NgModule-based architecture throughout- No
console.log— usethis.logger.log(...)fromLoggerService - Use Angular Signals (
signal(),computed(),effect()) for reactive state - Prefer new template control flow (
@if,@for,@switch) in new code - Use
| translatepipe for all user-facing strings (ngx-translate)
@import 'ws-vars'; // $size-* spacing, $color-* palette
@import 'ws-mixins'; // @include breakpoint-xs, breakpoint-s, breakpoint-gt-xs
@import 'ws-common'; // shared utility classes- Mobile breakpoint:
768px— useisMobile()signal orisXSmall$observable fromValueService - Mobile card:
ws-mobile-course-view(full-width image on top) - Desktop card:
ws-app-learning-card(horizontal thumbnail + text) - Always add shimmer for both layouts when adding loading states
- Keycloak via
keycloak-angular—KeycloakAuthGuardprotects authenticated routes - OAuth callback routes:
/openid/keycloak,/openid/sashakt,/openid/maternity,/openid/tnnmc,/openid/mnc,/openid/tnai,/google/callback - Public routes:
/public/**— no auth required - Route guard:
GeneralGuardverifies authentication and feature permissions before activating protected routes
The app renders two distinct UI branches detected at runtime from window.location.hostname:
| Domain | Branch | Layout |
|---|---|---|
sphere.aastrika.org (default) |
Sphere | Full layout: public nav, featured courses, footer |
*.ekshamata.* |
Ekshamata | Simplified layout: logged-in nav only, no public featured section |
Config is fetched from /apis/... at app init. Access via ConfigurationsService:
configSvc.activeLocale // current language
configSvc.userPreference // user's saved preferences
configSvc.isIntranetAllowed // intranet content visibility
configSvc.isAuthenticated // authentication stateproxy/localhost.proxy.json routes all API calls during local development:
| Path | Target |
|---|---|
/apis/** |
https://sphere.aastrika.org (uses hardcoded auth cookie — update when it expires) |
/content-api/** |
http://localhost:3004 |
/assets/** |
https://sphere.aastrika.org |
- Framework: Jest 29 with
jest-preset-angular - Test files:
*.spec.tsalongside source files - Integration style preferred — no mocking of real HTTP/database calls
- Run
yarn run jest-cacheif tests behave unexpectedly after dependency changes
| Integration | Package | Purpose |
|---|---|---|
| Keycloak SSO | keycloak-angular |
Authentication |
| Sunbird | @project-sunbird/client-services |
LMS content APIs |
| Telemetry | @project-sunbird/telemetry-sdk |
Event tracking |
| Competency framework | @aastrika_npmjs/comptency |
Competency assessment UI |
| Discussion forum | @aastrika_npmjs/discussions-ui-v8 |
Forum/chat |
| Micro surveys | @sunbird-cb/micro-surveys |
Feedback collection |
| Capacitor | @capacitor/* |
Native mobile app wrapper |
| FreshChat | window.fcWidget (CDN) |
Live chat support widget |
- Root Component Flow — detailed bootstrapping and layout orchestration