AI-powered room visualizer for the DSYN Luxury stone & moulding website. Lets clients upload room photos and instantly see DSYN Luxury products (marble, Gwalior stone, Moca Crema, columns, mouldings) applied to their space.
| Layer | Tech |
|---|---|
| Frontend | React 18, React Router v6, TanStack Query, Lucide icons |
| Backend | Node.js + Express |
| Database | MongoDB + Mongoose (Optimized with .lean() & pagination) |
| Image storage | Cloudinary |
| Local AI & Mapping | FastAPI, PyTorch, SegFormer, OpenCV (LAB Color Space) |
| Authentication | Google OAuth 2.0 & JWT with Resend Email OTP |
| Payments | Razorpay SDK & Secure API Integration |
dsyn-stratum/
├── server/ # Express backend
│ ├── index.js # Entry point, middleware
│ ├── models/
│ │ ├── User.js # Client accounts
│ │ ├── Product.js # Stone & moulding SKUs
│ │ ├── Render.js # AI-generated visualizations
│ │ └── Quote.js # Quote requests
│ ├── routes/
│ │ ├── auth.js # Register, login, profile
│ │ ├── products.js # CRUD + catalogue
│ │ ├── visualizer.js # Upload, segment, generate
│ │ ├── renders.js # Save, share renders
│ │ ├── quotes.js # Quote submission + CRM
│ │ └── admin.js # Dashboard, stats, seed
│ ├── segment_api.py # Python AI Microservice (FastAPI)
│ └── middleware/
│ ├── auth.js # JWT protect, adminOnly
│ └── upload.js # Cloudinary + multer
│
└── client/ # React frontend
└── src/
├── pages/
│ ├── HomePage.js # Landing page
│ ├── VisualizerPage.js # Core 3-step tool
│ ├── ProductsPage.js # Browse catalogue
│ ├── DashboardPage.js # My renders
│ ├── LoginPage.js
│ ├── RegisterPage.js
│ ├── SharedRenderPage.js # Public share link
│ └── AdminPage.js # Admin panel
├── components/
│ ├── shared/Navbar.js
│ └── visualizer/QuoteModal.js
├── context/AuthContext.js
└── utils/api.js # All API calls
# Root
npm install
# Install all packages
npm run install:allcd server
cp .env.example .envFill in .env:
PORT=5000
CLIENT_URL=http://localhost:3000
MONGODB_URI=mongodb://localhost:27017/dsyn
# JWT
JWT_SECRET=your_random_secret_32_chars_min
JWT_EXPIRES_IN=7d
# Cloudinary
CLOUDINARY_CLOUD_NAME=xxx
CLOUDINARY_API_KEY=xxx
CLOUDINARY_API_SECRET=xxx
# Admin Credentials
ADMIN_EMAIL=admin@dsyn.com
ADMIN_PASSWORD=your_password
# Watermark text
WATERMARK_TEXT=DSYN
# Google OAuth Credentials
GOOGLE_CLIENT_ID=your_google_client_id_here.apps.googleusercontent.com
# Resend API Key for Email OTP Verification
RESEND_API_KEY=re_your_api_key_here
# Razorpay Keys
RAZORPAY_KEY_ID=rzp_test_yourkey
RAZORPAY_KEY_SECRET=your_razorpay_secret
# AI Fallback Rendering APIs (Required for full pipeline)
STABILITY_API_KEY=your_stability_key_here
REPLICATE_API_TOKEN=your_replicate_token_here
# Python microservice connection URL
PYTHON_API_URL=http://localhost:8000
Start MongoDB, then run:
cd server && node -e "
const mongoose = require('mongoose');
const User = require('./models/User');
require('dotenv').config();
mongoose.connect(process.env.MONGODB_URI).then(async () => {
await User.create({ name: 'Admin', email: process.env.ADMIN_EMAIL, password: process.env.ADMIN_PASSWORD, role: 'admin' });
console.log('Admin created');
process.exit();
});
"# Start the server first, then:
curl -X POST http://localhost:5000/api/admin/seed \
-H "Authorization: Bearer YOUR_ADMIN_JWT"Or log in as admin on the website → Admin panel → click "Seed sample data".
First, start the Python FastAPI AI microservice:
cd server
# Activate your virtual environment:
.\venv\Scripts\Activate.ps1
# Install requirements
pip install -r requirements.txt
# Start the server:
python segment_api.pyThen, in a new terminal, start the Node/Express backend and React frontend:
# From root
npm run dev- Backend: http://localhost:5000
- Frontend: http://localhost:3000
- Python API: http://localhost:8000
The visualizer uses a highly optimized hybrid SegFormer (Ade20k) + OpenCV LAB Color Space mapping pipeline to apply product textures realistically in O(1) inference time:
- Pre-loaded globally in the FastAPI service (
segment_api.py). - Model runs in
.half()precision on CUDA for 50% VRAM reduction. - Runs once globally per base image to map all room components (walls, floors, ceiling, pillars), rather than re-running per applied material.
- Takes the product's Cloudinary SKU texture image (utilizes local temp caching to avoid redundant downloads).
- Converts both the original room photo (cropped to mask bounding box) and the tiled texture to the LAB Color Space to isolate lightness (
L). - Calculates a dynamic
lighting_ratiomap from the original wall's lighting patterns. - Multiplies the product texture's
Lchannel by this ratio, perfectly projecting original highlights, shadows, and ambient occlusion onto the new texture. - Aggressive
gc.collect()prevents memory leaks from massive NumPy tile arrays.
- Upload — drop or tap to upload room photo (JPG/PNG, up to 15MB)
- Select — choose zone (floor/wall/ceiling/pillar/etc.) then product from live inventory
- Presets — one-click neoclassical transformations: Ionic columns, cornice, wainscoting
- Generate — AI applies materials in seconds
- Cart & Checkout — add selected materials to a persistent cart and securely pay via Razorpay checkout
- Download — HD watermarked render with DSYN Luxury branding
- Quote — pre-filled quote form with selected SKUs, area estimator, total
- Dashboard with stats (users, products, renders, new quotes). Queries optimized with
.lean(). - Add / edit / remove products with texture upload
- Full quote CRM: new → contacted → quoted → won / lost
- User management (Paginated)
- One-click sample data seed
- Renders saved to MongoDB for 30 days (TTL index auto-cleanup)
- Logged-in users see full history at
/dashboard - Shareable public links (
/view/:token) for WhatsApp sharing
GET /api/products List products (filter: category, grade, zone)
GET /api/products/:id Product detail
POST /api/auth/register Create account (triggers Resend OTP)
POST /api/auth/login Login (requires verified account)
POST /api/auth/google OAuth 2.0 login / auto-registration
POST /api/auth/verify-otp Verify registration code
POST /api/auth/resend-otp Resend registration code
GET /api/renders/shared/:token Public render view
POST /api/quotes Submit quote (no auth required)
GET /api/auth/me Current user
GET /api/renders My renders
POST /api/visualizer/upload Upload room photo
POST /api/visualizer/generate Generate visualization
GET /api/admin/dashboard Stats
POST /api/products Create product
PATCH /api/products/:id Update product
DELETE /api/products/:id Soft-delete
GET /api/quotes All quotes
PATCH /api/quotes/:id/status Update quote status
Runs locally on http://localhost:8000 (or PYTHON_API_URL):
POST /segment Accepts photo URL. Returns Ade20k semantic segmentation masks & room coverage data.
POST /generate Accepts photo URL and array of applied zones & textures. Returns finalized rendered image.
cd server
npm startSet all env vars in the platform dashboard.
cd client
REACT_APP_API_URL=https://your-api-domain.com/api npm run buildSet CLIENT_URL=https://your-frontend-domain.com on server.
- Local MongoDB or MongoDB Atlas (free tier works fine)
- Atlas connection string:
mongodb+srv://user:pass@cluster.mongodb.net/dsyn
The UI is fully mobile-first:
- Native page scrolling restored for material selection process
- Dropzone supports tap-to-upload on iOS/Android
- Sticky product selector panel collapses on mobile
- WhatsApp-friendly share links with responsive stacked "Before & After" layouts
- PWA-ready meta tags
- Go to
/admin→ Products tab - Click "Add product"
- Fill: SKU, name, category, price per sq.ft, finish, grade, zones
- Upload a high-res texture image (the AI uses this for material reference)
- Mark as "Neoclassical preset" if it should appear in the one-click presets
- Save — it appears immediately in the visualizer inventory
Quotes come in at /admin → Quotes tab. Status flow:
new → contacted → quoted → won
↘ lost
Each quote includes: contact details, city, selected SKUs with area estimates, photo render thumbnail, and an auto-calculated estimate.
| Variable | Required | Description |
|---|---|---|
MONGODB_URI |
Yes | MongoDB Atlas connection string |
JWT_SECRET |
Yes | Random 32+ char token signing key |
CLOUDINARY_CLOUD_NAME |
Yes | Cloudinary cloud namespace |
CLOUDINARY_API_KEY |
Yes | Cloudinary API key |
CLOUDINARY_API_SECRET |
Yes | Cloudinary API secret |
GOOGLE_CLIENT_ID |
Yes | Google Developer Console OAuth Client ID |
RESEND_API_KEY |
Yes | Resend API Key for Email OTP Verification |
RAZORPAY_KEY_ID |
Yes | Razorpay Gateway Key ID |
RAZORPAY_KEY_SECRET |
Yes | Razorpay Gateway Key Secret |
STABILITY_API_KEY |
No | Required if using the SDXL fallback pipeline |
REPLICATE_API_TOKEN |
No | Required if using the Replicate fallback pipeline |
PYTHON_API_URL |
Yes | Python microservice URL (default: http://localhost:8000) |
CLIENT_URL |
Yes | React Frontend URL for CORS |
PORT |
No | Server port (default 5000) |
| Variable | Required | Description |
|---|---|---|
REACT_APP_GOOGLE_CLIENT_ID |
Yes | Google Developer Console OAuth Client ID |
- Replaced brown dark theme (
#1A1512) with a neutral cool-slate palette (#0f1318) across all CSS variables - Added
--sidebar-bg,--sidebar-active-bg,--sidebar-texttokens so the admin sidebar colour adapts per theme - Added smooth
0.25s easetransition onbackground-color,color, andborder-colorfor seamless theme switching
- Extracted all inline styles from
AdminDashboard.jsandAdminLayout.jsintoAdminDashboard.cssandAdminLayout.css - Admin sidebar background is now theme-aware (warm brown in light, cool slate in dark)
- Active nav link gets a gold left-border accent
- All admin headings switched to Inter (body font) — serif display font no longer bleeds into admin UI
- KPI card icon backgrounds replaced with translucent
rgbaso they work in both themes
- Replaced all hardcoded dark-text colors (
#2C2420,#1F2937,#6B7280, etc.) withvar(--charcoal)/var(--charcoal-light)across all 8 admin pages: Orders, Products, Quotes, Clients, Renders, Categories, Reviews, Users, Coupons, Settings - Replaced
background: "white"withvar(--warm-white)sitewide - Replaced hardcoded light borders with
var(--border)sitewide
- Dropdown header/footer backgrounds now use CSS variables
- Unread item highlight uses
rgba(201,168,76,0.06)— visible in both themes - Badge ring changed from
0 0 0 2px whiteto0 0 0 2px var(--nav-bg) - Dropdown repositioned using
position: fixedwith viewport-clamped coordinates — never overflows off-screen on mobile
- Admin avatar circle colour is theme-aware (orange in light, gold in dark)
- "BY DSYN LUXURY" stacks below "STRATUM" on screens ≤ 540px
- React Router v7 future flags added to silence deprecation warnings
- Google
initialize()double-call warning fixed with auseRefguard - Removed noisy
console.logdiagnostics fromfacebookSdk.js
- Sidebar defaults open on ≥ 900px, auto-closes on smaller screens
- Sidebar becomes a floating drawer on mobile — slides over content at
z-index: 1100with a dimmed backdrop overlay - Clicking the overlay closes the sidebar; fixed
setSidebarOpen is not definederror - Top navbar always full-width on mobile
- At ≤ 700px: user name, Admin label, and Visualizer button text hide to prevent overflow
- At ≤ 600px: KPI grid goes 2-column, charts and activity panels stack vertically
- Hero image switched from
<img>to CSSbackground-image— eliminates stretch on portrait images - On dark mode + screen width < 600px, hero swaps to
hero-bg-dark1.png(portrait-optimised)
- Removed unused
triggerFacebookLoginimport fromLoginPage - Merged duplicate
facebookSdkimports into one line