Overview
The Happy Habitat frontend is an Angular 19 single-page application (SPA) that serves as the user interface for the condominium management system. This guide covers building and deploying to various hosting platforms.
Prerequisites
- Node.js 18+ and npm
- Angular CLI 19.2.15 or higher
- Production backend API (see Backend Deployment)
Technology Stack
The frontend application uses (see package.json):
- Angular 19.2.0 - Framework
- TypeScript 5.7.2 - Language
- Tailwind CSS 4.1.13 - Styling framework
- DaisyUI 5.1.7 - Component library
- Chart.js 4.5.0 - Data visualization
- RxJS 7.8.0 - Reactive programming
Building for Production
Install Dependencies
cd ~/workspace/source/happy-habitat-frontend
npm install
Edit src/environments/environment.prod.ts with your production API URL:
import { LogLevel } from '../app/shared/interfaces/log.interface';
export const environment = {
production: true,
apiUrl: 'https://api.happyhabitat.com/api', // Your backend API URL
apiVersion: 'v1',
appName: 'Happy Habitat',
appVersion: '1.0.0',
logging: {
level: LogLevel.WARN,
enableConsole: false,
enableRemote: true,
enableStackTraces: true
},
auth: {
useMockAuth: false
}
};
The build process automatically replaces environment.ts with environment.prod.ts when building for production (see angular.json:49-54).
Build the Application
This creates an optimized production build in dist/happy-habitat/browser/ with:
- Minified JavaScript and CSS
- Output hashing for cache busting
- Tree-shaking for smaller bundle sizes
- AOT (Ahead-of-Time) compilation
The application uses hash-based routing (HashLocationStrategy in app.config.ts:24). URLs will use the # symbol (e.g., https://app.example.com/#/dashboard). This simplifies deployment as all routes can be handled by index.html without server-side rewrite rules.
Build Output
The production build generates:
index.html - Main HTML file
main-[hash].js - Application code
polyfills-[hash].js - Browser polyfills
styles-[hash].css - Compiled styles
- Static assets from
public/ directory
Bundle Size Optimization
The Angular configuration sets bundle size limits (angular.json:36-46):
- Initial bundle: 900kB warning, 1MB error
- Component styles: 4kB warning, 8kB error
To analyze bundle size:
npm run build -- --stats-json
npx webpack-bundle-analyzer dist/happy-habitat/browser/stats.json
Deployment Options
Option 1: Static Web Hosting (Nginx)
- Install Nginx:
sudo apt update
sudo apt install nginx
- Copy Build Files:
sudo mkdir -p /var/www/happyhabitat
sudo cp -r dist/happy-habitat/browser/* /var/www/happyhabitat/
- Configure Nginx (
/etc/nginx/sites-available/happyhabitat):
server {
listen 80;
listen [::]:80;
server_name app.happyhabitat.com;
root /var/www/happyhabitat;
index index.html;
# Gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_vary on;
gzip_min_length 1000;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Angular routing - serve index.html for all routes
location / {
try_files $uri $uri/ /index.html;
}
# API proxy (optional - if API is on same domain)
location /api {
proxy_pass https://api.happyhabitat.com;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
- Enable Site and Reload:
sudo ln -s /etc/nginx/sites-available/happyhabitat /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
- Configure SSL with Let’s Encrypt:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d app.happyhabitat.com
Option 2: Azure Static Web Apps
- Install Azure CLI:
npm install -g @azure/static-web-apps-cli
- Create Static Web App:
az staticwebapp create \
--name happyhabitat-frontend \
--resource-group happyhabitat-rg \
--location "East US 2" \
--sku Standard
- Configure Build (
staticwebapp.config.json):
{
"routes": [
{
"route": "/api/*",
"rewrite": "https://api.happyhabitat.com/api/{path}"
},
{
"route": "/*",
"serve": "/index.html",
"statusCode": 200
}
],
"navigationFallback": {
"rewrite": "/index.html",
"exclude": ["/images/*.{png,jpg,gif}", "/css/*"]
},
"globalHeaders": {
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "SAMEORIGIN",
"X-XSS-Protection": "1; mode=block"
},
"mimeTypes": {
".json": "application/json"
}
}
- Deploy:
npm run build
az staticwebapp deploy \
--name happyhabitat-frontend \
--resource-group happyhabitat-rg \
--app-location dist/happy-habitat/browser
Option 3: AWS S3 + CloudFront
- Create S3 Bucket:
aws s3 mb s3://happyhabitat-frontend
aws s3 website s3://happyhabitat-frontend \
--index-document index.html \
--error-document index.html
- Configure Bucket Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::happyhabitat-frontend/*"
}
]
}
- Upload Files:
npm run build
aws s3 sync dist/happy-habitat/browser/ s3://happyhabitat-frontend \
--delete \
--cache-control "public, max-age=31536000" \
--exclude "index.html" \
--exclude "*.json"
# Upload index.html without caching
aws s3 cp dist/happy-habitat/browser/index.html s3://happyhabitat-frontend/ \
--cache-control "no-cache, no-store, must-revalidate"
- Create CloudFront Distribution:
aws cloudfront create-distribution \
--origin-domain-name happyhabitat-frontend.s3-website-us-east-1.amazonaws.com \
--default-root-object index.html
- Configure Error Pages:
- Set custom error response for 404 to return
/index.html with 200 status
Option 4: Vercel
- Install Vercel CLI:
- Create
vercel.json:
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/static-build",
"config": {
"distDir": "dist/happy-habitat/browser"
}
}
],
"routes": [
{
"src": "/(.*)",
"dest": "/index.html"
}
]
}
- Add Build Script to package.json:
{
"scripts": {
"vercel-build": "ng build --configuration production"
}
}
- Deploy:
Option 5: Docker + Nginx
- Create Dockerfile:
# Build stage
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM nginx:alpine
COPY --from=build /app/dist/happy-habitat/browser /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
- Create nginx.conf:
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
- Build and Run:
docker build -t happyhabitat-frontend:latest .
docker run -d -p 80:80 happyhabitat-frontend:latest
Environment-Specific Builds
Runtime Configuration
For dynamic API URLs without rebuilding:
- Create
assets/config.json:
{
"apiUrl": "https://api.happyhabitat.com/api"
}
- Load Configuration in app.ts:
export function initializeApp(http: HttpClient) {
return () =>
http
.get('/assets/config.json')
.toPromise()
.then((config: any) => {
// Set runtime configuration
});
}
- Update Configuration per Environment:
# Replace config.json during deployment
echo '{"apiUrl":"https://staging-api.example.com/api"}' > dist/happy-habitat/browser/assets/config.json
Enable Production Mode
Production builds automatically enable:
- AOT compilation
- Tree shaking
- Minification
- Output hashing
Lazy Loading
Implement route-based lazy loading:
const routes: Routes = [
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
}
];
Service Worker (PWA)
Add Progressive Web App capabilities:
ng add @angular/pwa
npm run build
Security Best Practices
Content Security Policy
Add to index.html:
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.happyhabitat.com;">
Configure via web server (Nginx, CloudFront, etc.):
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000
CI/CD Integration
GitHub Actions
Create .github/workflows/deploy.yml:
name: Deploy Frontend
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm run build
- name: Deploy to Azure
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_TOKEN }}
app_location: "dist/happy-habitat/browser"
Troubleshooting
Blank Page After Deployment
- Check browser console for errors
- Verify API URL in environment.prod.ts
- Check CORS configuration on backend
- Ensure all routes return index.html (for Angular routing)
API Connection Failed
- Verify backend is accessible from frontend domain
- Check CORS configuration includes frontend URL
- Verify SSL certificates if using HTTPS
- Check browser network tab for request details
Assets Not Loading
- Verify base href in index.html:
<base href="/">
- Check file permissions on server
- Verify nginx/server configuration for static files
Next Steps