6.7 KiB
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.featuresarray
4. Salesforce Field Mapping
- ❌ Frontend guessing product types
- ✅ Backend uses actual
Internet_Plan_Tier__c,Addon_Type__c,Installation_Type__cfields
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
-
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 -
Use New Backend Endpoint:
// Replace this: const catalog = await api.get("/catalog"); // With this: const catalog = await api.get("/catalog/structured"); -
Update Frontend Components:
// Remove all name matching logic // Use structured data properties instead // Old: plan.name?.toLowerCase().includes('gold') // New: plan.tier === 'Gold' -
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!