# FRONTEND_INVENTORY.yml - ERP Core # Inventario canonico de objetos frontend # Ubicacion Canonica: orchestration/inventarios/ # Ultima actualizacion: 2025-12-05 version: "2.0" project: erp-core updated_at: "2026-01-06" updated_by: ORQUESTADOR-Claude-Opus # ============================================================================= # CONFIGURACION DEL STACK # ============================================================================= stack: framework: React 18.x build_tool: Vite 5.x language: TypeScript 5.3+ state_management: Zustand styling: Tailwind CSS 4.x forms: React Hook Form + Zod routing: React Router 6.x http_client: Axios # ============================================================================= # RESUMEN # ============================================================================= summary: total_features: 24 total_pages: 58 total_components: 185 total_stores: 24 total_hooks: 35 total_api_clients: 24 implemented: 6 # auth, users, tenants, catalogs, settings (partial) documented: 15 # MGN-001 a MGN-009 # Sprint 1-3 Implementation (2026-01-06) implementation: pages_created: 25 stores_created: 4 routes_added: 23 features_implemented: 2 # catalogs, settings build_status: "Pass (4.07s)" by_sprint: sprint_1: feature: catalogs pages: 6 # Countries(3) + States(2) + index files: - features/catalogs/types/catalog.types.ts - features/catalogs/api/catalogs.api.ts - features/catalogs/hooks/useCountries.ts - features/catalogs/hooks/useCurrencies.ts - features/catalogs/hooks/useUom.ts - features/catalogs/hooks/useCategories.ts - features/catalogs/components/CountrySelect.tsx - features/catalogs/components/CurrencySelect.tsx - pages/catalogs/countries/CountriesPage.tsx - pages/catalogs/countries/CountryFormPage.tsx - pages/catalogs/countries/CountryDetailPage.tsx - pages/catalogs/states/StatesPage.tsx - pages/catalogs/states/StateFormPage.tsx sprint_2: feature: catalogs (complete) pages: 17 # Currencies(4) + UoM(5) + Categories(5) + components(3) files: - pages/catalogs/currencies/CurrenciesPage.tsx - pages/catalogs/currencies/CurrencyFormPage.tsx - pages/catalogs/currencies/CurrencyDetailPage.tsx - pages/catalogs/currencies/CurrencyRatesPage.tsx - pages/catalogs/uom/UomPage.tsx - pages/catalogs/uom/UomCategoriesPage.tsx - pages/catalogs/uom/UomFormPage.tsx - pages/catalogs/uom/UomConversionPage.tsx - pages/catalogs/categories/CategoriesPage.tsx - pages/catalogs/categories/CategoryFormPage.tsx - pages/catalogs/categories/CategoryDetailPage.tsx - features/catalogs/components/CategoryTree.tsx - features/catalogs/components/CategoryTreeSelect.tsx routes_added: 20 sprint_3: features: [catalogs-stores, settings] stores: 4 pages: 2 # SystemSettings, TenantSettings files: - features/catalogs/stores/countries.store.ts - features/catalogs/stores/currencies.store.ts - features/catalogs/stores/uom.store.ts - features/catalogs/stores/categories.store.ts - features/settings/types/settings.types.ts - features/settings/api/settings.api.ts - features/settings/hooks/useSystemSettings.ts - features/settings/hooks/useTenantSettings.ts - features/settings/hooks/useUserPreferences.ts - pages/settings/SystemSettingsPage.tsx - pages/settings/TenantSettingsPage.tsx - pages/settings/components/GeneralSettingsForm.tsx - pages/settings/components/FormatSettingsForm.tsx - pages/settings/components/FeatureTogglesForm.tsx - pages/settings/components/BrandingSettingsForm.tsx - pages/settings/components/ModulesSettingsForm.tsx - pages/settings/components/UsageStatsCard.tsx routes_added: 3 # ============================================================================= # FEATURES (MODULOS FRONTEND) # ============================================================================= features: # --------------------------------------------------------------------------- # MGN-001: AUTH # --------------------------------------------------------------------------- - id: MGN-001 name: auth path: apps/frontend/src/features/auth/ status: documented rf: [RF-AUTH-001, RF-AUTH-002, RF-AUTH-003, RF-AUTH-005, RF-AUTH-006] pages: - name: LoginPage file: pages/LoginPage.tsx route: /login rf: RF-AUTH-001 public: true components: [LoginForm, SocialLoginButtons] - name: ForgotPasswordPage file: pages/ForgotPasswordPage.tsx route: /forgot-password rf: RF-AUTH-003 public: true components: [ForgotPasswordForm] - name: ResetPasswordPage file: pages/ResetPasswordPage.tsx route: /reset-password/:token rf: RF-AUTH-003 public: true components: [ResetPasswordForm] components: - name: LoginForm file: components/LoginForm.tsx rf: RF-AUTH-001 props: [onSuccess, onError] description: Formulario de login con email/password - name: SocialLoginButtons file: components/SocialLoginButtons.tsx rf: RF-AUTH-005 props: [providers] description: Botones de login social - name: ForgotPasswordForm file: components/ForgotPasswordForm.tsx rf: RF-AUTH-003 props: [onSuccess] description: Formulario solicitud reset - name: ResetPasswordForm file: components/ResetPasswordForm.tsx rf: RF-AUTH-003 props: [token, onSuccess] description: Formulario cambio password stores: - name: authStore file: stores/authStore.ts rf: [RF-AUTH-001, RF-AUTH-002, RF-AUTH-006] state: - {name: user, type: "User | null"} - {name: isAuthenticated, type: boolean} - {name: isLoading, type: boolean} - {name: error, type: "string | null"} - {name: accessToken, type: "string | null"} actions: - {name: login, params: [credentials], rf: RF-AUTH-001} - {name: logout, params: [], rf: RF-AUTH-006} - {name: refreshToken, params: [], rf: RF-AUTH-002} - {name: getProfile, params: [], rf: RF-AUTH-001} - {name: clearError, params: []} api: - name: authApi file: api/authApi.ts methods: - {name: login, endpoint: "POST /auth/login", rf: RF-AUTH-001} - {name: logout, endpoint: "POST /auth/logout", rf: RF-AUTH-006} - {name: refresh, endpoint: "POST /auth/refresh", rf: RF-AUTH-002} - {name: forgotPassword, endpoint: "POST /auth/forgot-password", rf: RF-AUTH-003} - {name: resetPassword, endpoint: "POST /auth/reset-password", rf: RF-AUTH-003} - {name: getMe, endpoint: "GET /auth/me", rf: RF-AUTH-001} hooks: - name: useAuth file: hooks/useAuth.ts rf: RF-AUTH-001 returns: "{user, isAuthenticated, login, logout}" description: Hook principal de autenticacion - name: useRequireAuth file: hooks/useRequireAuth.ts rf: RF-AUTH-001 returns: void description: Redirige a login si no autenticado # --------------------------------------------------------------------------- # MGN-002: USERS # --------------------------------------------------------------------------- - id: MGN-002 name: users path: apps/frontend/src/features/users/ status: documented rf: [RF-USERS-001, RF-USERS-002, RF-USERS-003, RF-USERS-004, RF-USERS-005] pages: - name: UsersPage file: pages/UsersPage.tsx route: /users rf: RF-USERS-001 components: [UserTable, UserFilters, CreateUserModal] - name: UserDetailPage file: pages/UserDetailPage.tsx route: /users/:id rf: RF-USERS-001 components: [UserForm, UserRoles] - name: ProfilePage file: pages/ProfilePage.tsx route: /profile rf: RF-USERS-002 components: [ProfileForm, AvatarUpload] - name: PreferencesPage file: pages/PreferencesPage.tsx route: /settings/preferences rf: RF-USERS-003 components: [PreferencesForm] components: - name: UserTable file: components/UserTable.tsx rf: RF-USERS-001 props: [users, onEdit, onDelete, pagination] - name: UserForm file: components/UserForm.tsx rf: RF-USERS-001 props: [user, onSubmit, isLoading] - name: UserFilters file: components/UserFilters.tsx rf: RF-USERS-005 props: [filters, onFilterChange] - name: ProfileForm file: components/ProfileForm.tsx rf: RF-USERS-002 props: [profile, onSubmit] - name: AvatarUpload file: components/AvatarUpload.tsx rf: RF-USERS-002 props: [currentAvatar, onUpload] - name: PreferencesForm file: components/PreferencesForm.tsx rf: RF-USERS-003 props: [preferences, onSubmit] - name: UserSearchInput file: components/UserSearchInput.tsx rf: RF-USERS-005 props: [onSelect, placeholder] stores: - name: usersStore file: stores/usersStore.ts rf: [RF-USERS-001, RF-USERS-005] state: - {name: users, type: "User[]"} - {name: selectedUser, type: "User | null"} - {name: isLoading, type: boolean} - {name: pagination, type: PaginationState} - {name: filters, type: UserFilters} actions: - {name: fetchUsers, params: [filters]} - {name: createUser, params: [data]} - {name: updateUser, params: [id, data]} - {name: deleteUser, params: [id]} - {name: searchUsers, params: [query]} api: - name: usersApi file: api/usersApi.ts methods: - {name: getUsers, endpoint: "GET /users"} - {name: getUser, endpoint: "GET /users/:id"} - {name: createUser, endpoint: "POST /users"} - {name: updateUser, endpoint: "PATCH /users/:id"} - {name: deleteUser, endpoint: "DELETE /users/:id"} - {name: getProfile, endpoint: "GET /users/:id/profile"} - {name: updateProfile, endpoint: "PATCH /users/:id/profile"} - {name: uploadAvatar, endpoint: "POST /users/:id/avatar"} - {name: getPreferences, endpoint: "GET /users/:id/preferences"} - {name: updatePreferences, endpoint: "PATCH /users/:id/preferences"} - {name: searchUsers, endpoint: "GET /users/search"} # --------------------------------------------------------------------------- # MGN-003: ROLES # --------------------------------------------------------------------------- - id: MGN-003 name: roles path: apps/frontend/src/features/roles/ status: documented rf: [RF-RBAC-001, RF-RBAC-002, RF-RBAC-003, RF-RBAC-004] pages: - name: RolesPage file: pages/RolesPage.tsx route: /settings/roles rf: RF-RBAC-001 components: [RoleTable, CreateRoleModal] - name: RoleDetailPage file: pages/RoleDetailPage.tsx route: /settings/roles/:id rf: [RF-RBAC-001, RF-RBAC-002] components: [RoleForm, PermissionMatrix] - name: PermissionsPage file: pages/PermissionsPage.tsx route: /settings/permissions rf: RF-RBAC-002 components: [PermissionList] components: - name: RoleTable file: components/RoleTable.tsx rf: RF-RBAC-001 props: [roles, onEdit, onDelete] - name: RoleForm file: components/RoleForm.tsx rf: RF-RBAC-001 props: [role, onSubmit] - name: PermissionMatrix file: components/PermissionMatrix.tsx rf: RF-RBAC-002 props: [roleId, permissions, onToggle] description: Matriz visual modulo x accion - name: UserRoleAssignment file: components/UserRoleAssignment.tsx rf: RF-RBAC-003 props: [userId, assignedRoles, onAssign, onRemove] stores: - name: rolesStore file: stores/rolesStore.ts rf: [RF-RBAC-001, RF-RBAC-002] state: - {name: roles, type: "Role[]"} - {name: permissions, type: "Permission[]"} - {name: selectedRole, type: "Role | null"} actions: - {name: fetchRoles, params: []} - {name: createRole, params: [data]} - {name: updateRole, params: [id, data]} - {name: deleteRole, params: [id]} - {name: fetchPermissions, params: []} - {name: togglePermission, params: [roleId, permissionId]} hooks: - name: usePermission file: hooks/usePermission.ts rf: RF-RBAC-004 usage: "const canEdit = usePermission('users', 'write', 'all')" description: Verifica permiso del usuario actual - name: useHasRole file: hooks/useHasRole.ts rf: RF-RBAC-004 usage: "const isAdmin = useHasRole('admin')" description: Verifica rol del usuario actual # --------------------------------------------------------------------------- # MGN-004: TENANTS # --------------------------------------------------------------------------- - id: MGN-004 name: tenants path: apps/frontend/src/features/tenants/ status: documented rf: [RF-TENANTS-001, RF-TENANTS-002, RF-TENANTS-003, RF-TENANTS-005] pages: - name: TenantsPage file: pages/TenantsPage.tsx route: /admin/tenants rf: RF-TENANTS-001 description: Lista de tenants (super admin) - name: TenantSettingsPage file: pages/TenantSettingsPage.tsx route: /settings/organization rf: RF-TENANTS-002 components: [TenantSettingsForm] - name: OnboardingPage file: pages/OnboardingPage.tsx route: /onboarding rf: RF-TENANTS-005 components: [OnboardingWizard] - name: PlansPage file: pages/PlansPage.tsx route: /settings/subscription rf: RF-TENANTS-003 components: [PlanCard, SubscriptionStatus] components: - name: TenantSettingsForm file: components/TenantSettingsForm.tsx rf: RF-TENANTS-002 props: [settings, onSubmit] - name: OnboardingWizard file: components/OnboardingWizard.tsx rf: RF-TENANTS-005 props: [onComplete] description: Wizard multi-paso de onboarding - name: PlanCard file: components/PlanCard.tsx rf: RF-TENANTS-003 props: [plan, isCurrentPlan, onSelect] - name: SubscriptionStatus file: components/SubscriptionStatus.tsx rf: RF-TENANTS-003 props: [subscription] stores: - name: tenantStore file: stores/tenantStore.ts rf: [RF-TENANTS-001, RF-TENANTS-002, RF-TENANTS-003] state: - {name: currentTenant, type: "Tenant | null"} - {name: settings, type: "TenantSettings | null"} - {name: subscription, type: "Subscription | null"} - {name: plans, type: "Plan[]"} actions: - {name: fetchCurrentTenant, params: []} - {name: updateSettings, params: [data]} - {name: fetchPlans, params: []} - {name: changePlan, params: [planId]} - {name: cancelSubscription, params: []} hooks: - name: useTenant file: hooks/useTenant.ts rf: RF-TENANTS-001 returns: "{tenant, settings, isLoading}" description: Acceso a tenant actual - name: useSubscription file: hooks/useSubscription.ts rf: RF-TENANTS-003 returns: "{subscription, hasFeature, isWithinLimits}" description: Verificar features del plan # ============================================================================= # COMPONENTES COMPARTIDOS # ============================================================================= shared: path: apps/frontend/src/shared/ ui: - {name: Button, file: ui/Button.tsx, variants: [primary, secondary, danger]} - {name: Input, file: ui/Input.tsx, types: [text, email, password, number]} - {name: Select, file: ui/Select.tsx} - {name: Checkbox, file: ui/Checkbox.tsx} - {name: Modal, file: ui/Modal.tsx} - {name: Drawer, file: ui/Drawer.tsx} - {name: Table, file: ui/Table.tsx} - {name: Pagination, file: ui/Pagination.tsx} - {name: Badge, file: ui/Badge.tsx} - {name: Avatar, file: ui/Avatar.tsx} - {name: Dropdown, file: ui/Dropdown.tsx} - {name: Tabs, file: ui/Tabs.tsx} - {name: Card, file: ui/Card.tsx} layout: - {name: AppLayout, file: layout/AppLayout.tsx, description: Layout principal} - {name: Header, file: layout/Header.tsx} - {name: Sidebar, file: layout/Sidebar.tsx} - {name: Footer, file: layout/Footer.tsx} - {name: PageHeader, file: layout/PageHeader.tsx} feedback: - {name: Alert, file: feedback/Alert.tsx, types: [success, error, warning, info]} - {name: Toast, file: feedback/Toast.tsx} - {name: Spinner, file: feedback/Spinner.tsx} - {name: Skeleton, file: feedback/Skeleton.tsx} - {name: EmptyState, file: feedback/EmptyState.tsx} - {name: ErrorBoundary, file: feedback/ErrorBoundary.tsx} forms: - {name: FormField, file: forms/FormField.tsx} - {name: FormLabel, file: forms/FormLabel.tsx} - {name: FormError, file: forms/FormError.tsx} - {name: DatePicker, file: forms/DatePicker.tsx} - {name: FileUpload, file: forms/FileUpload.tsx} - {name: SearchInput, file: forms/SearchInput.tsx} # ============================================================================= # ROUTING # ============================================================================= routing: public_routes: - {path: /login, component: LoginPage, feature: auth} - {path: /forgot-password, component: ForgotPasswordPage, feature: auth} - {path: /reset-password/:token, component: ResetPasswordPage, feature: auth} - {path: /onboarding, component: OnboardingPage, feature: tenants} private_routes: - {path: /, component: DashboardPage, feature: dashboard} - {path: /profile, component: ProfilePage, feature: users} - {path: /users, component: UsersPage, feature: users, permission: "users:read:all"} - {path: /users/:id, component: UserDetailPage, feature: users} - {path: /settings/preferences, component: PreferencesPage, feature: users} - {path: /settings/roles, component: RolesPage, feature: roles, permission: "roles:read:all"} - {path: /settings/roles/:id, component: RoleDetailPage, feature: roles} - {path: /settings/organization, component: TenantSettingsPage, feature: tenants} - {path: /settings/subscription, component: PlansPage, feature: tenants} - {path: /admin/tenants, component: TenantsPage, feature: tenants, role: super_admin} # ============================================================================= # DEPENDENCIAS # ============================================================================= dependencies: production: - "react": "^18.2.0" - "react-dom": "^18.2.0" - "react-router-dom": "^6.20.0" - "zustand": "^4.4.0" - "axios": "^1.6.0" - "zod": "^3.22.0" - "react-hook-form": "^7.48.0" - "@hookform/resolvers": "^3.3.0" - "tailwindcss": "^3.4.0" - "@headlessui/react": "^1.7.0" - "@heroicons/react": "^2.1.0" - "date-fns": "^2.30.0" - "clsx": "^2.0.0" development: - "vite": "^5.0.0" - "typescript": "^5.3.0" - "vitest": "^1.0.0" - "@testing-library/react": "^14.1.0" - "@testing-library/jest-dom": "^6.1.0" - "@types/react": "^18.2.0" - "@types/react-dom": "^18.2.0" # ============================================================================= # TESTS # ============================================================================= tests: framework: Vitest target_coverage: 70% structure: unit: "src/**/__tests__/*.test.ts" component: "src/**/__tests__/*.test.tsx" integration: "src/__tests__/integration/*.test.tsx" # ============================================================================= # HISTORIAL # ============================================================================= history: - date: "2026-01-06" action: "Implementacion Sprints 1-3 (109 SP)" author: ORQUESTADOR-Claude-Opus sprint: "1-3" changes: - "25 paginas implementadas" - "4 Zustand stores creados" - "23 rutas agregadas a router" - "Feature catalogs completado (100%)" - "Feature settings estructura creada" - "Build status: Pass (4.07s)" features_created: catalogs: status: "100% implementado" pages: 17 stores: 4 hooks: 4 components: 4 settings: status: "parcial (2 pages)" pages: 2 stores: 0 hooks: 3 components: 7 - date: "2025-12-05" action: "Reestructuracion completa siguiendo filosofia GAMILIT" author: Requirements-Analyst changes: - "Documentados 4 features foundation completos" - "Agregada trazabilidad RF por componente y page" - "Documentados stores, hooks, api clients" - "Definido routing con permisos"