Skip to content

Commit 62be2cc

Browse files
committed
feat: improve tutorials, dashboard sync, and quick actions customization limit
1 parent bf035e6 commit 62be2cc

11 files changed

Lines changed: 141 additions & 88 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ backlog
4141
.plans
4242
mobile-experimental
4343
.turbo
44+
*.py

apps/web/App.tsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useMemo, useEffect, useRef } from 'react';
1+
import React, { useState, useMemo, useEffect, useRef, useCallback } from 'react';
22
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
33
import { useExchangeRates } from './hooks/useExchangeRates';
44
import { Plus, Wallet, Home, ChartArea, User, Lock, Calendar as CalendarIcon, PieChart, Receipt, Activity, TrendingUp, ChartCandlestick, CalendarRange, Calendar1, Fingerprint, ShoppingCart, Target, FileText, Users, Scale, CalendarDays, FlaskConical, GraduationCap } from 'lucide-react';
@@ -363,6 +363,34 @@ function AppContent() {
363363
}
364364
});
365365

366+
const [pendingSync, setPendingSync] = useState(false);
367+
368+
const performSyncFlow = useCallback(async () => {
369+
try {
370+
await exportToCloud();
371+
await importFromCloud();
372+
} catch (err) {
373+
console.error("Dashboard sync error", err);
374+
}
375+
}, [exportToCloud, importFromCloud]);
376+
377+
useEffect(() => {
378+
if (isAuthenticated && pendingSync) {
379+
setPendingSync(false);
380+
performSyncFlow();
381+
}
382+
}, [isAuthenticated, pendingSync, performSyncFlow]);
383+
384+
const handleDashboardSync = useCallback(async () => {
385+
if (!isAuthenticated) {
386+
setPendingSync(true);
387+
handleLogin();
388+
} else {
389+
await performSyncFlow();
390+
}
391+
}, [isAuthenticated, handleLogin, performSyncFlow]);
392+
393+
366394

367395
// Load Data
368396
useEffect(() => {
@@ -1408,7 +1436,7 @@ function AppContent() {
14081436
onUpdateProfile={handleUpdateProfile}
14091437
syncPendingCount={syncPendingCount}
14101438
isSyncing={isSyncing}
1411-
onSync={exportToCloud}
1439+
onSync={handleDashboardSync}
14121440
goals={goals}
14131441
onOpenTutorials={() => setShowTutorials(true)}
14141442
gamProfile={gamProfile}

apps/web/components/TutorialSystem.tsx

Lines changed: 77 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -151,23 +151,26 @@ const TUTORIALS: Tutorial[] = [
151151
description: "Crea y gestiona tus cuentas y wallets",
152152
steps: [
153153
{
154-
title: "Accesos rápidos del Dashboard",
155-
description: "Desde los accesos rápidos puedes acceder al módulo Wallet para gestionar todas tus cuentas bancarias y billeteras digitales.",
156-
selector: "[data-tutorial='quick-actions']",
154+
title: "Acceso a Wallet",
155+
description: "Toca el botón Wallet en la barra de accesos rápidos para entrar al módulo de gestión de tus cuentas y billeteras.",
156+
selector: "[data-tutorial='quick-action-WALLET']",
157157
navigateTo: "DASHBOARD",
158+
waitForClick: true,
158159
position: "bottom",
159160
},
160161
{
161-
title: "Widget de Balance",
162-
description: "El widget de balance muestra tu patrimonio neto total calculado en tu moneda de visualización (USD, Bs o EUR).",
163-
selector: "[data-label='balanceChart']",
162+
title: "Crear una nueva billetera",
163+
description: "Toca el botón '+' (o 'Crear Billetera') para abrir el formulario y registrar tu nueva cuenta bancaria o billetera digital.",
164+
selector: "[data-tutorial='add-wallet-btn'], [data-tutorial='create-wallet-btn']",
165+
waitForClick: true,
164166
position: "bottom",
165167
},
166168
{
167-
title: "Personalizar el Dashboard",
168-
description: "Usa el botón de Widgets para activar, desactivar y reordenar los widgets que aparecen en tu Dashboard.",
169-
selector: "[data-tutorial='customize-btn']",
170-
position: "bottom",
169+
title: "Guardar tu billetera",
170+
description: "Ingresa el nombre, saldo inicial y selecciona tu moneda. Luego toca 'Guardar' para crear tu billetera.",
171+
selector: "[data-tutorial='wallet-save-btn']",
172+
waitForClick: true,
173+
position: "top",
171174
},
172175
],
173176
},
@@ -180,23 +183,23 @@ const TUTORIALS: Tutorial[] = [
180183
steps: [
181184
{
182185
title: "Abrir formulario",
183-
description: "Toca el botón + para registrar una transacción. Al escribir el nombre del comercio en la nota, la categoría se asigna sola.",
186+
description: "Toca el botón + para abrir la pantalla de agregar transacción.",
184187
selector: "[data-tutorial='fab-add']",
185188
navigateTo: "DASHBOARD",
186189
waitForClick: true,
187190
position: "top",
188191
},
189192
{
190-
title: "Tipo de transacción",
191-
description: "La detección automática funciona para gastos principalmente. Prueba escribir \"McDonald's\", \"Uber\" o \"Netflix\" en la nota.",
192-
selector: "[data-tutorial='tx-type-expense']",
193+
title: "Desplegable de Categoría",
194+
description: "Toca el botón de Categoría para desplegar la lista de categorías y seleccionar la que desees para esta transacción.",
195+
selector: "[data-tutorial='tx-category-btn']",
196+
waitForClick: true,
193197
position: "bottom",
194198
},
195199
{
196-
title: "Estructura de Gastos",
197-
description: "El widget de Estructura de Gastos muestra la distribución de tus gastos por categoría. Toca cualquier segmento para el detalle.",
198-
selector: "[data-label='expenses']",
199-
navigateTo: "DASHBOARD",
200+
title: "Detección Inteligente",
201+
description: "Recuerda que si escribes notas como \"McDonald's\", \"Uber\" o \"Netflix\", la categoría se seleccionará automáticamente por ti.",
202+
selector: "[data-tutorial='tx-amount-input']",
200203
position: "bottom",
201204
},
202205
],
@@ -209,24 +212,34 @@ const TUTORIALS: Tutorial[] = [
209212
description: "Controla tus gastos con límites mensuales",
210213
steps: [
211214
{
212-
title: "Accesos rápidos",
213-
description: "Desde los accesos rápidos del Dashboard accede al módulo de Presupuestos para crear y gestionar tus límites de gasto por categoría.",
214-
selector: "[data-tutorial='quick-actions']",
215+
title: "Módulo de Presupuestos",
216+
description: "Toca el botón Presupuesto en la barra de accesos rápidos del Dashboard para ingresar a la gestión de tus límites.",
217+
selector: "[data-tutorial='quick-action-BUDGET']",
215218
navigateTo: "DASHBOARD",
219+
waitForClick: true,
216220
position: "bottom",
217221
},
218222
{
219-
title: "Botón de Widgets",
220-
description: "Activa el widget de Pronóstico desde el personalizador para ver alertas inteligentes cuando te acerques al límite de un presupuesto.",
221-
selector: "[data-tutorial='customize-btn']",
223+
title: "Agregar un Sobre de Presupuesto",
224+
description: "Toca el botón '+ Presupuesto' para abrir el selector e iniciar el proceso de agregar un límite a tus categorías.",
225+
selector: "[data-tutorial='budget-add-btn']",
226+
waitForClick: true,
222227
position: "bottom",
223228
},
224229
{
225-
title: "Sincronización",
226-
description: "Cuando tus presupuestos estén configurados, sincroniza con la nube para respaldarlo todo de forma cifrada.",
227-
selector: "[data-tutorial='sync-btn']",
230+
title: "Crear Sobre Personalizado",
231+
description: "Toca 'Crear sobre personalizado' si deseas establecer un límite para una categoría personalizada o exclusiva.",
232+
selector: "[data-tutorial='budget-custom-envelope-btn']",
233+
waitForClick: true,
228234
position: "bottom",
229235
},
236+
{
237+
title: "Guardar Sobre",
238+
description: "Ingresa el nombre, límite mensual, ícono y toca 'Crear sobre' para añadirlo a tu lista.",
239+
selector: "[data-tutorial='budget-create-envelope-action']",
240+
waitForClick: true,
241+
position: "top",
242+
},
230243
],
231244
},
232245
{
@@ -237,22 +250,17 @@ const TUTORIALS: Tutorial[] = [
237250
description: "Analiza tus gastos por categoría y período",
238251
steps: [
239252
{
240-
title: "Estructura de Gastos",
241-
description: "Este widget muestra tus categorías de gasto principales. Toca cualquier segmento del gráfico para ver el detalle de transacciones.",
242-
selector: "[data-label='expenses']",
253+
title: "Acceder a Transacciones",
254+
description: "Toca el botón Transacciones en la barra de accesos rápidos para entrar a la lista de todos tus movimientos financieros.",
255+
selector: "[data-tutorial='quick-action-TRANSACTIONS']",
243256
navigateTo: "DASHBOARD",
257+
waitForClick: true,
244258
position: "bottom",
245259
},
246260
{
247-
title: "Ingresos vs Gastos",
248-
description: "El widget de Ingresos vs Gastos compara tus flujos mensuales con un gráfico de barras para cada mes del año.",
249-
selector: "[data-label='incomeVsExpense']",
250-
position: "bottom",
251-
},
252-
{
253-
title: "Vista de Análisis",
254-
description: "Desde los accesos rápidos accede a la vista de Análisis para reportes con filtros avanzados por fecha, categoría y tipo.",
255-
selector: "[data-tutorial='quick-actions']",
261+
title: "Filtrar y Buscar",
262+
description: "En esta vista puedes buscar transacciones por nombre usando la barra de búsqueda o filtrarlas por billetera, tipo y categoría.",
263+
selector: "[data-tutorial='tx-list-search']",
256264
position: "bottom",
257265
},
258266
],
@@ -265,23 +273,26 @@ const TUTORIALS: Tutorial[] = [
265273
description: "Automatiza pagos y cobros fijos",
266274
steps: [
267275
{
268-
title: "Accesos rápidos",
269-
description: "Desde los accesos rápidos del Dashboard busca el ícono de Programados para gestionar pagos y cobros recurrentes como suscripciones.",
270-
selector: "[data-tutorial='quick-actions']",
276+
title: "Acceso a Programados",
277+
description: "Busca y toca el icono de 'Programados' en los accesos rápidos del Dashboard para ver tus transacciones recurrentes.",
278+
selector: "[data-tutorial='quick-action-SCHEDULED']",
271279
navigateTo: "DASHBOARD",
280+
waitForClick: true,
272281
position: "bottom",
273282
},
274283
{
275-
title: "Configurar programación",
276-
description: "Crea un pago programado con monto, categoría y frecuencia: diario, semanal, mensual o anual. La app registra la transacción automáticamente.",
277-
selector: "[data-tutorial='quick-actions']",
284+
title: "Añadir Pago Programado",
285+
description: "Toca el botón '+' para abrir el formulario y crear un pago o cobro automatizado (como renta, suscripciones, etc.).",
286+
selector: "[data-tutorial='scheduled-add-btn']",
287+
waitForClick: true,
278288
position: "bottom",
279289
},
280290
{
281-
title: "Calendario financiero",
282-
description: "El acceso rápido de Calendario Financiero muestra todos tus compromisos programados en vista de calendario mensual.",
283-
selector: "[data-tutorial='quick-actions']",
284-
position: "bottom",
291+
title: "Guardar Programación",
292+
description: "Define el monto, la categoría, la frecuencia (mensual, anual, etc.) y toca 'Guardar' para automatizar tu transacción.",
293+
selector: "[data-tutorial='scheduled-save-btn']",
294+
waitForClick: true,
295+
position: "top",
285296
},
286297
],
287298
},
@@ -293,23 +304,19 @@ const TUTORIALS: Tutorial[] = [
293304
description: "Comparte o respalda tus datos",
294305
steps: [
295306
{
296-
title: "Botón de sincronización",
297-
description: "El ícono de nube sincroniza y respalda todos tus datos cifrados en Google Drive. Tócalo cuando tengas conexión a internet.",
298-
selector: "[data-tutorial='sync-btn']",
307+
title: "Centro de Gestión de Datos",
308+
description: "Busca el icono de 'Centro de exportación' (o Exportar). Si no lo ves a simple vista, desliza (swipe) hacia la derecha o usa las flechas de navegación en la barra.",
309+
selector: "[data-tutorial='quick-action-EXPORT']",
299310
navigateTo: "DASHBOARD",
311+
waitForClick: true,
300312
position: "bottom",
301313
},
302314
{
303-
title: "Exportar e Importar",
304-
description: "Desde los accesos rápidos puedes exportar en CSV (para Excel) o JSON (respaldo completo). También puedes generar un Reporte PDF visual.",
305-
selector: "[data-tutorial='quick-actions']",
306-
position: "bottom",
307-
},
308-
{
309-
title: "Verificar actualizaciones",
310-
description: "Cuando hay una nueva versión disponible, este botón se ilumina en azul. Tócalo para actualizar y obtener las últimas mejoras.",
311-
selector: "[data-tutorial='customize-btn']",
312-
position: "bottom",
315+
title: "Generar Archivo",
316+
description: "Elige si deseas exportar tus datos en formato CSV para Excel o JSON como copia de seguridad cifrada, y presiona 'Generar Exportación'.",
317+
selector: "[data-tutorial='export-generate-btn']",
318+
waitForClick: true,
319+
position: "top",
313320
},
314321
],
315322
},
@@ -322,9 +329,10 @@ const TUTORIALS: Tutorial[] = [
322329
steps: [
323330
{
324331
title: "Botón de personalización",
325-
description: "Toca este botón para abrir el personalizador del Dashboard donde puedes activar, desactivar y reordenar los widgets.",
332+
description: "Toca este botón de Widgets para abrir el panel donde puedes activar, desactivar y reordenar tus widgets favoritos.",
326333
selector: "[data-tutorial='customize-btn']",
327334
navigateTo: "DASHBOARD",
335+
waitForClick: true,
328336
position: "bottom",
329337
},
330338
{
@@ -336,7 +344,7 @@ const TUTORIALS: Tutorial[] = [
336344
{
337345
title: "Configuración general",
338346
description: "El botón de Ajustes da acceso a la configuración de perfil, seguridad, idioma, moneda y formato de fecha.",
339-
selector: "[data-tutorial='sync-btn']",
347+
selector: "[data-tutorial='settings-btn']",
340348
position: "bottom",
341349
},
342350
],
@@ -418,7 +426,7 @@ function CompletionScreen({ tutorial, onBackToList, onClose }: { tutorial: Tutor
418426
onClick={onBackToList}
419427
className="w-full py-2.5 rounded-xl bg-theme-brand text-white font-black text-[12px] hover:opacity-90 transition-opacity"
420428
>
421-
Ver más tutoriales
429+
Ir al Dashboard
422430
</button>
423431
<button
424432
onClick={onClose}
@@ -672,7 +680,10 @@ export function TutorialSystem({ onClose, onNavigate }: TutorialSystemProps) {
672680
return (
673681
<CompletionScreen
674682
tutorial={activeTutorial!}
675-
onBackToList={handleBackToList}
683+
onBackToList={() => {
684+
onNavigate("DASHBOARD");
685+
onClose();
686+
}}
676687
onClose={onClose}
677688
/>
678689
);

apps/web/views/AddTransaction.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,7 @@ export const AddTransaction: React.FC<AddTransactionProps> = ({ onClose, onSave,
10621062
{/* Category Button */}
10631063
<button
10641064
onClick={() => setShowCategoryModal(true)}
1065+
data-tutorial="tx-category-btn"
10651066
className="flex-shrink-0 bg-theme-surface rounded-2xl p-2 flex items-center gap-3 active:scale-[0.98] transition-all min-w-[140px] border border-white/5"
10661067
>
10671068
<div className={`${CATEGORIES.find(c => c.id === categoryId)?.color || 'text-theme-primary'} bg-white/5 w-11 h-11 rounded-full flex items-center justify-center shadow-lg`}>

apps/web/views/BudgetView.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ export const BudgetView: React.FC<BudgetViewProps> = ({
501501
<h2 className="text-lg font-bold text-theme-primary">{t('envelopes')}</h2>
502502
<button
503503
onClick={() => setShowAddBudgetModal(true)}
504+
data-tutorial="budget-add-btn"
504505
className="flex items-center gap-1 bg-theme-brand hover:brightness-110 text-white text-xs font-bold px-3 py-1.5 rounded-full shadow-lg shadow-brand/20 transition-all active:scale-95"
505506
>
506507
<Plus size={14} /> {t('addBudget')}
@@ -843,6 +844,7 @@ export const BudgetView: React.FC<BudgetViewProps> = ({
843844
whileHover={{ scale: 1.02 }}
844845
whileTap={{ scale: 0.98 }}
845846
onClick={() => { setShowAddBudgetModal(false); setShowCustomEnvelopeModal(true); }}
847+
data-tutorial="budget-custom-envelope-btn"
846848
className="w-full p-4 bg-theme-brand text-white rounded-xl font-bold mb-4 flex items-center justify-center gap-2 shadow-lg shadow-brand/20"
847849
>
848850
<Plus size={20} /> {t('createCustomEnvelope')}
@@ -997,7 +999,7 @@ export const BudgetView: React.FC<BudgetViewProps> = ({
997999

9981000
<div className="flex gap-3 mt-4">
9991001
<button onClick={() => setShowCustomEnvelopeModal(false)} className="px-4 py-3 rounded-xl bg-white/5 text-theme-secondary font-bold flex-1">{t('cancel')}</button>
1000-
<button onClick={handleAddCustomBudget} className="flex-[2] py-3 rounded-xl bg-theme-brand text-white font-bold shadow-lg shadow-brand/20">{t('createEnvelopeAction')}</button>
1002+
<button onClick={handleAddCustomBudget} data-tutorial="budget-create-envelope-action" className="flex-[2] py-3 rounded-xl bg-theme-brand text-white font-bold shadow-lg shadow-brand/20">{t('createEnvelopeAction')}</button>
10011003
</div>
10021004
</motion.div>
10031005
</motion.div>

0 commit comments

Comments
 (0)