Assist_Design/docs/STRUCTURED-CATALOG-EXAMPLE.md
2025-08-27 20:01:46 +09:00

6.7 KiB

Structured Catalog Architecture - Frontend Example

OLD WAY (Bad): Name Matching in Frontend

// WRONG: Frontend doing business logic and name matching
const [plans, setPlans] = useState<InternetPlan[]>([]);

// Bad API call - flat structure requiring guesswork
const catalog = await api.get<{internet: InternetPlan[]}>("/catalog");

// Terrible: Guessing product types by name matching  
const isSilver = plan.name?.toLowerCase().includes('silver');
const isGold = plan.name?.toLowerCase().includes('gold');
const isPlatinum = plan.name?.toLowerCase().includes('platinum');

// Bad: SKU parsing in frontend
const hikariService = addons.find(addon => 
  addon.sku.includes('HOME-PHONE') || addon.sku.includes('HIKARI-DENWA')
);

// Bad: Installation type guessing
const installationProduct = installations.find(inst => 
  inst.sku.toLowerCase().includes('12') || 
  inst.name.toLowerCase().includes('12')
);

// Frontend doing business logic it shouldn't
{isSilver && (
  <div>Basic setup - bring your own router</div>
)}
{isGold && (
  <div>Complete solution with v6plus router</div>
)}

NEW WAY (Good): Structured Data from Backend

// CORRECT: Backend organizes everything, frontend just displays
const [catalogData, setCatalogData] = useState<StructuredCatalog | null>(null);

// Good API call - fully structured data
const catalog = await api.get<StructuredCatalog>("/catalog/structured");

// No name matching needed! Backend already organized everything
const { internet } = catalog;
const { plans, installations, addons } = internet;

// Frontend just displays structured data
{plans.map(plan => (
  <InternetPlanCard 
    key={plan.id}
    plan={plan}
    // No guessing - data comes pre-structured!
    tier={plan.tier}                    // 'Silver' | 'Gold' | 'Platinum'
    features={plan.features}            // Pre-computed feature list
    isRecommended={plan.isRecommended}  // Boolean from backend
    tierDescription={plan.tierDescription} // "Basic" | "Recommended" | "Premium"
    monthlyPrice={plan.monthlyPrice}    // Direct from Salesforce PricebookEntry
  />
))}

// Add-ons are properly typed and organized
{addons
  .filter(addon => addon.type === 'hikari-denwa-service')
  .map(addon => (
    <AddonCard
      key={addon.id}
      name={addon.name}
      monthlyPrice={addon.monthlyPrice}  // Direct from Salesforce
      autoAdd={addon.autoAdd}           // From Salesforce Auto_Add__c field
      requiredWith={addon.requiredWith} // From Required_Products__c field
    />
  ))}

// Installation options - no guessing needed
{installations.map(inst => (
  <InstallationOption
    key={inst.id}
    type={inst.type}              // 'One-time' | '12-Month' | '24-Month'
    price={inst.price}            // Direct from Salesforce PricebookEntry
    billingCycle={inst.billingCycle} // From Salesforce Billing_Cycle__c
  />
))}

🔄 API Response Structure

Old Response (Bad)

{
  "internet": [
    {"id": "1", "name": "Internet Gold Plan (Home 1G)", "sku": "INTERNET-GOLD-HOME-1G"},
    {"id": "2", "name": "Internet Silver Plan (Home 1G)", "sku": "INTERNET-SILVER-HOME-1G"}
  ]
}

Frontend has to guess everything from names/SKUs! 😞

New Response (Good)

{
  "internet": {
    "plans": [
      {
        "id": "1",
        "name": "Internet Gold Plan (Home 1G)",
        "sku": "INTERNET-GOLD-HOME-1G",
        "tier": "Gold",
        "offeringType": "Home 1G",
        "monthlyPrice": 6500,
        "description": "Complete solution with v6plus router included",
        "features": [
          "1 NTT Wireless Home Gateway Router (v6plus compatible)",
          "1 SonixNet ISP (IPoE-HGW) Activation + Monthly",
          "Professional setup included"
        ],
        "tierDescription": "Recommended",
        "isRecommended": true
      }
    ],
    "installations": [
      {
        "id": "10",
        "name": "NTT Installation Fee (12-Month Plan)",
        "sku": "INTERNET-INSTALL-12M",
        "type": "12-Month",
        "price": 1900,
        "billingCycle": "Monthly",
        "description": "NTT Installation Fee (12-Month Plan)"
      }
    ],
    "addons": [
      {
        "id": "20",
        "name": "Hikari Denwa (Home Phone)",
        "sku": "INTERNET-ADDON-HOME-PHONE",
        "type": "hikari-denwa-service",
        "monthlyPrice": 450,
        "autoAdd": false,
        "requiredWith": ["INTERNET-ADDON-HIKARI-DENWA-INSTALL"]
      },
      {
        "id": "21",
        "name": "Hikari Denwa Installation Fee",
        "sku": "INTERNET-ADDON-HIKARI-DENWA-INSTALL",
        "type": "hikari-denwa-installation",
        "activationPrice": 1000,
        "autoAdd": true,
        "requiredWith": []
      }
    ]
  }
}

Everything pre-organized! Frontend just displays! 🎉

🎯 Benefits of Structured Approach

1. No Name Matching

  • plan.name.includes('gold')
  • plan.tier === 'Gold'

2. No SKU Parsing

  • addon.sku.includes('HIKARI-DENWA')
  • addon.type === 'hikari-denwa-service'

3. Business Logic in Right Place

  • Frontend calculating features based on tier names
  • Backend provides pre-computed plan.features array

4. Salesforce Field Mapping

  • Frontend guessing product types
  • Backend uses actual Internet_Plan_Tier__c, Addon_Type__c, Installation_Type__c fields

5. Extensible Without Code Changes

  • New product types break name matching
  • New Salesforce fields automatically supported

6. Type Safety

  • String matching is error-prone
  • Strongly typed enums: 'Silver' | 'Gold' | 'Platinum'

🚀 Implementation Steps

  1. Add Salesforce Fields (if missing):

    -- Custom fields for proper categorization
    Internet_Plan_Tier__c (Picklist): Silver | Gold | Platinum
    Addon_Type__c (Text): Hikari Denwa Service | Hikari Denwa Installation  
    Installation_Type__c (Picklist): One-time | 12-Month | 24-Month
    
  2. Use New Backend Endpoint:

    // Replace this:
    const catalog = await api.get("/catalog");
    
    // With this:
    const catalog = await api.get("/catalog/structured");
    
  3. Update Frontend Components:

    // Remove all name matching logic
    // Use structured data properties instead
    
    // Old: plan.name?.toLowerCase().includes('gold')
    // New: plan.tier === 'Gold'
    
  4. Add New Salesforce Fields to Field Mapping:

    // In field-map.ts
    internetPlanTier: "Internet_Plan_Tier__c",
    addonType: "Addon_Type__c", 
    installationType: "Installation_Type__c"
    

This approach makes the frontend much simpler, more reliable, and fully leverages Salesforce's structured data instead of relying on fragile name matching!