npm.io
1.1.8 • Published 4h ago

igloo-d2c-components

Licence
MIT
Version
1.1.8
Deps
1
Size
4.8 MB
Vulns
0
Weekly
0

D2C Component Library

Reusable React component library with centralized tenant themes and tenant-aware theming for B2C applications.

Table of Contents


Overview

The D2C Component Library provides reusable, tenant-aware UI components with centralized theme management. All tenant themes are defined in one place, ensuring consistency across all applications.

What Are These Projects?

We've built a multi-tenant B2C insurance platform that enables rapid deployment of white-label insurance applications for different partners.

Project Purpose
d2c-component-library Shared component library with centralized themes and reusable UI components
b2c-web-demo Multi-tenant web application consuming the component library
Key Highlights
  • Centralized Themes - Igloo and AmMetLife tenant themes in one library
  • Tenant-Aware Components - Automatically adapt to tenant branding
  • ES2015 Compatible - Works with older webpack configurations
  • Unminified Output - Better debugging and tree-shaking
  • Full TypeScript Support - Complete type definitions
  • Tree-Shakeable - Import only what you need
Supported Tenants
Tenant Port Description
Igloo 8000 Default insurance brand
AmmetLife 8001 Life insurance partner

Project Architecture

The Solution Architecture
┌─────────────────────────────────────────────────────────────────┐
│                     d2c-component-library                        │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │                    Centralized Themes                        ││
│  │  ┌───────────────────────┐  ┌───────────────────────┐       ││
│  │  │       iglooTheme      │  │    ammetlifeTheme     │       ││
│  │  └───────────────────────┘  └───────────────────────┘       ││
│  └─────────────────────────────────────────────────────────────┘│
│  ┌─────────────────────────────────────────────────────────────┐│
│  │                 Shared Components                            ││
│  │  Button │ Card │ Banner │ Header │ CheckoutProgress │ ...   ││
│  └─────────────────────────────────────────────────────────────┘│
│  ┌─────────────────────────────────────────────────────────────┐│
│  │                    Theme Utilities                           ││
│  │  TenantThemeProvider │ useTenantTheme │ getTenantTheme       ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                        b2c-web-demo                              │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │                   Tenant Configurations                      ││
│  │  ┌───────────────────────┐  ┌───────────────────────┐       ││
│  │  │       igloo.ts        │  │     ammetlife.ts      │       ││
│  │  │   (uses iglooTheme)   │  │ (uses ammetlifeTheme) │       ││
│  │  └───────────────────────┘  └───────────────────────┘       ││
│  └─────────────────────────────────────────────────────────────┘│
│  ┌─────────────────────────────────────────────────────────────┐│
│  │               Single Codebase, Multiple Tenants              ││
│  │  yarn start-igloo       → http://localhost:8000             ││
│  │  yarn start-ammetlife   → http://localhost:8001             ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
Technical Stack
Frontend Framework:    React 17 + TypeScript
Build System:          UmiJS (React framework) for b2c-web-demo
UI Components:         MUI v5 + Custom Components
Styling:               Emotion (CSS-in-JS) + Tailwind CSS
State Management:      Recoil + React Context
Component Library:     Rollup (ES2015 output)
Documentation:         Storybook
Package Manager:       Yarn

Features

Centralized Theme System
  • Single source of truth for all tenant themes
  • Type-safe theme access with TypeScript
  • Dynamic theme loading with getTenantTheme()
  • ~585 lines of code removed from consuming apps
  • Easy to maintain - update once, reflects everywhere
Available Themes
Tenant Theme Export Description
Igloo iglooTheme Default insurance brand
AmmetLife ammetlifeTheme Life insurance partner
Component Categories
Category Components
General Button, Card, Banner, Header, NewHeader, Footer
Interactive RecommendationsDrawer, ProductSelectionDrawer, QuestionSection, OptionButton, ToggleGroup
Checkout CheckoutProgress, ProductCard, CheckoutHeader, CheckoutFormButton, HealthQuestionGroup
Forms PersonalInformationForm, ContactDetailsForm, HealthInformationForm
Hooks
  • useTenantTheme() - Access tenant theme and ID
  • useTenantId() - Get current tenant ID
  • useIsTenant() - Check tenant match
  • useTenantLogo() - Get tenant logo
  • useTenantAsset() - Get tenant-specific asset
Utilities
  • getTenantTheme() - Get theme by tenant ID
  • isValidTenantId() - Validate tenant ID
  • getThemeColor() - Extract colors from theme
  • createThemeCSSVariables() - Generate CSS variables
Key Benefits
For Development Teams
Benefit Impact
Single Source of Truth Update themes once, reflected everywhere
~585 Lines Removed From consuming apps (no more duplicated themes)
Type Safety Full TypeScript support with interfaces
Faster Development Reuse tested components instead of building from scratch
Better DX Hot reload, Storybook for component development
For Business
Benefit Impact
Faster Partner Onboarding Days instead of weeks to launch a new tenant
Consistent UX Same quality experience across all brands
Reduced Costs One team maintains one codebase
Scalability Easy to add new tenants and features

Installation

Perfect for active development when working on the library.

# 1. Build the library
cd /path/to/d2c-component-library
yarn install
yarn build

# 2. In consuming app (b2c-web-demo)
cd /path/to/b2c-web-demo

# Add to package.json:
{
  "dependencies": {
    "igloo-d2c-components": "file:../d2c-component-library"
  }
}

# Install
yarn install

Workflow:

# Make changes to library
cd d2c-component-library
# ... edit files ...
yarn build

# Update consuming app
cd ../b2c-web-demo
yarn install  # Copies updated build
Option 2: NPM Registry (Production)

For production deployments and CI/CD.

Install:

yarn add igloo-d2c-components
# or
npm install igloo-d2c-components

Configure authentication (if using private registry):

# For npm
export NPM_AUTH_TOKEN="your-npm-token"

# For GitLab Package Registry
export GITLAB_NPM_TOKEN="glpat-your-token"
Component Library Management Scripts (in b2c-web-demo)
# Check installed version
yarn lib:check

# Get package information
yarn lib:info

# Upgrade to latest version (from registry)
yarn lib:upgrade

# Install local development version
yarn lib:install-local

# Install registry version
yarn lib:install-registry

# Verify registry configuration
yarn lib:verify-registry

Quick Start

import {
  TenantThemeProvider,
  iglooTheme,
  ammetlifeTheme
} from 'igloo-d2c-components'

function App() {
  return (
    <TenantThemeProvider tenantId="igloo" theme={iglooTheme}>
      <YourApp />
    </TenantThemeProvider>
  )
}
2. Dynamic Theme Loading
import { TenantThemeProvider, getTenantTheme } from 'igloo-d2c-components'

function App({ tenantId }) {
  const theme = getTenantTheme(tenantId) // 'igloo' or 'ammetlife'

  return (
    <TenantThemeProvider tenantId={tenantId} theme={theme}>
      <YourApp />
    </TenantThemeProvider>
  )
}
3. Use Components
import { Button, Card, Banner } from 'igloo-d2c-components'

function MyPage() {
  return (
    <div>
      {/* Tenant-themed button */}
      <Button tenantColored variant="contained">
        Click Me
      </Button>

      {/* Tenant-themed card */}
      <Card
        title="My Card"
        content="Card content"
        tenantAccent
      />

      {/* Tenant-themed banner */}
      <Banner
        title="Welcome"
        description="Get started today"
        gradient
      />
    </div>
  )
}

Centralized Themes

Why Centralized Themes?

Before ( Old Way):

// In each consuming app - duplicated code
const iglooTheme = {
  palette: {
    primary: { main: '#5656F6', dark: '#1300A9', ... },
    // ... 60+ lines per tenant
  }
}

After ( New Way):

// Import from library - single source of truth
import { iglooTheme } from 'igloo-d2c-components'
Available Theme Exports
import {
  // Individual themes
  iglooTheme,      // Igloo brand theme
  ammetlifeTheme,  // AmmetLife insurance theme

  // Theme registry
  tenantThemes,    // { igloo: iglooTheme, ammetlife: ammetlifeTheme }

  // Utility functions
  getTenantTheme,  // (tenantId: TenantId) => TenantThemeConfig
  isValidTenantId, // (id: string) => boolean
  getAvailableTenants, // () => TenantId[]
} from 'igloo-d2c-components'
Theme Structure

Each theme includes:

interface TenantThemeConfig {
  palette: {
    // Core palettes
    primary: { main, dark, light, bright, plain, border }
    secondary: { dim, dark, main, bright, mediumBright, light, lighter }
    tertiary: { dim, dark, main, light, bright }
    natural: { dim, dark, main, light, bright, granite }

    // Product-specific colors
    motor: { main, light, bright }
    car: { main, light, darkAI }
    travel: { main, light }
    health: { main, light? }
    life: { main, light }
    pet: { main }
  }
  typography: {
    fontFamily: string
  }
  logo: string
  logoDark?: string
  logoAlt?: string
  logoWhite?: string
  favicon: string
  assetBaseUrl?: string
}
Using Themes

In Tenant Configuration:

// config/tenants/igloo.ts
import { iglooTheme } from 'igloo-d2c-components'

const iglooConfig: TenantConfig = {
  id: 'igloo',
  theme: iglooTheme, // ✨ That's it!
  // ... other config
}

In Components:

import { useTenantTheme } from 'igloo-d2c-components'

function MyComponent() {
  const { theme, tenantId } = useTenantTheme()

  return (
    <div style={{
      backgroundColor: theme.palette.primary.main,
      color: theme.palette.primary.bright
    }}>
      Current tenant: {tenantId}
    </div>
  )
}

Components

General Components
Button

Tenant-aware button component based on MUI Button.

import { Button } from 'igloo-d2c-components'

// Tenant-colored button
<Button tenantColored variant="contained">
  Tenant Colored Button
</Button>

// Standard MUI button
<Button color="primary" variant="outlined">
  Default Button
</Button>

Props:

  • tenantColored?: boolean - Use tenant primary color
  • variant?: 'text' | 'outlined' | 'contained' - Button variant
  • All MUI ButtonProps
Card

Tenant-aware card component based on MUI Card.

import { Card } from 'igloo-d2c-components'

<Card
  title="Card Title"
  content="Card content goes here"
  actions={<Button>Action</Button>}
  tenantAccent
/>

Props:

  • title?: React.ReactNode - Card title
  • content?: React.ReactNode - Card content
  • actions?: React.ReactNode - Card actions
  • tenantAccent?: boolean - Add tenant-colored top border
  • All MUI CardProps
Banner

Promotional banner with tenant theming.

import { Banner } from 'igloo-d2c-components'

<Banner
  title="Special Offer"
  description="Limited time only"
  action={<Button>Learn More</Button>}
  gradient
/>
NewHeader

Simplified header component with mobile drawer navigation.

import { NewHeader } from 'igloo-d2c-components'

<NewHeader
  logo="/path/to/logo.svg"
  navigationLinks={[
    {
      key: 'car',
      label: 'Car Insurance',
      onClick: () => navigate('/car')
    }
  ]}
  additionalMenuSections={[
    {
      title: 'Account',
      items: [
        { key: 'login', label: 'Log in', onClick: handleLogin }
      ]
    }
  ]}
  onLogoClick={() => navigate('/')}
/>

Props:

interface NewHeaderProps {
  logo: string                          // Logo image source
  navigationLinks?: NewHeaderNavigationLink[]
  additionalMenuSections?: {
    title?: string
    items: NewHeaderNavigationLink[]
  }[]
  isMobile?: boolean
  onLogoClick?: () => void
  onMenuOpen?: () => void
  onMenuClose?: () => void
  customDrawerContent?: React.ReactNode
  formatMessage?: (descriptor: { id: string }) => string
}
Interactive Components
RecommendationsDrawer

Mobile drawer for collecting user preferences and showing personalized recommendations.

import {
  RecommendationsDrawer,
  QuestionSection,
  OptionButton,
  ToggleGroup
} from 'igloo-d2c-components'

function MyComponent() {
  const [open, setOpen] = React.useState(false)
  const [selectedOption, setSelectedOption] = React.useState('')

  const options = [
    { value: 'option1', label: 'Option 1', icon: '🎯' },
    { value: 'option2', label: 'Option 2', icon: '' },
  ]

  return (
    <>
      <Button onClick={() => setOpen(true)}>
        See my recommendations
      </Button>

      <RecommendationsDrawer
        open={open}
        onClose={() => setOpen(false)}
        title="Personalize Your Plan"
        subtitle="Answer a few questions"
        primaryButtonText="Next"
        onPrimaryAction={handleSubmit}
      >
        <QuestionSection
          question="What's your preference?"
          options={options}
          selectedValue={selectedOption}
          onSelect={setSelectedOption}
          renderOption={(option, isSelected) => (
            <OptionButton
              key={option.value}
              value={option.value}
              label={option.label}
              icon={option.icon}
              selected={isSelected}
              onClick={setSelectedOption}
            />
          )}
        />
      </RecommendationsDrawer>
    </>
  )
}

RecommendationsDrawer Props:

interface RecommendationsDrawerProps {
  open: boolean
  onClose: () => void
  children: React.ReactNode
  headerIcon?: string
  title?: string
  subtitle?: string
  showBackButton?: boolean
  onBack?: () => void
  primaryButtonText?: string
  onPrimaryAction?: () => void
  primaryButtonDisabled?: boolean
  secondaryButtonText?: string
  onSecondaryAction?: () => void
  showFooter?: boolean
  customFooter?: React.ReactNode
  formatMessage?: (descriptor: { id: string }) => string
}
ProductSelectionDrawer

Mobile-first bottom drawer for displaying insurance products in a grid.

import { ProductSelectionDrawer, Product } from 'igloo-d2c-components'

const products: Product[] = [
  {
    id: 'life-byond',
    name: 'LIFE BYOND',
    type: 'Term Plan',
    logo: '/path/to/logo.png',
  },
  {
    id: 'health-cvr',
    name: 'HEALTH CVR',
    type: 'CI Plan',
    logo: '/path/to/logo.png',
  },
]

<ProductSelectionDrawer
  open={open}
  onClose={() => setOpen(false)}
  products={products}
  onProductSelect={(productId) => {
    console.log('Selected:', productId)
    setOpen(false)
  }}
  title="Select your product"
  subtitle="Pick the product that suits your protection goals"
/>
ToggleGroup

Segmented control for binary/multiple choice toggles.

import { ToggleGroup } from 'igloo-d2c-components'

<ToggleGroup
  options={[
    { value: 'domestic', label: 'Domestic', icon: '/icon1.svg' },
    { value: 'international', label: 'International', icon: '/icon2.svg' }
  ]}
  value={travelType}
  onChange={setTravelType}
/>
Checkout Components
CheckoutProgress

Progress bar with step indicator for multi-step checkout flows.

import { CheckoutProgress } from 'igloo-d2c-components'

<CheckoutProgress
  currentStep={0}
  totalSteps={3}
  onBack={() => handleBack()}
  showBackButton={true}
/>

Props:

Prop Type Default Description
currentStep number Required Current step (0-indexed)
totalSteps number Required Total number of steps
onBack () => void - Callback when back button is clicked
showBackButton boolean true Whether to show the back button
ProductCard

Product information card for checkout flows.

import { ProductCard } from 'igloo-d2c-components'

<ProductCard
  productName="LIFE BYOND"
  planName="Plan A"
  price="25"
  currency="RM"
  period="/month"
  logoUrl="path/to/logo.png"
  showIndicator={true}
/>
CheckoutHeader

Complete header component combining progress, product card, and section information.

import { CheckoutHeader } from 'igloo-d2c-components'

<CheckoutHeader
  progress={{
    currentStep: 0,
    totalSteps: 3,
    onBack: () => handleBack(),
  }}
  product={{
    productName: 'LIFE BYOND',
    planName: 'Plan A',
    price: '25',
    currency: 'RM',
    period: '/month',
  }}
  sectionTitle="Personal information"
  sectionDescription="Let's get to know you better..."
  sticky={true}
/>
CheckoutFormButton

Fixed or floating button for form submission.

import { CheckoutFormButton } from 'igloo-d2c-components'

<CheckoutFormButton
  text="Next"
  disabled={!isValid}
  onClick={handleSubmit}
  fixed={true}
  type="button"
  loading={isSubmitting}
/>

Tenant Theme Support: The button automatically uses tenant-specific colors:

  • AmmetLife: Blue (#317ABC)
  • Igloo: Black (#13131B)
HealthQuestionGroup

Health questions with Yes/No button options.

import { HealthQuestionGroup } from 'igloo-d2c-components'

<HealthQuestionGroup
  question="Have you ever had any ongoing or past health conditions?"
  questionNumber={1}
  value={healthConditions}
  onChange={(value) => setHealthConditions(value)}
  error={errors.healthConditions}
  labels={{ yes: 'Yes', no: 'No' }}
/>
Form Components

The library includes three reusable checkout form components designed to be form library agnostic.

PersonalInformationForm

Collects personal details, occupation, and banking information.

import { PersonalInformationForm } from 'igloo-d2c-components'

<PersonalInformationForm
  renderField={renderField}
  fields={{
    full_name: {
      name: 'full_name',
      label: 'Full name',
      value: formik.values.full_name,
      error: formik.errors.full_name,
      touched: formik.touched.full_name,
      helperText: 'Full name as per your ID card',
      onChange: formik.handleChange,
      onBlur: formik.handleBlur,
    },
    nric: { /* ... */ },
    date_of_birth: { /* ... */ },
    gender: { /* ... */ },
    // ... other fields
  }}
  consents={{
    bank_account_confirmation: {
      checked: formik.values.bank_account_confirmation,
      onChange: (checked) =>
        formik.setFieldValue('bank_account_confirmation', checked),
      error: formik.errors.bank_account_confirmation,
    },
    marketing_consent: {
      checked: formik.values.marketing_consent,
      onChange: (checked) =>
        formik.setFieldValue('marketing_consent', checked),
    },
  }}
  onSubmit={formik.handleSubmit}
/>
ContactDetailsForm

Collects contact and address information.

import { ContactDetailsForm } from 'igloo-d2c-components'

<ContactDetailsForm
  renderField={renderField}
  fields={{
    phone_number: { /* ... */ },
    email_address: { /* ... */ },
    residential_address: { /* ... */ },
    postal_code: { /* ... */ },
    city: { /* ... */ },
    state: { /* ... */ },
  }}
  mailingAddressSame={{
    checked: formik.values.mailing_same_as_residential,
    onChange: (checked) =>
      formik.setFieldValue('mailing_same_as_residential', checked),
  }}
  onSubmit={formik.handleSubmit}
/>
HealthInformationForm

Collects health measurements and questions with gender-specific support.

import { HealthInformationForm } from 'igloo-d2c-components'

<HealthInformationForm
  renderField={renderField}
  measurementFields={{
    weight: {
      name: 'weight',
      label: 'Weight (kg)',
      type: 'number',
      value: formik.values.weight,
      inputProps: { min: 20, max: 300 },
      onChange: formik.handleChange,
    },
    height: { /* ... */ },
  }}
  healthQuestions={[
    {
      question: 'Have you ever had any ongoing or past health conditions?',
      questionNumber: 1,
      name: 'health_conditions',
      value: formik.values.health_conditions,
      onChange: (value) => formik.setFieldValue('health_conditions', value),
    },
  ]}
  onSubmit={formik.handleSubmit}
/>

Hooks & Utilities

Hooks
useTenantTheme()

Access tenant theme configuration and ID.

import { useTenantTheme } from 'igloo-d2c-components'

function MyComponent() {
  const { theme, tenantId } = useTenantTheme()
  const primaryColor = theme.palette.primary.main

  return <div style={{ color: primaryColor }}>...</div>
}
useTenantId()

Get current tenant ID.

import { useTenantId } from 'igloo-d2c-components'

function MyComponent() {
  const tenantId = useTenantId() // 'igloo' | 'ammetlife'
  return <div>Current tenant: {tenantId}</div>
}
useIsTenant()

Check if current tenant matches a specific ID.

import { useIsTenant } from 'igloo-d2c-components'

function MyComponent() {
  const isAmmetLife = useIsTenant('ammetlife')

  if (isAmmetLife) {
    return <AmmetLifeSpecificFeature />
  }

  return <DefaultFeature />
}

Get tenant logo URL.

import { useTenantLogo } from 'igloo-d2c-components'

function MyComponent() {
  const logo = useTenantLogo()           // Default logo
  const darkLogo = useTenantLogo('dark') // Dark variant
  return <img src={logo} alt="Logo" />
}
Utility Functions
getTenantTheme()

Get theme configuration by tenant ID.

import { getTenantTheme } from 'igloo-d2c-components'

const theme = getTenantTheme('igloo')
console.log(theme.palette.primary.main) // '#5656F6'
isValidTenantId()

Type guard to check if a string is a valid tenant ID.

import { isValidTenantId } from 'igloo-d2c-components'

if (isValidTenantId(userInput)) {
  const theme = getTenantTheme(userInput) // Type-safe!
}
getAvailableTenants()

Get list of all available tenant IDs.

import { getAvailableTenants } from 'igloo-d2c-components'

const tenants = getAvailableTenants()
// ['igloo', 'ammetlife']
getThemeColor()

Extract color from theme using dot notation.

import { getThemeColor } from 'igloo-d2c-components'

const color = getThemeColor(theme, 'primary.main', '#000')
// Returns theme.palette.primary.main or '#000' if not found

Asset Management

Asset Organization
d2c-component-library/src/assets/
├── icons/                    # Common UI icons
│   ├── alert.svg
│   ├── arrow-down.svg
│   ├── close.svg
│   ├── facebook.svg
│   ├── instagram.svg
│   ├── youtube.svg
│   └── index.ts             # Icon path utilities
├── tenants/                 # Tenant-specific assets
│   ├── igloo/logo.svg
│   └── ammetlife/logo.svg
└── index.ts                 # Main asset exports
Asset Flow Architecture
┌─────────────────────────────────────────────────────┐
│  ASSET MANAGEMENT ARCHITECTURE                       │
├─────────────────────────────────────────────────────┤
│                                                      │
│  d2c-component-library/                             │
│  └── src/assets/                                    │
│      ├── icons/           → Common UI icons         │
│      └── tenants/         → Tenant logos            │
│          ├── igloo/logo.svg                         │
│          └── ammetlife/logo.svg                     │
│                                                      │
│  BUILD ⬇️                                            │
│  dist/assets/            → Published with library   │
│                                                      │
│  INSTALL ⬇️                                          │
│  node_modules/igloo-d2c-components/dist/assets/     │
│                                                      │
│  COPY (yarn copy-assets) ⬇️                         │
│  public/assets/          → Served by UMI            │
│                                                      │
└─────────────────────────────────────────────────────┘
Using Assets
// Get Tenant Logo
import { useTenantLogo } from 'igloo-d2c-components'

function MyComponent() {
  const logo = useTenantLogo()           // Default logo
  const darkLogo = useTenantLogo('dark') // Dark variant
  return <img src={logo} alt="Logo" />
}

// Get Icon Paths
import { ICON_PATHS, getIconPath } from 'igloo-d2c-components'
<img src={ICON_PATHS.facebook} alt="Facebook" />
<img src={getIconPath('instagram')} alt="Instagram" />
Asset Distribution Strategy
Asset Type Location Reason
Generic UI Icons Library Reusable across projects
Tenant Logos Library (via theme) Centralized management
Insurer Logos CDN Shared, updated by partners
Marketing Banners Application Frequent changes
Documents (PDFs) CDN Large, rarely change
Logo Rendering in Applications

Important: Assets need to be copied to the public/ folder for UMI to serve them:

# In consuming application
yarn copy-assets  # Copies library assets to public/assets/

The theme paths reference /assets/tenants/{tenant}/logo.svg which maps to:

  • Theme config: logo: '/assets/tenants/igloo/logo.svg'
  • File location: public/assets/tenants/igloo/logo.svg
  • Browser URL: http://localhost:8001/assets/tenants/igloo/logo.svg

Integration Guide

Next.js Integration

Complete Next.js Integration Guide

The library is fully compatible with Next.js 13+ (both App Router and Pages Router).

Quick Start:

# Install
npm install igloo-d2c-components

# Configure next.config.js
module.exports = {
  transpilePackages: ['igloo-d2c-components'],
}

# Copy assets
npm run copy-assets

Key Features:

  • Server-Side Rendering (SSR) support
  • Automatic 'use client' boundaries
  • Optimized for Next.js 13+ App Router
  • Tree-shaking enabled
  • TypeScript support
  • ES2020+ modern output

See NEXTJS_MIGRATION.md for:

  • Detailed setup instructions
  • Asset configuration
  • Common issues & solutions
  • Performance optimization tips
  • Testing strategies
Multi-Tenant Architecture
Tenant Configuration Structure
config/tenants/
├── index.ts         # Tenant registry and utility functions
├── types.ts         # TypeScript interfaces
├── igloo.ts         # Igloo tenant configuration
└── ammetlife.ts     # AmmetLife tenant configuration
Tenant Configuration Example
// config/tenants/igloo.ts
import { iglooTheme } from 'igloo-d2c-components'
import { TenantConfig } from './types'

const iglooConfig: TenantConfig = {
  id: 'igloo',
  name: 'igloo',
  displayName: 'Igloo',
  domain: 'igloo.co.id',

  theme: iglooTheme, // ← Centralized theme from component library

  features: {
    showPAMenu: true,
    enableAIChatbot: true,
    showWorkshopEntryPoint: false,
    enableFreeAddons: false,
  },

  routes: {
    enabled: ['car', 'motorbike', 'health', 'life', 'travel', 'pet'],
    disabled: [],
  },

  branding: {
    companyName: 'Igloo',
    supportEmail: 'hello@iglooinsure.com',
    supportPhone: '+62 21 5022 0888',
  },

  integrations: {
    gtmCode: 'GTM-5RHHNVQ',
    gaCode: 'G-QQV6F41YPJ',
  },
}

export default iglooConfig
AmmetLife Custom Homepage

AmMetLife uses a custom homepage that displays the life insurance landing page:

// config/tenants/ammetlife.ts
customRoutes: [
  {
    path: '/',
    exact: true,
    component: '@/pages/life-landing-page/index.tsx',
  },
]
Checkout Flow Integration
Complete 3-Step Checkout

The library provides components for a complete checkout flow:

  1. Personal Information - 11 fields + 2 consent checkboxes
  2. Contact Details - 6 address fields + mailing checkbox
  3. Health Information - 2 measurements + health questions (gender-specific)
Session Storage Requirements
// Required before navigating to checkout
session.setItem('plan', {
  name: 'Plan A',
  premiumInfo: { currency: 'RM', monthlyAmount: '25' }
});

session.setItem('product', {
  name: 'LIFE BYOND',
  logoUrl: 'url-to-logo'
});
Navigation Flow
PDP (/en/ammetlife-pdp)
  ↓ Click "Buy now"
Checkout Step 1/3 (Personal Info) ← Back → PDP
  ↓ Click "Next"
Checkout Step 2/3 (Contact Details) ← Back → Step 1
  ↓ Click "Next"
Checkout Step 3/3 (Health Information) ← Back → Step 2
  ↓ Click "Submit"
Payment (/en/payment-options)
Back Button Logic
const handleBack = () => {
  if (currentStep === 0) {
    // On first step, redirect to PDP
    window.location.href = `/${currentLocale}/ammetlife-pdp`;
  } else {
    // On other steps, go to previous step
    setCurrentStep(currentStep - 1);
    window.scrollTo({ top: 0, behavior: 'smooth' });
  }
};
Validation Rules

Personal Information:

  • Full name: minimum 2 characters
  • NRIC: numbers and dashes only
  • DOB: 30 days to 80 years old
  • Bank account: numbers only
  • Bank confirmation: must be checked

Contact Details:

  • Phone: 9-14 digits
  • Email: valid format
  • Address: minimum 10 characters
  • Postal code: exactly 5 digits

Health Information:

  • Weight: 20-300 kg
  • Height: 50-250 cm
  • All questions: must select Yes or No
Recommendations Feature
User Flow
1. User sees "See my recommendations" button
   ↓ User clicks
2. Mobile drawer slides up from bottom (95vh height)
   ↓
3. Drawer shows:
   - Toggle: Domestic/International
   - 5 Questions with selectable options
   - Next button
   ↓ User answers questions
4. User clicks "Next" button
   ↓
5. Data collected, drawer closes, recommendations shown
Implementation
import { TravelRecommendations } from '@/components/recommendations';
import { RecommendationsButton } from '@/components/recommendations/recommendations-button';

export default function MyPage() {
  const [showRecommendations, setShowRecommendations] = React.useState(false);

  return (
    <div>
      <RecommendationsButton
        onClick={() => setShowRecommendations(true)}
      />

      <TravelRecommendations
        open={showRecommendations}
        onClose={() => setShowRecommendations(false)}
        onSubmit={(data) => {
          console.log('User selected:', data);
        }}
      />
    </div>
  );
}
Data Structure
interface TravelRecommendationsData {
  travelType: 'domestic' | 'international';
  selectedPlan: string;
  monthlyIncome: string;
  monthlyExpenses: string;
  currentCoverage: string;
  employerCoverage: string;
}
Header Integration
NewHeader Implementation

The NewHeader component provides a simplified header with mobile drawer navigation.

Features:

  1. Product Navigation - Dynamically loaded from navLinks
  2. User Authentication - Login/Logout flow
  3. Additional Links - Partnership, About Us, Blog
  4. Language Selector - EN/ID with cookie persistence
  5. Analytics - Google Analytics integration

Performance Improvement:

Metric Before After
Component size 860 lines 200 lines
Bundle size ~45KB ~15KB
Props count 30+ 9
Routing Guide
Route Pattern
/:locale/life-checkout
Navigation with Locale
// ✅ CORRECT - With locale
const { currentLocale } = local.getItem('globalState');
window.location.href = `/${currentLocale}/life-checkout`;

// ❌ WRONG - Without locale
window.location.href = '/life-checkout';

Development

Prerequisites
  • Node.js: >=16.20.0 <=18.x
  • Yarn: ^1.22.0 (recommended) or npm

Check your version:

node --version  # Should be 16.20.0 - 18.x
yarn --version  # Should be 1.22.x
Setup
# Clone the repository
git clone https://gitlab.iglooinsure.com/axinan/fe/d2c-component-library.git
cd d2c-component-library

# Install dependencies
yarn install
Build Commands
# Production build
yarn build

# Development mode (watch)
yarn dev

# Clean build artifacts
yarn clean

# Clean and rebuild
yarn clean && yarn build
Code Quality
# Lint code
yarn lint

# Type check
yarn type-check
Build Output

The library outputs unminified code targeting ES2015 for maximum compatibility:

dist/
├── cjs/
│   ├── index.js          # CommonJS bundle (unminified, ES2015)
│   └── index.js.map      # Source map
├── esm/
│   ├── index.js          # ES Module bundle (unminified, ES2015)
│   └── index.js.map      # Source map
├── types/
│   └── index.d.ts        # TypeScript definitions
└── assets/               # Copied assets
Project Structure
d2c-component-library/
├── src/
│   ├── components/          # Component implementations
│   │   ├── Button/
│   │   ├── Card/
│   │   ├── Banner/
│   │   ├── NewHeader/
│   │   ├── RecommendationsDrawer/
│   │   ├── ProductSelectionDrawer/
│   │   ├── CheckoutHeader/
│   │   ├── PersonalInformationForm/
│   │   └── ...
│   ├── context/
│   │   └── TenantThemeContext.tsx
│   ├── themes/
│   │   └── index.ts        # ⭐ Centralized theme definitions
│   ├── types/
│   │   └── tenant.ts       # TypeScript types
│   ├── utils/
│   │   ├── theme.ts        # Theme utilities
│   │   └── assets.ts       # Asset utilities
│   ├── assets/             # UI icons and tenant logos
│   └── index.ts            # Main exports
├── dist/                    # Build output (generated)
├── examples/
│   └── usage-example.tsx
├── .storybook/             # Storybook configuration
├── rollup.config.cjs       # Rollup build config
├── tsconfig.json           # TypeScript config
├── package.json
└── README.md               # This file

Storybook

The library includes Storybook for component documentation and testing.

Running Storybook
# Start Storybook dev server
yarn storybook
# Opens at http://localhost:6006

# Build static Storybook
yarn build-storybook
# Output in storybook-static/
Available Stories

NewHeader Stories (11 scenarios):

  1. Default - Basic header with Igloo branding
  2. Authenticated User - Header with logged-in user menu
  3. Mobile View - Responsive mobile layout
  4. AmMetLife Tenant - AmMetLife branding
  5. Minimal Header - Bare-bones implementation
  6. Custom Drawer Content - Advanced customization
  7. Many Menu Items - Stress test with scrolling
  8. Without Logo - Edge case testing
  9. With Internationalization - i18n integration
  10. All Tenants Comparison - Side-by-side view
  11. Responsive Demo - Interactive responsive testing

Other Component Stories:

  • RecommendationsDrawer - Complete questionnaire flow
  • ProductSelectionDrawer - Product grid selection
  • CheckoutHeader - Progress and product display
  • All checkout components
Keyboard Shortcuts
Key Action
F Toggle fullscreen
S Toggle sidebar
A Toggle addons panel
T Toggle toolbar
D Toggle dark mode
/ Search stories

Build Pipeline

Problems Solved
  1. Module parse error: Webpack couldn't parse igloo-d2c-components ESM format
  2. Source map error: Terser plugin couldn't process source maps
Solutions Applied
1. Library: Disabled Source Maps
// rollup.config.cjs
output: [
  {
    file: packageJson.main,
    format: 'cjs',
    sourcemap: false,  // ✅ Disabled
    exports: 'named',
    banner: '"use client"',
  },
]
2. Consumer App: Webpack Configuration
// .umirc.ts
chainWebpack: (memo, { type, webpack }) => {
  // Prefer CommonJS over ESM
  memo.resolve.mainFields.clear().add('main').add('module').add('browser');

  // Include igloo-d2c-components in babel transpilation
  memo.module
    .rule('js')
    .include.add(/node_modules[\\/]igloo-d2c-components/)
    .end();
}
CI/CD Pipeline Options

Option A: Publish to npm (Recommended)

cd d2c-component-library
npm publish --access public

Option B: Use Git URL

{
  "igloo-d2c-components": "git+https://gitlab.com/axinan/fe/d2c-component-library.git#main"
}

Option C: Multi-Repo CI/CD

before_script:
  - cd ..
  - git clone https://gitlab.com/axinan/fe/d2c-component-library.git
  - cd d2c-component-library && yarn install && yarn build
  - cd ../b2c-web-demo && yarn install

Versioning & Publishing

Versioning Strategy

Complete Versioning Guide | Quick Start

This library uses Semantic Versioning (semver) with multiple release channels:

Version Type Example When to Use
Patch Bug fix 1.0.50 → 1.0.51 Security fixes, bug fixes
Minor New feature 1.0.51 → 1.1.0 New components, new props
Major Breaking 1.1.0 → 2.0.0 API changes, removed features
NPM Dist-tags
# Latest stable (production)
npm install igloo-d2c-components          # or @latest

# Next version (beta features)
npm install igloo-d2c-components@next

# v2 beta (next major version)
npm install igloo-d2c-components@v2-beta

# Specific version (recommended for production)
npm install igloo-d2c-components@1.0.51
For Production Apps (@d2c-sales-portal)

Complete Setup Guide | Environment Versioning

Pin versions by environment:

// Production
{
  "dependencies": {
    "igloo-d2c-components": "~1.0.51"  // Pin to production versions
  }
}

// DEV/QA/Staging
{
  "dependencies": {
    "igloo-d2c-components": "non-prod"  // Auto-get latest non-prod
  }
}

Automatic Environment Detection:

  • main branch → Publishes 1.0.51 (production)
  • other branches → Publishes 1.0.51-non-prod (DEV/QA/Staging)

See VERSIONING_STRATEGY.md for complete version management guide.

Quick Release
# Bug fix release
npm run release:patch

# Feature release (test in staging first)
npm run release:minor

# Breaking change (needs migration guide)
npm run release:major

# Beta release (for testing)
npm run release:beta
Prerequisites
  1. Ensure clean working directory:

    git status  # Should be clean
  2. Update version in package.json:

    {
      "version": "1.0.7"
    }
  3. Build the library:

    yarn build
Publishing to NPM

Set up NPM token:

export NPM_AUTH_TOKEN="npm_your_actual_token"

Publish:

# Automated script with safety checks
./publish-to-npm.sh

# Or manual
yarn build
npm publish --access public
Version Management

Semantic Versioning:

  • Major (1.0.0 → 2.0.0): Breaking changes
  • Minor (1.0.0 → 1.1.0): New features, backwards compatible
  • Patch (1.0.0 → 1.0.1): Bug fixes

Update version:

npm version patch  # 1.0.6 → 1.0.7
npm version minor  # 1.0.6 → 1.1.0
npm version major  # 1.0.6 → 2.0.0

Technical Details

Build Configuration

Rollup Configuration (rollup.config.cjs):

{
  input: 'src/index.ts',
  output: [
    {
      file: 'dist/cjs/index.js',
      format: 'cjs',
      sourcemap: true,
      exports: 'named',
      banner: '"use client"',
    },
    {
      file: 'dist/esm/index.js',
      format: 'esm',
      sourcemap: true,
      exports: 'named',
      banner: '"use client"',
    },
  ],
}
TypeScript Configuration

Target: ES2015 for maximum compatibility Module: ESNext for tree-shaking Strict: Enabled for type safety

Peer Dependencies
{
  "peerDependencies": {
    "@emotion/react": "^11.11.4",
    "@emotion/styled": "^11.11.5",
    "@mui/icons-material": "^5.15.20",
    "@mui/material": "^5.15.20",
    "@mui/styles": "^5.15.20",
    "react": "^17.0.0",
    "react-dom": "^17.0.0"
  }
}

Troubleshooting

Webpack Module Parse Error

Error:

Module parse failed: Unexpected token

Solution:

cd d2c-component-library
yarn clean && yarn build
cd ../b2c-web-demo
rm -rf node_modules/igloo-d2c-components
yarn install
Theme Not Found Error

Error:

Theme not found for tenant: xxx

Solution: Use valid tenant IDs: 'igloo' or 'ammetlife'.

TypeScript Errors with Imports

Error:

Module '"igloo-d2c-components"' has no exported member 'iglooTheme'

Solution:

  1. Rebuild the library: yarn build
  2. Reinstall in consuming app: yarn install
  3. Restart TypeScript server in your IDE
Build Failures

Error: Build fails with memory issues

Solution:

export NODE_OPTIONS="--max-old-space-size=4096"
yarn build
Asset Issues

Logos not displaying:

  1. Run yarn copy-assets in consuming app
  2. Verify assets in public/assets/tenants/
  3. Check theme path: /assets/tenants/igloo/logo.svg
Routing Issues

Issue: 404 on /life-checkout Use locale prefix: /en/life-checkout, not /life-checkout


Changelog

[Unreleased]
Added
  • CheckoutProgress - Progress bar component with step indicator
  • ProductCard - Product information card for checkout flows
  • CheckoutHeader - Complete checkout header combining progress and product card
  • CheckoutFormButton - Fixed/floating button for form submission
  • HealthQuestionGroup - Health question component with Yes/No options
  • ProductSelectionDrawer - Mobile-first bottom drawer for product selection
  • PersonalInformationForm - Personal details form component
  • ContactDetailsForm - Contact and address form component
  • HealthInformationForm - Health measurements and questions form
  • Comprehensive Storybook documentation
  • Integration guides for multi-tenant architecture
[1.0.1] - 2025-11-11
Changed
  • Package name configured for public NPM registry
  • Updated all documentation
[1.0.0] - 2025-10-31
Added
  • Initial release of D2C Component Library
  • TenantThemeProvider - Context provider for tenant theming
  • Hooks: useTenantTheme(), useTenantId(), useIsTenant()
  • Components: Button, Card, Banner
  • Utility functions for theme management
  • Full TypeScript support
  • ESM and CommonJS builds

Contributing

Getting Started
  1. Fork the repository

  2. Create a feature branch:

    git checkout -b feature/my-new-feature
  3. Make your changes

  4. Test thoroughly:

    yarn lint
    yarn type-check
    yarn build
  5. Test in consuming app:

    cd ../b2c-web-demo
    yarn install
    yarn start-igloo-dev
  6. Commit your changes:

    git commit -m "feat: add new feature"
  7. Push and create a Pull Request

Commit Message Format

Follow Conventional Commits:

<type>(<scope>): <description>

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • style: Code style changes (formatting)
  • refactor: Code refactoring
  • test: Test additions/changes
  • chore: Maintenance tasks
Adding a New Tenant
  1. Add theme to src/themes/index.ts:

    export const newTenantTheme: TenantThemeConfig = {
      palette: { /* ... */ },
      typography: { /* ... */ },
      logo: '/assets/tenants/newtenant/logo.svg',
      favicon: 'https://...',
    }
    
    export const tenantThemes: Record<TenantId, TenantThemeConfig> = {
      igloo: iglooTheme,
      ammetlife: ammetlifeTheme,
      newtenant: newTenantTheme, // Add here
    }
  2. Update TenantId type in src/types/tenant.ts:

    export type TenantId = 'igloo' | 'ammetlife' | 'newtenant'
  3. Add logo asset to src/assets/tenants/newtenant/

  4. Build and test:

    yarn build
Adding New Components
  1. Create component folder in src/components/
  2. Include: ComponentName.tsx, styled.tsx, index.ts
  3. Add Storybook stories
  4. Export from src/index.ts
  5. Update documentation

License

MIT


Team

Frontend Engineering Team - Axinan/Igloo



Quick Reference

Installation
# Local development
yarn add igloo-d2c-components@file:../d2c-component-library

# Production
yarn add igloo-d2c-components@latest
Import Themes
import { iglooTheme, ammetlifeTheme, getTenantTheme } from 'igloo-d2c-components'
Import Components
import { Button, Card, Banner, TenantThemeProvider, NewHeader, CheckoutHeader } from 'igloo-d2c-components'
Import Hooks
import { useTenantTheme, useTenantId, useIsTenant, useTenantLogo } from 'igloo-d2c-components'
Build & Publish
# Build
yarn build

# Publish to NPM
./publish-to-npm.sh
Test All Tenants (in b2c-web-demo)
yarn start-igloo        # Port 8000
yarn start-ammetlife    # Port 8001

Version: 1.0.11+ Last Updated: December 2025 Node.js: >=16.20.0 <=18.x Target: ES2015 Output: Unminified

Keywords