# Foundek Workspace — Plan de Refactorización Integral

> **Fecha**: 2026-03-10  
> **Objetivo**: Resolver deuda técnica, consolidar buenas prácticas (DRY, SOLID, TDD), simplificar y documentar.  
> **Proyectos afectados**: Backend (Laravel 12), Panel (Angular 19), App (Ionic/Angular 19 + Capacitor 7)

---

## Resumen ejecutivo

| Proyecto | Archivos | Problemas críticos | `: any` | `console.*` | Tests reales |
|----------|----------|---------------------|---------|-------------|--------------|
| **Backend** | ~90 PHP | 2 god controllers (1600+ líneas), 0 FormRequests, 0 Policies | N/A | ~40 | 32 (parcial) |
| **Panel** | ~70 TS | 0 lazy loading, 8 fat components, 0 modelos tipados | 284 | 256 | 1 |
| **App** | ~120 TS | 5 god components, 3 mecanismos storage, 501 any | 501 | 458 | ~66 boilerplate |

---

## FASE 1 — BACKEND (Laravel)

### 1.1 Infraestructura de validación: FormRequests
**Problema**: 58 instancias de validación inline en controllers.  
**Acción**: Crear `app/Http/Requests/` con FormRequests por acción.

| FormRequest | Controller(s) afectados |
|-------------|------------------------|
| `StartConversationRequest` | App\ChatController |
| `StoreMessageRequest` | App\ChatController |
| `StoreArticleRequest` | App\ArticleController, Api\ArticleController |
| `UpdateArticleRequest` | App\ArticleController, Api\ArticleController |
| `LoginRequest` | App\AuthController, Api\AuthController |
| `RegisterRequest` | App\AuthController |
| `SocialLoginRequest` | App\AuthController |
| `StoreCategoryRequest` | Api\CategoryController |
| `UpdateCategoryRequest` | Api\CategoryController |
| `StoreSubcategoryRequest` | Api\SubcategoryController |
| `UpdateSubcategoryRequest` | Api\SubcategoryController |
| `StoreAttributeRequest` | Api\AttributeController |
| `UpdateAttributeRequest` | Api\AttributeController |
| `StoreUserRequest` | Api\AdminUserController |
| `UpdateUserRequest` | Api\AdminUserController |
| `UpdateProfileRequest` | Api\ProfileController, App\ProfileController |
| `CreatePaymentIntentRequest` | App\PaymentController |
| `StorePaymentAmountRequest` | Api\PaymentAmountController |
| `UpdatePaymentAmountRequest` | Api\PaymentAmountController |

### 1.2 Extracción de servicios desde God Controllers

#### ChatController (1633 → ~200 líneas)
| Servicio nuevo | Métodos a extraer |
|----------------|-------------------|
| `ConversationService` | `startConversation()`, `inbox()`, `showConversation()`, `getUnreadMessagesCount()` |
| `ChatMessageService` | `store()`, `markAsRead()`, `uploadImage()` |
| `ExchangeService` (backend) | `generateExchangeCode()`, `validateExchangeCode()`, `closeExchange()`, `rejectExchange()` |
| `ChatNotificationService` | Toda la lógica de push/email dentro de store(), startConversation() |

#### PaymentController (1472 → ~200 líneas)
| Servicio nuevo | Métodos a extraer |
|----------------|-------------------|
| `PaymentIntentService` | `createPaymentIntent()`, `confirmPayment()`, `capturePayment()` |
| `CommissionService` | `calculateCommission()`, `processCommissionPayout()` |
| `InvoiceService` (expandir) | `generateInvoice()`, `downloadInvoice()` |

#### ArticleController App (833 → ~200 líneas)
| Servicio nuevo | Métodos a extraer |
|----------------|-------------------|
| `ArticleService` (nuevo) | `createArticle()`, `updateArticle()`, `deleteArticle()` |
| `ArticleImageService` | `uploadImages()`, `deleteImage()`, `processImages()` |
| `ArticleSearchService` | `search()`, `filterByLocation()`, `filterByCategory()` |

### 1.3 Trait → Service: PushNotificationsManagement
**Problema**: Trait de 712 líneas usado como servicio.  
**Acción**: Convertir a `PushNotificationService` inyectable. Mantener trait como wrapper delegante por compatibilidad temporal.

### 1.4 Centralización de lógica duplicada

| Duplicación | Ubicaciones | Acción |
|-------------|-------------|--------|
| `generateTokenApi()` | User model, App\AuthController, Api\AdminUserController | Dejar solo en User model |
| Patrón locale-switch | ~30 ocurrencias en controllers/models/webhook | Crear helper `withLocale($lang, Closure)` |
| Geo-filtering raw queries | App\ArticleController, Api\ArticleController | Scope `scopeNearLocation()` en Article model |
| `checkAdminRole()` | Múltiples Api controllers | Middleware `EnsureAdmin` + añadir a grupo de rutas |

### 1.5 Autorización: Policies
**Acción**: Crear policies para: `Article`, `Conversation`, `User`, `Category`, `Subcategory`, `Attribute`.  
Registrar en `AuthServiceProvider` y usar `$this->authorize()` en controllers.

### 1.6 Modelo Conversation: extraer lógica de negocio
**Problema**: 491 líneas con envío de emails, push notifications, callbacks `booted()` complejos.  
**Acción**: Mover lógica de side-effects a `ConversationObserver` o al servicio correspondiente.

### 1.7 Limpieza de rutas 
- [ ] Eliminar rutas debug/test sin autenticar (`/test-notification`, `/debug/push/{userId}`)
- [ ] Unificar referencia cruzada Api↔App controllers en routes/app.php
- [ ] Agrupar rutas de chat bajo prefijo `/conversations`

### 1.8 Middleware
- [ ] Fusionar `SetLocale` y `SetLocaleFromUserOrRequest` en uno solo
- [ ] Refactorizar `TranslateApiErrors` para usar sistema `__()` en vez de strings hardcoded
- [ ] Crear middleware `EnsureAdmin` para proteger rutas admin a nivel de grupo

### 1.9 Respuestas API consistentes
- [ ] Crear trait `ApiResponse` con métodos `success()`, `error()`, `paginated()`
- [ ] Estandarizar: siempre `{success, data, message}` o `{success, error, message}`

### 1.10 Tests
- [ ] Tests para cada nuevo Service extraído
- [ ] Tests para FormRequests (validación)
- [ ] Tests para Policies (autorización)
- [ ] Tests para middleware `EnsureAdmin`

---

## FASE 2 — PANEL (Angular 19)

### 2.1 Lazy Loading (prioridad máxima)
**Problema**: 0 rutas lazy-loaded; todo importado eagerly.  
**Acción**: Convertir cada page a `loadComponent` standalone con lazy loading.

### 2.2 Interfaces/Modelos
**Problema**: 284 usos de `: any`, 0 interfaces de dominio.  
**Acción**: Crear directorio `src/app/models/` con:

| Interface | Propiedades clave |
|-----------|-------------------|
| `User` | id, name, email, role, language, stripe_connect_status, avatar_url |
| `Article` | id, title, description, type, status, category, subcategory, images, location |
| `Category` | id, translations, icon_url, order, attributes |
| `Subcategory` | id, category_id, translations, attributes |
| `Attribute` | id, type, options, translations |
| `Conversation` | id, article, initiator, receiver, last_message, unread_count |
| `Notification` | id, type, title, body, read_at, data |
| `Payment` | id, amount, status, article, user, created_at |
| `Complaint` | id, article_id, reporter, status, reason |
| `Cancellation` | id, article_id, status, reason, verdict |
| `PaymentAmount` | id, amount, subcategory_ids |
| `PaginatedResponse<T>` | data: T[], current_page, last_page, total, per_page |
| `ApiResponse<T>` | success, data?: T, message?: string |

### 2.3 Extracción de patrón CRUD base
**Problema**: 8 pages repiten el mismo boilerplate (paginación, búsqueda, sort, dialog).  
**Acción**: Crear clase abstracta `BaseCrudPage<T>` o composable con:
- `currentPage`, `totalPages`, `perPage`, `totalItems` signals
- `searchTerm$` Subject con debounce
- `sortField`, `sortOrder`
- `isFetching` signal
- `dialogVisible`, `dialogMode`, `dialogData`
- Métodos: `loadData()`, `onSearch()`, `onPageChange()`, `onSort()`, `openDialog()`, `closeDialog()`

### 2.4 Eliminar console.log (256 → 0)
**Acción**: Búsqueda global y eliminación. Sin servicio de logging (es panel admin, no hace falta).

### 2.5 Guards funcionales
**Problema**: `AuthGuard` y `PublicGuard` usan API class-based deprecada.  
**Acción**: Migrar a `CanActivateFn`.

### 2.6 Eliminar doble Authorization header
**Problema**: `HttpService.getHeaders()` y `authInterceptor` ambos ponen Bearer token.  
**Acción**: Quitar token de `HttpService.getHeaders()`, dejar solo en interceptor.

### 2.7 Descomponer componentes fat
| Componente | Líneas | Acción |
|------------|--------|--------|
| `article-page` | 1071 | Separar: tabla, filtros, mapa, formulario en sub-componentes |
| `category-page` | 813 | Separar: tabla, asignación de atributos |
| `subcategory-page` | 774 | Separar: tabla, asignación de atributos |
| `user-form` | 769 | Separar: mapa, cropper, datos de formulario |
| `users-page` | 650 | Separar: tabla, filtros |
| `complaints-page` | 642 | Separar: tabla, detalle |

### 2.8 Limpieza de código muerto
- [ ] Quitar `SearchBarComponent` importado pero no usado en `PaymentAmountsPage`
- [ ] Quitar `console.log("test")` del interceptor
- [ ] Quitar `effect()` debug en article-page
- [ ] Quitar `BehaviorSubject` no usado en `UsersService`
- [ ] Quitar guards redundantes en rutas hijas

### 2.9 Lifecycle cleanup
- [ ] Añadir `DestroyRef` + `takeUntilDestroyed()` a todos los componentes con subscripciones

### 2.10 Tests
- [ ] Tests para guards funcionales
- [ ] Tests para interceptor
- [ ] Tests para servicios API (mock HttpClient)
- [ ] Tests para `BaseCrudPage` si se crea

---

## FASE 3 — APP (Ionic/Angular 19 + Capacitor 7)

### 3.1 Unificar almacenamiento
**Problema**: 3 mecanismos: `localStorage` (333 usos), `@capacitor/preferences`, `@ionic/storage`.  
**Acción**: 
1. Crear `StorageService` unificado sobre `@capacitor/preferences` (funciona en web y nativo)
2. Reemplazar todos los `localStorage` directos
3. Eliminar `@ionic/storage-angular` si no aporta valor extra

### 3.2 Crear Interfaces/Modelos
**Problema**: 501 usos de `: any`.  
**Acción**: Crear `src/app/models/` con interfaces para User, Article, Category, Subcategory, Attribute, Message, Conversation, Notification, Payment, Gratification, etc.

### 3.3 Extraer lógica de AppComponent (858 → ~100 líneas)
| Servicio nuevo | Responsabilidades |
|----------------|-------------------|
| `AppInitService` | statusBar, safeArea, keyboard, screenOrientation |
| `PushNotificationService` (expandir) | FCM, local notifications, badge, channels |
| `RealtimeService` (expandir pusher) | WebSocket channels, reconexión |

### 3.4 Unificar create-post / edit-post
**Problema**: ~1200 líneas duplicadas entre ambos.  
**Acción**: Crear `PostFormService` compartido con:
- Carga de categorías/subcategorías/atributos
- Gestión de fotos (captura, recorte, base64)
- Validación de formulario
- Preparación de payload para API

### 3.5 Eliminar console.* (458 → 0)
**Acción**: Búsqueda global y eliminación.

### 3.6 Subscription cleanup
**Problema**: 158 `.subscribe()` con solo 15 `takeUntil`.  
**Acción**: Añadir `DestroyRef` + `takeUntilDestroyed()` a todas las suscripciones en componentes.

### 3.7 Guards funcionales + interceptor funcional
- [ ] Migrar 4 guards a `CanActivateFn`
- [ ] Migrar interceptor a `HttpInterceptorFn`
- [ ] Mover lógica de Bearer token del HttpService al interceptor

### 3.8 Eliminar plugins Cordova
- [ ] Reemplazar `@awesome-cordova-plugins/deeplinks` con Capacitor App Listener
- [ ] Reemplazar `cordova-plugin-fullscreen` con Capacitor StatusBar

### 3.9 Estrategia de preloading
**Problema**: `PreloadAllModules` anula lazy loading.  
**Acción**: Cambiar a `NoPreloading` o custom strategy que precargue solo rutas principales.

### 3.10 ComponentsModule → Standalone
**Problema**: 33 componentes en un solo módulo cargado siempre.  
**Acción**: Migrar componentes a standalone y hacer imports directos donde se necesiten.

### 3.11 Strings hardcoded → i18n
- [ ] Mover Spanish hardcoded strings a archivos de traducción
- [ ] Eliminar `.pipe(map(response => response))` redundante del loader

### 3.12 Fat components
| Componente | Líneas | Acción |
|------------|--------|--------|
| `messages.page` | 1248 | Extraer lógica de exchange, gratificación, media upload a servicios |
| `listing.page` | 820 | Extraer filtrado, paginación a servicio dedicado |
| `chat.page` | 628 | Extraer gestión de Pusher channels a servicio |
| `map.component` | 900 | Extraer lógica de clustering, geofencing a servicios |

### 3.13 Tests
- [ ] Crear tests para `StorageService` unificado
- [ ] Crear tests para `PostFormService`
- [ ] Crear tests para `AppInitService`
- [ ] Actualizar specs boilerplate existentes para que sean útiles

---

## Orden de ejecución

```
FASE 1 (Backend)
  1.1 FormRequests                    ← Primero: no rompe nada, mejora estructura
  1.2 Extracción de servicios         ← Segundo: reduce controllers
  1.3 PushNotifications Trait→Service ← Tercero
  1.4 Centralización de duplicados    ← Cuarto
  1.5 Policies                        ← Quinto
  1.6 Conversation model cleanup      ← Sexto
  1.7 Limpieza de rutas               ← Séptimo
  1.8 Middleware                       ← Octavo
  1.9 ApiResponse trait                ← Noveno
  1.10 Tests                          ← Último (para cubrir todo lo nuevo)

FASE 2 (Panel)
  2.1 Lazy loading                    ← Primero: mejora rendimiento inmediata
  2.2 Interfaces/modelos              ← Segundo: base para tipar todo
  2.3 BaseCrudPage                    ← Tercero: reduce boilerplate
  2.4 Eliminar console.log            ← Cuarto: limpieza rápida
  2.5 Guards funcionales              ← Quinto
  2.6 Fix doble Authorization         ← Sexto
  2.7 Descomponer fat components      ← Séptimo
  2.8 Código muerto                   ← Octavo
  2.9 Lifecycle cleanup               ← Noveno
  2.10 Tests                          ← Último

FASE 3 (App)
  3.1 Unificar storage                ← Primero: impacto transversal
  3.2 Interfaces/modelos              ← Segundo
  3.3 AppComponent split              ← Tercero
  3.4 create-post/edit-post merge     ← Cuarto
  3.5 Eliminar console.*              ← Quinto
  3.6 Subscription cleanup            ← Sexto
  3.7 Guards/interceptor funcional    ← Séptimo
  3.8 Eliminar Cordova plugins        ← Octavo
  3.9 Preloading strategy             ← Noveno
  3.10 ComponentsModule → standalone  ← Décimo
  3.11 Strings → i18n                 ← Undécimo
  3.12 Fat components                 ← Duodécimo
  3.13 Tests                          ← Último
```

---

## Criterios de aceptación por cambio

- [ ] Tests existentes siguen pasando (32 backend + build panel + build app)
- [ ] Cada servicio extraído tiene tests propios
- [ ] 0 nuevas columnas de base de datos
- [ ] 0 cambios en contratos de API (mismos endpoints, mismas respuestas)
- [ ] Código compila sin errores TypeScript
- [ ] Cada fase se valida antes de pasar a la siguiente

---

## Progreso

| Fase | Subtarea | Estado |
|------|----------|--------|
| 1.1 | FormRequests | ⬜ Pendiente |
| 1.2 | Extracción de servicios | ⬜ Pendiente |
| 1.3 | Trait → Service | ⬜ Pendiente |
| 1.4 | Centralización duplicados | ⬜ Pendiente |
| 1.5 | Policies | ⬜ Pendiente |
| 1.6 | Conversation cleanup | ⬜ Pendiente |
| 1.7 | Rutas | ⬜ Pendiente |
| 1.8 | Middleware | ⬜ Pendiente |
| 1.9 | ApiResponse | ⬜ Pendiente |
| 1.10 | Tests | ⬜ Pendiente |
| 2.1 | Lazy loading | ⬜ Pendiente |
| 2.2 | Interfaces | ⬜ Pendiente |
| 2.3 | BaseCrudPage | ⬜ Pendiente |
| 2.4 | console.log | ⬜ Pendiente |
| 2.5 | Guards funcionales | ⬜ Pendiente |
| 2.6 | Double Auth header | ⬜ Pendiente |
| 2.7 | Fat components | ⬜ Pendiente |
| 2.8 | Código muerto | ⬜ Pendiente |
| 2.9 | Lifecycle | ⬜ Pendiente |
| 2.10 | Tests | ⬜ Pendiente |
| 3.1 | Storage | ⬜ Pendiente |
| 3.2 | Interfaces | ⬜ Pendiente |
| 3.3 | AppComponent | ⬜ Pendiente |
| 3.4 | PostForm merge | ⬜ Pendiente |
| 3.5 | console.* | ⬜ Pendiente |
| 3.6 | Subscriptions | ⬜ Pendiente |
| 3.7 | Guards/interceptor | ⬜ Pendiente |
| 3.8 | Cordova → Capacitor | ⬜ Pendiente |
| 3.9 | Preloading | ⬜ Pendiente |
| 3.10 | Standalone components | ⬜ Pendiente |
| 3.11 | i18n hardcoded | ⬜ Pendiente |
| 3.12 | Fat components | ⬜ Pendiente |
| 3.13 | Tests | ⬜ Pendiente |
