This project is an evolution of the original PHP-based Cinemo movie recommendation web app, refactored with a modern tech stack. Currently in development, the focus is on building a robust system design using React, server-side rendering (SSR), Progressive Web App (PWA) capabilities, and other cutting-edge technologies.
graph TD;
A[Next.js Client] -->|Firebase SDK| B[Firebase Auth]
A -->|Firestore SDK| C[(Cloud Firestore)]
A -->|Storage SDK| D[(Firebase Storage)]
A -->|Client-Side API Calls| E[Netlify Functions]
E -->|Admin SDK| C
E -->|Token Verification| B
C -->|Realtime Listeners| A
F[TMDB API] --> E
G[Recommendation Service] --> E
H[CDN] --> A
- Hybrid Rendering: SSG for public pages, SSR for user-specific content
- Real-time Core: Firestore listeners for collections/likes updates
- Decoupled Services: Firebase for stateful services, Netlify for static hosting
- Progressive Enhancement: PWA-first approach with network resilience
Component Architecture:
/cinemo
├── app
│ ├── api
│ │ └──[auth/collections/preferences/details]
│ ├── (auth) # Auth group layout
│ │ └── signin/page.tsx # Sign-in UI
│ │ └── signup/page.tsx # Sign-up UI
│ │ └── layout.tsx # Auth layout
│ ├── home
│ │ └── page.tsx # Homepage
│ ├── collections
│ │ └── page.tsx # Main collections page
│ ├── preferences
│ │ └── page.tsx # Mood/Genre preferences
│ ├── movie
│ │ └── [id]/page.tsx # Movie details page
│ └── layout.tsx # Global nav (CINEMO/MyPrefs/Collections)
├── components
│ ├── ui # Reusable/Primitive components
│ │ └── [Avatar/Button/Input/Modal/etc]
│ ├── forms # Forms
│ │ ├── auth # Auth Forms
│ │ │ └── [SigninForm/SignupForm]
│ │ ├── preferences # Preferences Forms
│ │ │ └── [GenreForm/MoodForm]
│ │ └── hooks # Custom form hooks
│ ├── features # Feature-specific components
│ │ └── [MoodGrid/MovieCard/RatingBadge/etc] # Combines UI components + logic
├── utils
│ └── helpers.ts # Utility functions
│ └── ...
├── hooks
│ └── useAuth.tsx # Authentication hook
│ └── usePreferences.tsx # Preferences hook
│ └── ...
├── types
│ └── tmdb.d.ts # TMDB API types
│ └── ...
├── styles
│ ├── global.css # Global styles
│ └── tailwind.config.ts # Tailwind config
├── public
│ └── [favicon/manifest/etc]
├── lib
│ └── firebase.ts # Firebase client
├── netlify
│ └── functions # Netlify functions
│ │ └── recommendations.ts # ML integration
│ │ └── migration.ts # Data import
│ │ └── tmdb-proxy.ts # API shielding
- Core: Next.js (App Router), TypeScript
- State: Zustand (global), TanStack Query (client-side data management, caching, and synchronization)
- Styling: Tailwind for global styles, CSS Modules for custom components
- PWA:
next-pwa
with runtime caching strategies
- Authentication Flow: Email/password + social logins (low priority)
- Recommendation Engine: Multi-filter browsing (mood and genres)
- Collection Management: Drag-and-drop organisation
- Preference Editing: Visual genre and mood selection
- Image lazy loading with blur placeholders
- Route-based code splitting
- SWR for client-side caching
- Optimistic UI updates for likes/collections
- Mixpanel integration for tracking and analysing user behaviour
- Unit: Jest + Testing Library
- E2E: Cypress with auth state reuse
- Visual: Percy snapshot testing
- Lighthouse: CI-based performance audits and automated a11y checks
Internationalisation (i18n) Implementation:
Key Technologies:
next-i18next
for SSR translationsi18next-browser-languagedetector
for auto language detectiondate-fns
for localised date formattingreact-intl
for number/currency formatting
// Users collection
users/${uid}
├─ preferences: {
│ genres: string[],
│ decade: "1990s" | "2000s",
│ ratingThreshold: number
│ }
├─ likes: subcollection[
│ {
│ movieId: string,
│ timestamp: Date,
│ metadata: TMDBMovie
│ }
│ ]
└─ activity: {
lastLogin: Date,
totalLikes: number
}
Public APIs:
// TMDB API Adapter
export const fetchTMDBMovie = async (id: string) => {
const res = await fetch(
`https://api.themoviedb.org/3/movie/${id}?append_to_response=credits`,
{
headers: {
Authorization: `Bearer ${process.env.TMDB_TOKEN}`,
},
}
)
return MovieSchema.parse(await res.json())
}
- Firestore: User-generated content, such as user profile
- Firebase Cloud Storage: User avatars (with compression)
- LocalStorage: Session caching for offline use
- CDN Cache: TMDB images (via Netlify edge)
Zod schema validation:
// Zod schema for Firestore documents
const UserPreferencesSchema = z.object({
genres: z.array(z.string().max(20)),
decade: z.enum(['1990s', '2000s', '2010s']),
ratingThreshold: z.number().min(0).max(10),
})
/netlify
/functions
recommendations.ts # ML integration
migration.ts # Data import
tmdb-proxy.ts # API shielding
sequenceDiagram
participant Git as GitHub
participant Netlify
participant Firebase
Git->>Netlify: Push to main
Netlify->>Netlify: Install deps
Netlify->>Netlify: Build Next.js
Netlify->>Netlify: Run tests
Netlify->>Firebase: Deploy Firestore rules
Netlify->>Netlify: Deploy to CDN
- Netlify Edge: Static asset delivery
- Firebase Auth: User identity
- Firestore: Real-time database
- Cloud Functions: Background tasks
- Sentry: Error monitoring
- CSP Headers: Restrict script sources
- Firestore Rules: Role-based access
- API Shield: Netlify Functions proxy external APIs
- Secret Management: Environment variables encryption
graph TD
A[User Preferences] --> B(Content Filtering)
C[Liked Movies] --> D(Collaborative Filtering)
E[TMDB Metadata] --> F(Semantic Analysis)
B --> G[Recommendation Engine]
D --> G
F --> G
G --> H[Ranked Results]
- Precompute daily recommendations via Cloud Run
- Real-time personalization with TensorFlow.js
- Fallback to TMDB similar movies API
- Screen reader testing with JAWS/NVDA
- Keyboard navigation checks
- Color contrast validation (WCAG 2.1 AA)
- ARIA landmarks verification