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

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!