222 lines
6.7 KiB
Markdown
222 lines
6.7 KiB
Markdown
# Structured Catalog Architecture - Frontend Example
|
|
|
|
## ❌ **OLD WAY (Bad)**: Name Matching in Frontend
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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)
|
|
```json
|
|
{
|
|
"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)
|
|
```json
|
|
{
|
|
"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):
|
|
```sql
|
|
-- 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**:
|
|
```typescript
|
|
// Replace this:
|
|
const catalog = await api.get("/catalog");
|
|
|
|
// With this:
|
|
const catalog = await api.get("/catalog/structured");
|
|
```
|
|
|
|
3. **Update Frontend Components**:
|
|
```typescript
|
|
// 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**:
|
|
```typescript
|
|
// 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!
|