Aplicación web desarrollada en Angular 19 para la visualización y gestión de documentos de facturación electrónica (PDF, XML, CDR).
EFACT es una aplicación frontend moderna construida con Angular 19 que permite a los usuarios autenticarse y visualizar documentos de facturación electrónica mediante un sistema de búsqueda por número de ticket. La aplicación proporciona una experiencia de usuario fluida para visualizar PDFs, archivos XML firmados y archivos CDR (Constancia de Recepción) con syntax highlighting profesional.
Nombre: Ethan Matías Aliaga Aguirre
Rol: Full-Stack / Front-end Developer Intern
Experiencia: Desarrollo de aplicaciones web, bases de datos y arquitecturas orientadas a buenas prácticas.
Especialidades:
- Desarrollo web con Angular, Vue, Express.js.
- Desarrollo mobile con Flutter y Jetpack Compose.
- Desarrollo backend con ASP.NET Core, Springboot y Node.js.
- Diseño e implementación de arquitecturas modulares (DDD, MVC, Clean Architecture).
- Bases de datos SQL y NoSQL (MySQL, PostgreSQL, SQL Server, MongoDB).
- Integración de autenticación segura, envío de correos, geolocalización e interfaces modernas con Tailwind y Bootstrap.
- Optimización de experiencia de usuario y desarrollo de sistemas escalables.
Contacto:
- Email: ethan.aliaga@gmail.com
- LinkedIn: https://www.linkedin.com/in/matias-aliaga-aguirre-5332b020a
- GitHub: https://github.com/MatFragg
Resumen breve:
Estudiante de Ingeniería de Software con sólida experiencia en desarrollo web full-stack, diseño de bases de datos y construcción de sistemas robustos orientados a servicios. He desarrollado plataformas completas como ElectroLink, Meeti, InmoLinking y Mentorly, aplicando principios de Domain-Driven Design, buenas prácticas de arquitectura y un enfoque en optimización de flujo, rendimiento y experiencia de usuario. Me especializo en crear soluciones escalables, limpias y organizadas, aplicando patrones como DI, MVC y servicios reutilizables.
La prueba técnica requería implementar un sistema frontend para:
- Autenticación: Login mediante OAuth 2.0 (grant type password) con almacenamiento seguro de tokens
- Búsqueda de Documentos: Interfaz para buscar documentos mediante número de ticket
- Visualización Multi-formato:
- PDF: Visualización en iframe
- XML: Código fuente con syntax highlighting y acciones (copiar/descargar)
- CDR: Código fuente con syntax highlighting y acciones (copiar/descargar)
- UX/UI Profesional: Interfaz alineada con diseño Figma proporcionado
- Routing Avanzado: URLs compartibles con ticket ID incluido
- Manejo de Errores: Página 404 personalizada y manejo global de errores HTTP
Objetivos Específicos:
- Clean Architecture y separación de responsabilidades
- Componentes reutilizables (Smart/Presentational pattern)
- Gestión de estado reactiva con Signals
- Seguridad mediante Guards e Interceptors
- Código mantenible y escalable
| Tecnología | Versión | Propósito |
|---|---|---|
| Angular | 19.2.0 | Framework principal SPA |
| TypeScript | 5.7.2 | Lenguaje de programación tipado |
| RxJS | 7.8.0 | Programación reactiva y manejo de async |
| Zone.js | 0.15.0 | Change detection de Angular |
| Tecnología | Versión | Propósito |
|---|---|---|
| Angular Material | 19.2.19 | Componentes UI (buttons, forms, icons, tabs) |
| Angular CDK | 19.2.19 | Utilidades y comportamientos UI |
| highlight.js | 11.11.1 | Motor de syntax highlighting |
| ngx-highlightjs | 14.0.1 | Wrapper Angular para highlight.js |
| Tecnología | Versión | Propósito |
|---|---|---|
| Angular CLI | 19.2.7 | Build tool y desarrollo |
| Karma + Jasmine | 6.4.0 / 5.6.0 | Testing framework |
| TypeScript Compiler | 5.7.2 | Compilación y type checking |
- Standalone Components: Eliminación de NgModules para simplicidad
- Signals: Gestión de estado reactiva sin RxJS para datos síncronos
- Functional Guards: Guards modernos usando funciones en lugar de clases
- HTTP Interceptors Funcionales: Interceptors modernos basados en funciones
- Control Flow Syntax:
@if,@for,@switchen templates - Lazy Loading: Carga diferida de módulos mediante routes
La aplicación sigue una arquitectura feature-based con separación clara de responsabilidades:
src/app/
├── core/ # Servicios singleton, guards, interceptors
│ ├── guards/ # Protección de rutas (authGuard)
│ ├── interceptors/ # HTTP interceptors (auth, error)
│ ├── services/ # Servicios globales (AuthService, TokenService)
│ └── models/ # Interfaces y tipos compartidos
│
├── features/ # Módulos de funcionalidades
│ ├── auth/ # Feature de autenticación
│ │ ├── components/ # Login (Smart Component)
│ │ │ └── ui/ # LoginForm (Presentational)
│ │ └── auth.routes.ts
│ │
│ └── documents/ # Feature de documentos
│ ├── components/
│ │ ├── documents-landing/ # Smart Component
│ │ └── ui/ # Presentational Components
│ │ ├── pdf-viewer/
│ │ ├── xml-viewer/
│ │ ├── cdr-viewer/
│ │ └── code-viewer/ # Reutilizable para XML/CDR
│ ├── services/ # DocumentService
│ ├── models/ # DocumentData interfaces
│ └── documents.routes.ts
│
└── shared/ # Componentes, pipes, utilidades reutilizables
├── components/ # Header, NotFound, Spinners, ErrorMessage
└── pipes/ # SafeUrlPipe (para iframes)
Smart Components (Containers):
- Ubicación:
features/{feature}/components/ - Responsabilidades:
- Inyectar servicios
- Gestionar estado con Signals
- Manejar lógica de negocio
- Orquestar llamadas HTTP
- Ejemplo:
DocumentsLandingComponent
Presentational Components (UI):
- Ubicación:
features/{feature}/components/ui/ - Responsabilidades:
- Recibir datos vía
@Input() - Emitir eventos vía
@Output() - Sin inyección de servicios
- 100% reutilizables
- Recibir datos vía
- Ejemplo:
CodeViewerComponent,PdfViewerComponent
// Todos los servicios son singleton
@Injectable({ providedIn: 'root' })
export class AuthService { }
// Inyección mediante inject()
private authService = inject(AuthService);Estrategia híbrida:
- Signals para estado síncrono local de componentes
- RxJS Observables para operaciones asíncronas (HTTP)
// Signals para estado
documentData = signal<DocumentData | null>(null);
isLoading = signal(false);
// RxJS para HTTP
this.documentService.getDocuments(ticket)
.subscribe(data => this.documentData.set(data));Usuario → LoginComponent → AuthService → HTTP (OAuth) → TokenService → Signal Update → Route Guard → Navegación
URL Params → DocumentsLanding → DocumentService → Auth Interceptor → API → Signal Update → Presentational Components
- Formulário reactivo con validaciones
- OAuth 2.0 Password Grant
- Almacenamiento seguro de tokens (sessionStorage)
- Feedback visual (loading, errors)
- Redirección post-login con
returnUrl
- AuthGuard: Bloquea acceso a
/documentossin autenticación - Redirección automática a
/auth/login - Preservación de URL destino
- AuthInterceptor: Agrega token Bearer automáticamente
- ErrorInterceptor: Manejo global de errores (401, 403, 404, 500)
- Notificaciones de usuario automáticas
- Interfaz tipo "Google Search"
- Campo de ticket con validación
- Resultados inline sin navegación
- URL dinámica:
/documentos/{ticketId}
PDF Viewer:
- Renderizado en iframe seguro
- SafeUrlPipe para sanitización
- Botón de descarga
XML Viewer:
- Syntax highlighting (GitHub theme)
- Líneas numeradas
- Botón copiar al portapapeles
- Botón descargar archivo
- Scroll horizontal/vertical
CDR Viewer:
- Mismas características que XML
- Reutiliza CodeViewerComponent
- Formato específico para CDR
Características:
- Toolbar con nombre de archivo y contador de líneas
- Botones de acción (copiar, descargar)
- Syntax highlighting con highlight.js
- Tema GitHub (light) para usuarios de negocio
- Fallback para copiar (navigator.clipboard + execCommand)
- Fuente monospace (Fira Code)
- Scrollbar personalizado
- Navegación global con logo EFACT
- 7 ítems del menú (Crear, Enviados, Recibidos, etc.)
- Sección de ayuda
- Avatar de usuario
- Sticky positioning
- Diseño alineado con Figma
- Diseño profesional con icono de error
- Código 404 destacado
- Mensaje en español
- Botones de acción:
- "Ir a Inicio" →
/documentos - "Volver" →
window.history.back()
- "Ir a Inicio" →
- Incluye header para consistencia
- Rutas definidas:
/auth/login- Login/documentos- Landing sin ticket/documentos/:ticket- Con resultados/404- Página de error**- Wildcard → 404
- URLs compartibles
- Integración con historial del navegador
- Colores:
- Primario (Rojo):
#dc2626(EFACT brand) - Éxito (Verde):
#16a34a(botón XML) - Info (Azul):
#2563eb(botón CDR, iconos) - Fondo:
#f5f7fa(limpio, profesional)
- Primario (Rojo):
- Tipografía: Inter (Google Fonts) con pesos 400-700
- Espaciado: Sistema consistente de 8px baseline
- Border Radius: 8-12px para modernidad
- Sombras: Sutiles (0 2px 8px rgba(0,0,0,0.08))
- Loading spinners durante fetch
- Error messages con Material Snackbar
- Estados vacíos (sin documentos)
- Validaciones de formulario en tiempo real
- Tooltips en botones de acción
- Breakpoint móvil: 768px
- Header adaptativo (menú colapsable en móvil)
- Cards flexibles
- Botones apilados en mobile
- Cada componente tiene una única responsabilidad
- Servicios enfocados (AuthService, TokenService, DocumentService)
- Separación de concerns (presentación vs lógica)
CodeViewerComponentreutilizable para XML y CDRSafeUrlPipecompartido- Componentes shared (Header, ErrorMessage, LoadingSpinner)
- Archivos:
kebab-case.type.ts - Clases:
PascalCase - Variables/funciones:
camelCase - Constantes:
UPPER_SNAKE_CASE - Prefijos consistentes (
app-para selectores)
- Loose coupling entre componentes y servicios
- Testability mejorada
- Singleton services automáticos
- RxJS Observables para async operations
- Subscripciones y unsubscripciones apropiadas
- Uso de
pipe()para transformaciones
- Interceptors para estrategias de manejo HTTP
- Guards para estrategias de protección de rutas
- Services como fachadas de la lógica de negocio
- Ocultación de complejidad HTTP
- Almacenamiento en sessionStorage (no localStorage)
- Expiración de tokens validada
- Limpieza en logout
- SafeUrlPipe para sanitización de URLs
- Angular's built-in sanitization
- No uso de
innerHTMLsin sanitizar
- Proxy configuration en
proxy.conf.json - Endpoint
/apiproxiado a backend
- Try/catch en operaciones críticas
- Fallbacks para APIs no soportadas (clipboard)
- Manejo global de errores HTTP
- Features cargados bajo demanda
loadChildren()en routes
- Signals para actualizaciones granulares
- OnPush strategy implícita con standalone
- Import específicos de Material
- Highlight.js cargado solo cuando necesario
- Interfaces para todos los modelos
- Strict TypeScript configuration
- Typed RxJS operators
- Feature-based organization
- Standalone components
- Clear boundaries entre features
- JSDoc en servicios críticos
- Comentarios explicativos en lógica compleja
- README detallado (este documento)
- Archivos
*.spec.tsjunto a componentes - Karma + Jasmine configurado
- Preparado para unit tests
- Dependency Injection facilita mocking
- Smart/Presentational split mejora testing
- Servicios desacoplados
Node.js >= 18.x
npm >= 9.x# Clonar repositorio
git clone [REPO_URL]
# Instalar dependencias
cd efac-angular-frontend
npm installArchivo: src/environments/environment.development.ts
export const environment = {
production: false,
apiUrl: '/api', // Proxied a backend
oauth: {
tokenEndpoint: '/oauth/token',
clientCredentials: 'Y2xpZW50OnNlY3JldA==', // Base64
}
};Proxy Configuration: proxy.conf.json
{
"/api": {
"target": "https://odin-dev.efact.pe/api-efact-ose",
"secure": false,
"changeOrigin": true,
"pathRewrite": {
"^/api": ""
}
}
}# Desarrollo
npm start
# O alternativamente
ng serve
# Navega a: http://localhost:4200/npm run build
# Artifacts en: dist/| Ruta | Componente | Descripción | Guard |
|---|---|---|---|
/auth/login |
LoginComponent | Página de login | - |
/documentos |
DocumentsLandingComponent | Búsqueda de tickets | authGuard |
/documentos/:ticket |
DocumentsLandingComponent | Vista con documentos | authGuard |
/404 |
NotFoundComponent | Página de error | - |
** |
NotFoundComponent | Wildcard catch-all | - |
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic [clientCredentials]
grant_type=password&username={user}&password={pass}GET /documentoSunat/{ticket}
Authorization: Bearer {token}GET /documentoSunat/{ticket}/{type}
Authorization: Bearer {token}- Implementar tests unitarios completos
- Agregar refresh token automático
- Dark mode toggle
- Búsqueda con filtros avanzados
- Implementar páginas del header (
/crear,/enviados, etc.) - Historial de búsquedas
- Pretty-print XML con formateo
- Búsqueda dentro del código (Ctrl+F)
- PWA con offline support
- i18n (internacionalización)
- Analytics integration
- E2E tests con Cypress
# Generar componente
ng generate component features/documents/components/ui/new-component
# Generar servicio
ng generate service core/services/new-service
# Ejecutar tests
ng test
# Build con análisis de bundle
ng build --stats-json
npx webpack-bundle-analyzer dist/stats.json
# Linting
ng lintEste proyecto es parte de una evaluación técnica para EFACT.
Desarrollador: Ethan Matias Aliaga Aguirre
Email: ethan.aliaga@gmail.com
LinkedIn: https://www.linkedin.com/in/matias-aliaga-aguirre-5332b020a/
GitHub: https://github.com/MatFragg
Desarrollado con ❤️ usando Angular 19