Targ Apps Docs

Páginas core

Dashboard, Transactions y Accounts en Targ-Apps-Finance — wireframes y lógica implementada.

Dashboard (pages/Dashboard.js, TAF-012)

Vista de resumen con jerarquía tipográfica (sin tarjetas KPI de color). Estilos en finance.css con prefijo .taf-dashboard-*.

SecciónContenido
HeaderPageHeader — eyebrow «Resumen», título «Posición financiera», contexto del mes actual y CTA «Registrar movimiento»
Balance (firma visual)Bloque .taf-dashboard-balance con borde cobre lateral (ledger): balance multi-moneda vía balanceByCurrency()
Flujo del mesTres columnas tipográficas: ingresos, gastos y resultado del mes (monthlyTotals()), tono positive/negative/neutral
Accesos rápidosEnlaces a transacciones, reportes y cuentas con iconos Lucide (Icon)
GráficosPaneles .taf-dashboard-panel con ECharts embebidos (embedded): ingresos vs gastos por día del mes actual (granularity: "day") y patrimonio diario del mes (TrendChart, NetWorthChart)
Top categoríasLista con barras de progreso cobre derivadas de topExpenseCategories
Últimos movimientosHasta cinco TransactionRow y enlace «Ver historial» (#/transactions)

Componentes compartidos:

  • src/components/PageHeader.js — eyebrow, título, description y slot de acciones (data-component="page-header").
  • src/components/LucideIcon.js — iconos Lucide SVG inline (sin lucide-react).
  • src/components/charts/TrendChart.js y src/components/charts/NetWorthChart.js — ECharts con tokens --cs-*; prop embedded (sin doble .cs-card dentro del panel; figure a ancho completo con márgenes simétricos vía buildEmbeddedChartGrid()) y granularity: "day" | "month"; ResizeObserver + resize() post-layout en mountEcharts.js.

SummaryCard.js sigue disponible para otras vistas; el dashboard ya no lo usa.

Transactions (pages/Transactions.js, TAF-008 / TAF-009 / TAF-010 / TAF-011 / TAF-016)

Vista principal del historial con UI Chroma-Style, filtros reactivos y formularios separados para movimientos y transferencias.

BloqueComportamiento
HeaderPageHeader — título, lead descriptivo y CTA «Nuevo movimiento» (icono Lucide plus)
FiltrosTransactionFilters — barra compacta .taf-transactions-filters (Desde/Hasta/Cuenta/Categoría/Tipo/Buscar); oculta mientras no haya movimientos; botón «Limpiar» solo con filtros activos; estado en #/transactions?…; cada filtro es hookState; DatePicker enlaza min/max entre Desde/Hasta
ModalTransactionModal — pestaña por defecto «Ingreso / Gasto»; formularios en modal con Select en posición fija (taf-select-menu--fixed) para evitar recorte y solapamiento
Formulario gasto/ingresoTransactionForm + FormField; errores solo tras tocar el campo o intentar guardar; submit deshabilitado si hay errores; helpers de cuenta resuelta declarados antes de formData / useMemo
Formulario transferenciaTransferForm (TAF-016) — advertencia de saldo insuficiente sin bloquear submit; misma convención de orden y deps de señales que TransactionForm
Listado vacíoEmptyState (context: transactions) — «Sin movimientos» + CTA «Registrar primer movimiento»; visible sin depender de data-animate (opacidad base en CSS + animateEmptyState)
GruposOrden descendente por fecha; etiquetas «Hoy», «Ayer» o fecha legible (.taf-transactions-groups); filteredTransactions → listState con cadena useMemo (getters en deps); el DOM del listado usa For sobre listState().groups para re-renderizar al cambiar filtros (no capturar el array en un hijo de Show montado una vez)
FilasTransactionRow (.taf-transactions-row) — monto prominente con tabular-nums; categoría con punto de color; transferencias con icono arrowLeftRight; acciones editar/eliminar con Lucide
Paginación«Cargar más movimientos» incrementa el límite visible (20 por página)
AnimacionesAnime.js: animateListStagger en filas cuando cambian filteredTransactions / listState (useLayoutEffect con las mismas deps); refreshAnimations al cambiar de ruta en App.js; formularios (animateFormEnter), shake en campo inválido (shakeField)
EliminaciónConfirmDialog → removeTransaction (borra el par si es transferencia)

Componentes:

  • src/components/PageHeader.js — cabecera compartida (título, lead, acciones)
  • src/components/LucideIcon.js — iconos SVG inline (sin lucide-react)
  • src/components/FormField.js — label + control + error (Chroma-Style)
  • src/components/TransactionForm.js — solo income / expense
  • src/components/TransferForm.js — transferencias entre cuentas
  • src/components/TransactionFilters.js — barra de filtros
  • src/components/ConfirmDialog.js — diálogo reutilizable con Show; overlay a pantalla completa (backdrop + panel centrado), botones cs-btn-outline / cs-btn-destructive, cierre con Escape o clic en el fondo

Estilos: src/styles/finance.css — namespace .taf-transactions-*.

Utilidades de fecha en logic/utils/dates.js: localDateKey, formatGroupDateLabel, todayISO.

Reactividad de filtros (Transactions)

Patrón canónico Chroma (useMemo encadenados + For con getter) — sin API computed/watch.

Causa del bug corregido: Show monta su función children una vez (rama bajo untracked). El listado capturaba listState().groups en ese primer montaje → snapshot estático. Los memos (filteredTransactions, listState) y el useLayoutEffect de animación sí se actualizaban; las filas en pantalla no.

Fix en pages/Transactions.js:

const filteredTransactions = useMemo(
    () => filterTransactions(finance.transactions(), { /* getters de filtro */ }),
    [finance.transactions, filterDateFrom, filterDateTo, filterCategoryId, filterAccountId, filterType, filterSearch],
);

const listState = useMemo(
    () => groupVisibleTransactions(filteredTransactions(), visibleLimit()),
    [filteredTransactions, visibleLimit],
);

useLayoutEffect(() => { /* animateListStagger */ }, [listState, filteredTransactions]);

// Show solo como compuerta vacío/con datos; el listado va en For:
e(For, { each: () => listState().groups, children: (group) => … });
CapaQué hace
FuentehookState por campo de filtro; TransactionFilters enlaza value: filterDateFrom (getter)
DerivadofilteredTransactions → listState — deps son getters, no valores
VistaFor({ each: () => listState().groups }) — binding reactivo al signal del memo
EfectosAnimación con deps [listState, filteredTransactions]

Chroma: Derived State & Dynamic Updates · pitfall Show vs listas: Common Pitfalls §6.

Accounts (pages/Accounts.js, TAF-006)

Lista y formulario cableados a useFinance(). El saldo de cada tarjeta sale de getAccountBalance. Estilos en .taf-accounts-*.

BloqueContenido
CabeceraPageHeader + «Nueva cuenta» (plus) y toggle «Archivadas» (archive) si aplica
ListaGrid .taf-accounts-grid con AccountCard (.taf-accounts-card)
TarjetaIcono Lucide por tipo (wallet, building2, creditCard), saldo tabular, badge «Principal»
VacíoEmptyState con icono wallet y CTA «Crear primera cuenta»
FormularioPanel .taf-accounts-form-panel → AccountForm → addAccount / updateAccount

API de store para esta página (y para un listado filtrado futuro): activeAccounts, archivedAccounts, archiveAccount, restoreAccount, setDefaultAccount. removeAccount sigue bloqueado si la cuenta tiene transacciones.

Componentes: AccountCard.js y AccountForm.js (submit/inputs ya conectados).

Convención

  • Atributos data-page, data-slot y data-component marcan secciones para pruebas y estilos.
  • La lógica de negocio vive en logic/store/ (ledger.js, transactions.js, financeStore.js) y se consume con useFinance() + useMemo donde aplica.

On this page