diff --git a/.cursor/plans/d-67f8fea5.plan.md b/.cursor/plans/d-67f8fea5.plan.md deleted file mode 100644 index 9ba89a1b..00000000 --- a/.cursor/plans/d-67f8fea5.plan.md +++ /dev/null @@ -1,350 +0,0 @@ - - -# Domain & BFF Clean Architecture Refactoring - -## Overview - -Establish clean separation between domain (business logic) and BFF (infrastructure) layers by: - -1. Removing redundant mapper service wrappers in BFF -2. Moving query builders from domain to BFF integration -3. Using domain mappers directly in BFF services -4. Eliminating unnecessary transformation layers - -## Current Issues - -### 1. Query Builders in Wrong Layer - -**Location**: `packages/domain/orders/providers/salesforce/query.ts` - -- `buildOrderSelectFields()`, `buildOrderItemSelectFields()`, `buildOrderItemProduct2Fields()` -- These are SOQL infrastructure concerns, not business logic -- Domain should not know about Salesforce query language - -### 2. Redundant Mapper Service Wrapper - -**Location**: `apps/bff/src/modules/orders/services/order-whmcs-mapper.service.ts` - -- Just wraps `Providers.Whmcs.mapFulfillmentOrderItems()` from domain -- Adds logging but no transformation logic -- Creates confusion about where mapping lives - -### 3. Double Transformation Pattern - -**Current Flow**: - -``` -BFF query → Raw SF data → Domain mapper → Domain type → BFF mapper??? → Same type -``` - -**Should Be**: - -``` -BFF query → Raw SF data → Domain mapper → Domain type → Use directly -``` - -### 4. Catalog Services Do It Correctly - -**Good Example**: `apps/bff/src/modules/catalog/services/sim-catalog.service.ts` - -```typescript -const product = CatalogProviders.Salesforce.mapSimProduct(record, entry); -// Uses domain mapper directly, no BFF wrapper! -``` - -## Architecture Principles - -### Domain Layer (`packages/domain/`) - -**Contains**: - -- ✅ Business types (OrderDetails, OrderSummary) -- ✅ Raw provider types (SalesforceOrderRecord) -- ✅ Validation schemas (Zod) -- ✅ Transformation mappers (Raw → Domain) -- ✅ Business validation functions - -**Does NOT Contain**: - -- ❌ Query builders (SOQL, GraphQL) -- ❌ Field configuration -- ❌ HTTP/API concerns - -### BFF Integration Layer (`apps/bff/src/integrations/`) - -**Contains**: - -- ✅ Query builders (SOQL construction) -- ✅ Connection services -- ✅ Integration services that: - - Build queries - - Execute queries - - Use domain mappers - - Return domain types - -**Does NOT Contain**: - -- ❌ Additional mapping logic -- ❌ Business validation - -### BFF Application Layer (`apps/bff/src/modules/`) - -**Contains**: - -- ✅ Orchestrators (workflow coordination) -- ✅ Controllers (HTTP endpoints) -- ✅ Uses integration services -- ✅ Uses domain types directly - -**Does NOT Contain**: - -- ❌ Direct Salesforce queries -- ❌ Mapper service wrappers -- ❌ Double transformations - -## Refactoring Steps - -### Phase 1: Create Salesforce Integration Services - -#### 1.1 Create `SalesforceOrderService` - -**File**: `apps/bff/src/integrations/salesforce/services/salesforce-order.service.ts` - -Encapsulates all Salesforce order operations: - -- `getOrderById(orderId): Promise` -- `getOrdersForAccount(accountId): Promise` -- Builds queries internally -- Uses domain mappers for transformation -- Returns domain types - -**Benefits**: - -- Encapsulation of SF-specific logic -- Easy to test -- Easy to swap providers -- No SF details leak to application layer - -#### 1.2 Update OrderOrchestrator - -- Remove direct `this.sf.query()` calls -- Inject and use `SalesforceOrderService` -- Remove query building logic -- Just coordinate workflows - -### Phase 2: Move Query Builders - -#### 2.1 Move Query Builders to BFF - -**From**: `packages/domain/orders/providers/salesforce/query.ts` - -**To**: `apps/bff/src/integrations/salesforce/utils/order-query-builder.ts` - -Move these functions: - -- `buildOrderSelectFields()` -- `buildOrderItemSelectFields()` -- `buildOrderItemProduct2Fields()` - -#### 2.2 Clean Domain Exports - -Remove query builder exports from: - -- `packages/domain/orders/providers/salesforce/index.ts` -- `packages/domain/orders/providers/index.ts` -- `packages/domain/orders/index.ts` - -### Phase 3: Remove Redundant Mapper Services - -#### 3.1 Delete OrderWhmcsMapper Service - -**File to DELETE**: `apps/bff/src/modules/orders/services/order-whmcs-mapper.service.ts` - -It only wraps domain mappers - provides no value. - -#### 3.2 Update OrderFulfillmentOrchestrator - -Replace: - -```typescript -constructor(private orderWhmcsMapper: OrderWhmcsMapper) {} -const result = this.orderWhmcsMapper.mapOrderItemsToWhmcs(items); -``` - -With: - -```typescript -import { Providers } from "@customer-portal/domain/orders"; -const result = Providers.Whmcs.mapFulfillmentOrderItems(items); -``` - -**Direct domain mapper usage** - single transformation! - -#### 3.3 Update orders.module.ts - -- Remove `OrderWhmcsMapper` from providers -- Remove import statement - -### Phase 4: Verify Catalog Pattern Consistency - -Catalog services already follow the clean pattern - verify they continue to: - -- Build queries in BFF service layer -- Use `CatalogProviders.Salesforce.mapXXXProduct()` directly -- Return domain types without additional mapping - -### Phase 5: Clean Up Field Configuration (If Unused) - -**Investigation needed**: Check if `SalesforceFieldConfigService` is actually used. - -If NOT used in order/catalog flows: - -- Consider removing or documenting as future feature -- Field names are already defined in raw types - -### Phase 6: Documentation Updates - -#### 6.1 Update Domain README - -- Clarify that query builders don't belong in domain -- Add architecture diagram showing clear boundaries - -#### 6.2 Create Integration Layer Guide - -Document: - -- When to create integration services -- Pattern: Query → Transform → Return domain type -- No additional mapping in BFF - -#### 6.3 Update ORDERS-ARCHITECTURE-REVIEW.md - -- Mark query builders as moved -- Mark mapper wrappers as removed -- Show final clean architecture - -## File Changes Summary - -### Files to CREATE - -1. `apps/bff/src/integrations/salesforce/services/salesforce-order.service.ts` -2. `apps/bff/src/integrations/salesforce/utils/order-query-builder.ts` - -### Files to MODIFY - -3. `apps/bff/src/modules/orders/services/order-orchestrator.service.ts` - Use SalesforceOrderService -4. `apps/bff/src/modules/orders/services/order-fulfillment-orchestrator.service.ts` - Use domain mapper directly -5. `apps/bff/src/modules/orders/orders.module.ts` - Update providers -6. `apps/bff/src/integrations/salesforce/salesforce.module.ts` - Export new service -7. `packages/domain/orders/providers/salesforce/index.ts` - Remove query exports -8. `packages/domain/orders/providers/index.ts` - Remove query exports -9. `packages/domain/orders/index.ts` - Remove query exports - -### Files to DELETE - -10. `apps/bff/src/modules/orders/services/order-whmcs-mapper.service.ts` -11. `packages/domain/orders/providers/salesforce/query.ts` - -### Documentation to UPDATE - -12. `packages/domain/README.md` -13. `ORDERS-ARCHITECTURE-REVIEW.md` -14. Create: `docs/BFF-INTEGRATION-PATTERNS.md` - -## Expected Outcomes - -### Architecture Cleanliness - -- ✅ Single source of truth for transformations (domain mappers) -- ✅ Clear separation: domain = business, BFF = infrastructure -- ✅ No redundant mapping layers -- ✅ Query logic in correct layer (BFF integration) - -### Code Quality - -- ✅ Easier to test (clear boundaries) -- ✅ Easier to maintain (no duplication) -- ✅ Easier to understand (one transformation path) -- ✅ Easier to swap providers (integration services encapsulate) - -### Developer Experience - -- ✅ Clear patterns to follow -- ✅ No confusion about where code goes -- ✅ Consistent with catalog services -- ✅ Self-documenting architecture - -## Migration Notes - -### Breaking Changes - -**None for consumers** - All changes are internal refactoring - -### Testing - -- Unit test new `SalesforceOrderService` -- Verify order creation flow still works -- Verify order fulfillment flow still works -- Verify catalog fetching still works - -### Rollback Plan - -Git history preserves old structure - can revert commits if issues arise. - -## Success Criteria - -- [x] Query builders moved to BFF integration layer -- [x] `OrderWhmcsMapper` service deleted -- [x] `SalesforceOrderService` created and used -- [x] `OrderOrchestrator` no longer builds SOQL queries -- [x] `OrderFulfillmentOrchestrator` uses domain mapper directly -- [x] Domain exports cleaned (no query builders) -- [x] Documentation updated -- [x] All tests passing (no linting errors) -- [x] Order creation works end-to-end (ready for testing) -- [x] Order fulfillment works end-to-end (ready for testing) - ---- - -**Final Architecture**: - -``` -┌─────────────────────────────────────────┐ -│ Controller (HTTP) │ -└──────────────┬──────────────────────────┘ - │ -┌──────────────▼──────────────────────────┐ -│ Orchestrator (Application) │ -│ - Coordinates workflows │ -│ - Uses Integration Services │ -│ - Works with Domain Types │ -└──────────────┬──────────────────────────┘ - │ - ┌──────────┴──────────┐ - │ │ -┌───▼───────────┐ ┌──────▼──────────────┐ -│ Domain │ │ Integration │ -│ (Business) │ │ (Infrastructure) │ -│ │ │ │ -│ • Types │ │ • SF OrderService │ -│ • Schemas │ │ • Query Builders │ -│ • Mappers ────┼──┤ • Connections │ -│ • Validators │ │ • Field Mapping │ -└───────────────┘ └─────────────────────┘ - -Flow: Query (BFF) → Raw Data → Domain Mapper → Domain Type → Use Directly - └─ One transformation, no duplication ─┘ -``` - -### To-dos - -- [x] Create SalesforceOrderService in BFF integration layer with methods: getOrderById, getOrdersForAccount -- [x] Move query builders from packages/domain/orders/providers/salesforce/query.ts to apps/bff/src/integrations/salesforce/utils/order-query-builder.ts -- [x] Update OrderOrchestrator to use SalesforceOrderService instead of direct SF queries -- [x] Delete redundant OrderWhmcsMapper service wrapper -- [x] Update OrderFulfillmentOrchestrator to use domain Providers.Whmcs mapper directly -- [x] Remove query builder exports from domain package index files -- [x] Update orders.module.ts and salesforce.module.ts with new services -- [x] Verify catalog services follow same clean pattern (already correct) -- [x] Update domain README and architecture documentation with clean patterns -- [x] Test order creation and fulfillment flows end-to-end diff --git a/.cursor/plans/restructure_to_account_portal_efdb4b10.plan.md b/.cursor/plans/restructure_to_account_portal_efdb4b10.plan.md deleted file mode 100644 index 9fea44ba..00000000 --- a/.cursor/plans/restructure_to_account_portal_efdb4b10.plan.md +++ /dev/null @@ -1,390 +0,0 @@ ---- -name: Restructure to Account Portal -overview: Restructure the app to have public pages under (public)/ and all authenticated portal pages under /account/*, with auth-aware headers in public shells. -todos: - - id: auth-aware-public-shell - content: "Make PublicShell auth-aware: show 'My Account' for logged-in users, 'Sign in' for guests" - status: pending - - id: auth-aware-catalog-shell - content: Make CatalogShell auth-aware with same pattern - status: pending - - id: create-account-layout - content: Create account/layout.tsx with AppShell and auth guard redirect - status: pending - - id: move-dashboard-to-account - content: Move dashboard page to account/page.tsx - status: pending - - id: move-billing-to-account - content: Move billing pages to account/billing/* - status: pending - - id: move-subscriptions-to-services - content: Move subscriptions to account/services/* - status: pending - - id: move-orders-to-account - content: Move orders to account/orders/* - status: pending - - id: move-support-to-account - content: Move support cases to account/support/* - status: pending - - id: move-profile-to-settings - content: Move account/profile to account/settings/* - status: pending - - id: fix-shop-double-header - content: Fix shop layout to not create double header - add CatalogNav only - status: pending - - id: create-contact-route - content: Create (public)/contact/page.tsx for contact form - status: pending - - id: update-navigation - content: Update AppShell navigation.ts with /account/* paths - status: pending - - id: update-catalog-links - content: Replace all /catalog links with /shop - status: pending - - id: update-portal-links - content: Replace all old portal links with /account/* paths - status: pending - - id: remove-sfnumber - content: Remove sfNumber from domain schema and signup components - status: pending - - id: delete-old-authenticated - content: Delete (authenticated)/ directory after migration - status: pending - - id: rebuild-test - content: Rebuild domain package and test all routes - status: pending ---- - -# Restructure Portal to /account/\* Architecture - -## Target Architecture - -```mermaid -flowchart TB - subgraph public ["(public)/ - Public Pages"] - P1["/"] --> Home["Homepage"] - P2["/auth/*"] --> Auth["Login, Signup, etc"] - P3["/shop/*"] --> Shop["Product Catalog"] - P4["/help"] --> Help["FAQ & Knowledge Base"] - P5["/contact"] --> Contact["Contact Form"] - P6["/order/*"] --> Order["Checkout Flow"] - end - - subgraph account ["/account/* - My Portal"] - A1["/account"] --> Dashboard["Dashboard"] - A2["/account/billing"] --> Billing["Invoices & Payments"] - A3["/account/services"] --> Services["My Subscriptions"] - A4["/account/orders"] --> Orders["Order History"] - A5["/account/support"] --> Support["My Tickets"] - A6["/account/settings"] --> Settings["Profile Settings"] - end - - public -.->|"Auth-aware header"| account -``` - ---- - -## Phase 1: Make Shells Auth-Aware - -### 1.1 Update PublicShell - -**File:** `apps/portal/src/components/templates/PublicShell/PublicShell.tsx` - -Add auth detection to header navigation: - -```tsx -"use client"; -import { useAuthStore } from "@/features/auth/services/auth.store"; - -export function PublicShell({ children }: PublicShellProps) { - const { isAuthenticated } = useAuthStore(); - - return ( -
-
- -
- ... -
- ); -} -``` - -### 1.2 Update CatalogShell - -**File:** `apps/portal/src/components/templates/CatalogShell/CatalogShell.tsx` - -Same auth-aware pattern - show "My Account" or "Sign in" based on auth state. - ---- - -## Phase 2: Create /account Route Structure - -### 2.1 Create Account Layout with Auth Guard - -**File:** `apps/portal/src/app/account/layout.tsx` (NEW) - -```tsx -import { redirect } from "next/navigation"; -import { cookies } from "next/headers"; -import { AppShell } from "@/components/organisms/AppShell"; - -export default async function AccountLayout({ children }: { children: React.ReactNode }) { - const cookieStore = await cookies(); - const hasAuthToken = cookieStore.has("access_token"); - - if (!hasAuthToken) { - redirect("/auth/login?redirect=/account"); - } - - return {children}; -} -``` - -### 2.2 Create Account Pages - -Move and rename pages: - -| Current Path | New Path | New File | - -|--------------|----------|----------| - -| `(authenticated)/dashboard/page.tsx` | `/account` | `account/page.tsx` | - -| `(authenticated)/billing/*` | `/account/billing/*` | `account/billing/*` | - -| `(authenticated)/subscriptions/*` | `/account/services/*` | `account/services/*` | - -| `(authenticated)/orders/*` | `/account/orders/*` | `account/orders/*` | - -| `(authenticated)/support/*` | `/account/support/*` | `account/support/*` | - -| `(authenticated)/account/*` | `/account/settings/*` | `account/settings/*` | - ---- - -## Phase 3: Update Navigation - -### 3.1 Update AppShell Navigation - -**File:** `apps/portal/src/components/organisms/AppShell/navigation.ts` - -Update all paths to use `/account/*`: - -```typescript -export const baseNavigation: NavigationItem[] = [ - { name: "Dashboard", href: "/account", icon: HomeIcon }, - { name: "Orders", href: "/account/orders", icon: ClipboardDocumentListIcon }, - { - name: "Billing", - icon: CreditCardIcon, - children: [ - { name: "Invoices", href: "/account/billing/invoices" }, - { name: "Payment Methods", href: "/account/billing/payments" }, - ], - }, - { - name: "My Services", - icon: ServerIcon, - children: [{ name: "All Services", href: "/account/services" }], - }, - { name: "Shop", href: "/shop", icon: Squares2X2Icon }, // Links to public shop - { - name: "Support", - icon: ChatBubbleLeftRightIcon, - children: [ - { name: "My Tickets", href: "/account/support" }, - { name: "New Ticket", href: "/account/support/new" }, - ], - }, - { name: "Settings", href: "/account/settings", icon: UserIcon }, - { name: "Log out", href: "#", icon: ArrowRightStartOnRectangleIcon, isLogout: true }, -]; -``` - ---- - -## Phase 4: Fix Public Routes - -### 4.1 Fix Double Header in Shop - -Remove the nested shell issue by having CatalogShell NOT render a full page wrapper, or by not nesting it under PublicShell. - -**Option A:** Move shop out of (public) to its own route with CatalogShell only - -**Option B:** Have (public)/shop/layout.tsx return just children with catalog nav (no shell) - -Recommended: **Option B** - Keep shop under (public) but have shop layout add only catalog navigation, not a full shell. - -**File:** `apps/portal/src/app/(public)/shop/layout.tsx` - -```tsx -import { CatalogNav } from "@/components/templates/CatalogShell"; - -export default function ShopLayout({ children }: { children: React.ReactNode }) { - // Don't wrap with another shell - parent (public) layout already has PublicShell - return ( - <> - - {children} - - ); -} -``` - -**File:** `apps/portal/src/components/templates/CatalogShell/CatalogShell.tsx` - -Split into two exports: - -- `CatalogShell` - full shell (if ever needed standalone) -- `CatalogNav` - just the navigation bar - -### 4.2 Create /contact Route - -**File:** `apps/portal/src/app/(public)/contact/page.tsx` (NEW) - -Move content from `(public)/help/contact/` to `(public)/contact/`. - ---- - -## Phase 5: Delete Old Routes - -### 5.1 Delete (authenticated) Directory - -After moving all content to /account/: - -- Delete entire `apps/portal/src/app/(authenticated)/` directory - -### 5.2 Clean Up Unused Files - -- Delete `(public)/help/contact/` (moved to /contact) -- Keep `(public)/help/page.tsx` for FAQ - ---- - -## Phase 6: Update All Internal Links - -### 6.1 Update /catalog to /shop Links - -Replace in feature components (11 files, 27 occurrences): - -``` -/catalog → /shop -/catalog/internet → /shop/internet -/catalog/sim → /shop/sim -/catalog/vpn → /shop/vpn -``` - -### 6.2 Update Dashboard/Portal Links - -Replace throughout codebase: - -``` -/dashboard → /account -/billing → /account/billing -/subscriptions → /account/services -/orders → /account/orders -/support/cases → /account/support -``` - ---- - -## Phase 7: Remove sfNumber from Signup - -### 7.1 Update Domain Schema - -**File:** `packages/domain/auth/schema.ts` - -```typescript -// Line 44: Remove required sfNumber -// Before: -sfNumber: z.string().min(6, "Customer number must be at least 6 characters"), - -// After: -sfNumber: z.string().optional(), -``` - -Also update `validateSignupRequestSchema` to not require sfNumber. - -### 7.2 Update SignupForm Components - -- `SignupForm.tsx` - Remove sfNumber from initialValues and validation -- `AccountStep.tsx` - Remove Customer Number form field -- `ReviewStep.tsx` - Remove Customer Number display - ---- - -## Phase 8: Rebuild and Test - -### 8.1 Rebuild Domain Package - -```bash -pnpm --filter @customer-portal/domain build -``` - -### 8.2 Test Matrix - -| Scenario | URL | Expected | - -|----------|-----|----------| - -| Public homepage | `/` | PublicShell, homepage content | - -| Public shop | `/shop` | CatalogShell (auth-aware), products | - -| Auth user in shop | `/shop` | "My Account" button, personalized pricing | - -| Public help | `/help` | FAQ content | - -| Public contact | `/contact` | Contact form, prefills if logged in | - -| Login | `/auth/login` | Login form | - -| Signup | `/auth/signup` | No sfNumber field | - -| Account dashboard | `/account` | AppShell, dashboard (redirect if not auth) | - -| My services | `/account/services` | Subscriptions list | - -| My tickets | `/account/support` | Support cases | - -| Checkout | `/order` | CheckoutShell, wizard | - ---- - -## Files Summary - -| Category | Action | Count | - -|----------|--------|-------| - -| New account/ routes | Create | ~15 files | - -| Shell components | Modify | 2 (PublicShell, CatalogShell) | - -| Shop layout | Modify | 1 | - -| Navigation | Modify | 1 | - -| Link updates | Modify | ~20 files | - -| Domain schema | Modify | 1 | - -| Signup components | Modify | 3 | - -| Delete old routes | Delete | ~20 files | - -**Total: ~60+ file operations** diff --git a/.cursor/rules/portal-rule.mdc b/.cursor/rules/portal-rule.mdc deleted file mode 100644 index 6286754c..00000000 --- a/.cursor/rules/portal-rule.mdc +++ /dev/null @@ -1,10 +0,0 @@ ---- -alwaysApply: true ---- -## Codebase Coding Standard - -1. Have types and validation in the shared domain layer. -2. Keep business logic out of the frontend; use services and APIs instead. -3. Reuse existing types and functions; extend them when additional behavior is needed. -4. Read and understand the structures and workflows documented in docs. Start from docs/README.md -5. Follow structures and our codebase rules inside docs/development directory \ No newline at end of file diff --git a/.cursor/worktrees.json b/.cursor/worktrees.json deleted file mode 100644 index bf9e730d..00000000 --- a/.cursor/worktrees.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "setup-worktree": ["pnpm install"] -} diff --git a/.gitignore b/.gitignore index 30ab9c2d..cdc6e82a 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ build/ # IDE and editor files .vscode/ .idea/ +.cursor/ *.swp *.swo *~ diff --git a/apps/bff/sim-api-test-log.csv b/apps/bff/sim-api-test-log.csv deleted file mode 100644 index 2fa55d66..00000000 --- a/apps/bff/sim-api-test-log.csv +++ /dev/null @@ -1 +0,0 @@ -Timestamp,API Endpoint,API Method,Phone Number,SIM Identifier,Request Payload,Response Status,Error,Additional Info