diff --git a/src/assets/images/coverage.svg b/src/assets/images/coverage.svg new file mode 100644 index 0000000..ca62113 --- /dev/null +++ b/src/assets/images/coverage.svg @@ -0,0 +1 @@ + Cover Info Product: Amount: Expiry Date: Cover ID: OpenCover Savings xDAI 516.90 USDCMay 15 20264478 \ No newline at end of file diff --git a/src/flows/caixinha-review/Screen1_Dashboard.tsx b/src/flows/caixinha-review/Screen1_Dashboard.tsx new file mode 100644 index 0000000..8e4b337 --- /dev/null +++ b/src/flows/caixinha-review/Screen1_Dashboard.tsx @@ -0,0 +1,226 @@ +/** + * @screen Dashboard (caixinha-review copy) + * @description Caixinhas list uses ListItem + Avatar (size md, 40px) instead of + * the bespoke CaixinhaCard. Only USD active, EUR/BRL disabled with "Em breve". + */ +import { RiArrowDownLine, RiArrowRightUpLine } from '@remixicon/react' +import savingsPiggy from '../../assets/images/savings-piggy-3d.jpg' +import type { FlowScreenProps } from '../../pages/simulator/flowRegistry' +import { useScreenData } from '../../lib/ScreenDataContext' +import FeatureLayout from '../../library/layout/FeatureLayout' +import Stack from '../../library/layout/Stack' +import Text from '../../library/foundations/Text' +import GroupHeader from '../../library/navigation/GroupHeader' +import ShortcutButton from '../../library/inputs/ShortcutButton' +import Alert from '../../library/display/Alert' +import Avatar from '../../library/display/Avatar' +import ListItem from '../../library/display/ListItem' +import { motion } from 'framer-motion' +import { BalanceDisplay } from '../savings-reviewed/manage/Screen2_Hub.parts' +import { + CURRENCIES, + formatBrlEquivalent, + formatCurrency, + type CaixinhaCurrency, +} from '../savings-reviewed/shared/data' + +interface ScreenData { + hasBalance?: boolean + hasPending?: boolean + [key: string]: unknown +} + +interface CaixinhaRow { + currency: CaixinhaCurrency + name: string + balance: number + yieldToday: number + disabled?: boolean + onPress?: () => void +} + +function CaixinhaRow({ currency, name, balance, yieldToday, disabled, onPress }: CaixinhaRow) { + const curr = CURRENCIES[currency] + const subtitle = disabled + ? 'Em breve' + : `Rendendo ${curr.apyDisplay.replace(' a.a.', '')} ao ano` + + const right = disabled ? null : ( + + + {formatCurrency(balance, currency)} + + {yieldToday > 0 && ( + + ↑ {formatCurrency(yieldToday, currency)} + + )} + + ) + + return ( + } + title={name} + subtitle={subtitle} + right={right} + trailing={null} + onPress={disabled ? undefined : onPress} + disabled={disabled} + /> + ) +} + +export default function Screen1_Dashboard({ onNext, onElementTap }: FlowScreenProps) { + const data = useScreenData() + const hasBalance = data.hasBalance ?? true + const hasPending = data.hasPending ?? false + + const usdBalance = hasBalance ? 9894.89 : 0 + const usdYield = hasBalance ? 80.32 : 0 + + const handleTapDolar = () => { + const resolved = onElementTap?.('CaixinhaCard: Caixinha em Dólar') + if (!resolved) onNext() + } + + const handleTapAdicionar = () => { + const resolved = onElementTap?.('ShortcutButton: Adicionar') + if (!resolved) onNext() + } + + const handleTapResgatar = () => { + const resolved = onElementTap?.('ShortcutButton: Resgatar') + if (!resolved) onNext() + } + + return ( + +
+
+ + Caixinhas + + Rendimento automático e simples + + + + } + > + + {/* Total balance */} + + Total guardado +
+ +
+ {hasPending && ( +
+ + + + + + + + Processando depósito... + +
+ )} + {hasBalance && !hasPending && ( + + {usdYield > 0 && ( + + ↑ {formatCurrency(usdYield, 'USD')} + + )} + + {formatBrlEquivalent(usdBalance, 'USD')} + + + )} +
+ + {!hasBalance && ( + + )} + + {/* Shortcuts */} + + } + label="Adicionar" + variant="primary" + onPress={handleTapAdicionar} + /> + } + label="Resgatar" + variant="secondary" + disabled={!hasBalance} + onPress={handleTapResgatar} + /> + + + {/* Caixinhas list */} + + + + + + + + +
+ + ) +} diff --git a/src/flows/caixinha-review/index.ts b/src/flows/caixinha-review/index.ts new file mode 100644 index 0000000..18d0ad5 --- /dev/null +++ b/src/flows/caixinha-review/index.ts @@ -0,0 +1,222 @@ +/** + * Caixinha Review — curated flow for dev handoff review. + * + * Six screens picked from existing savings flows so we can iterate on the + * final "save-all" set without touching the original source flows. + * + * 1. save-all-onboarding-intro → poupar/Screen1_Intro + * 2. save-all-onboarding-insurance → savings-onboarding-b/Screen2_InsuranceAbout + * 3. save-all-manage-dashboard → savings-v2/manage/Screen1_Dashboard (Com saldo) + * 4. save-all-manage-hub → savings-v2/manage/Screen2_Hub (Com saldo) + * 5. save-all-manage-hub-yield-info → savings-v2/manage/Screen2_Hub (BottomSheet de rentabilidade aberto) + * 6. save-all-manage-insurance → savings-reviewed/manage/Screen3_InsuranceCard + */ + +import { registerFlow } from '../../pages/simulator/flowRegistry' +import { registerPage } from '../../pages/gallery/pageRegistry' +import { bootstrapFlowGraph } from '../../pages/simulator/flowGraphStore' +import type { FlowNodeData } from '../../pages/simulator/flowGraph.types' + +// Onboarding +import OnboardingIntro from '../poupar/Screen1_Intro' +import OnboardingInsurance from '../savings-onboarding-b/Screen2_InsuranceAbout' + +// Manage (Com saldo — default state of the savings-v2 manage screens) +import ManageDashboard from './Screen1_Dashboard' +import ManageHub from '../savings-v2/manage/Screen2_Hub' + +// Manage insurance +import ManageInsurance from '../savings-reviewed/manage/Screen3_InsuranceCard' + +// ═══════════════════════════════════════════════════════════════ +// SCREEN DEFINITIONS +// ═══════════════════════════════════════════════════════════════ + +const screenDefs = [ + { + id: 'save-all-onboarding-intro', + title: 'Onboarding – Intro', + description: 'First-time user intro: what the caixinha is and why it matters.', + componentsUsed: ['FeatureLayout', 'StickyFooter', 'Stack', 'Button', 'Text', 'Chip', 'Summary', 'GroupHeader'], + component: OnboardingIntro, + }, + { + id: 'save-all-onboarding-insurance', + title: 'Onboarding – Insurance', + description: 'Explains how the Nexus Mutual-style insurance protects deposits.', + componentsUsed: ['BaseLayout', 'Header', 'Stack', 'Summary', 'GroupHeader', 'DataList', 'Alert', 'Button', 'Text'], + component: OnboardingInsurance, + }, + { + id: 'save-all-manage-dashboard', + title: 'Manage – Dashboard (Com saldo)', + description: 'Dashboard with USD/EUR/BRL caixinhas rendered as ListItem + Avatar (md).', + componentsUsed: ['FeatureLayout', 'Stack', 'ShortcutButton', 'GroupHeader', 'ListItem', 'Avatar', 'Alert', 'Text'], + component: ManageDashboard, + interactiveElements: [ + { id: 'cc-usd', component: 'CurrencyCard', label: 'Dólar americano' }, + ], + states: [ + { id: 'default', name: 'Com saldo', description: 'USD caixinha has balance', isDefault: true, data: {} }, + ], + }, + { + id: 'save-all-manage-hub', + title: 'Manage – Hub (Com saldo)', + description: 'USD caixinha detail with balance, chart, shortcuts, and insurance.', + componentsUsed: ['BaseLayout', 'Header', 'LineChart', 'ShortcutButton', 'SegmentedControl', 'DataList', 'Banner', 'Text', 'Stack'], + component: ManageHub, + interactiveElements: [ + { id: 'sc-adicionar', component: 'ShortcutButton', label: 'Adicionar' }, + { id: 'sc-resgatar', component: 'ShortcutButton', label: 'Resgatar' }, + { id: 'btn-apolice', component: 'Button', label: 'Ver certificado' }, + ], + states: [ + { id: 'default', name: 'Com saldo', description: 'Has balance, Details tab', isDefault: true, data: {} }, + ], + }, + { + id: 'save-all-manage-hub-yield-info', + title: 'Manage – Hub (Rentabilidade info aberta)', + description: 'Hub with the "Rentabilidade atual" BottomSheet open — shows the variable-yield explanation + CTA.', + componentsUsed: ['BaseLayout', 'Header', 'SegmentedControl', 'DataList', 'Alert', 'BottomSheet', 'Button', 'Text', 'Stack'], + component: ManageHub, + states: [ + { id: 'default', name: 'Rentabilidade info aberta', description: 'BottomSheet explaining variable yield is open by default.', isDefault: true, data: { showYieldInfo: true } }, + ], + }, + { + id: 'save-all-manage-insurance', + title: 'Manage – Insurance Card', + description: 'Full coverage detail screen mirroring the onboarding insurance content, with coverage certificate illustration.', + componentsUsed: ['BaseLayout', 'Header', 'Summary', 'Alert', 'GroupHeader', 'DataList', 'Link', 'Stack'], + component: ManageInsurance, + }, +] + +// ═══════════════════════════════════════════════════════════════ +// REGISTER PAGES +// ═══════════════════════════════════════════════════════════════ + +for (const s of screenDefs) { + registerPage({ + id: s.id, + name: s.title, + description: s.description, + area: 'Earn', + componentsUsed: [...s.componentsUsed], + component: s.component, + ...('states' in s && Array.isArray(s.states) ? { states: s.states } : {}), + }) +} + +// ═══════════════════════════════════════════════════════════════ +// REGISTER FLOW +// ═══════════════════════════════════════════════════════════════ + +registerFlow({ + id: 'caixinha-review', + name: 'Caixinha Review', + description: + 'Handoff review set: onboarding intro + insurance, manage dashboard + hub (Com saldo), and the insurance card.', + domain: 'earn', + level: 1, + screens: screenDefs.map((s) => ({ + id: s.id, + title: s.title, + description: s.description, + componentsUsed: s.componentsUsed, + component: s.component, + ...('interactiveElements' in s && Array.isArray(s.interactiveElements) + ? { interactiveElements: s.interactiveElements } + : {}), + ...('states' in s && Array.isArray(s.states) ? { states: s.states } : {}), + })), +}) + +// ═══════════════════════════════════════════════════════════════ +// GRAPH — linear onboarding → manage with a branch into insurance +// ═══════════════════════════════════════════════════════════════ + +const X = 400 +const ROW = 160 + +bootstrapFlowGraph( + 'caixinha-review', + [ + { + id: 'n-onboarding-intro', + type: 'screen', + position: { x: X, y: 0 }, + data: { + label: 'Onboarding Intro', + screenId: 'save-all-onboarding-intro', + nodeType: 'screen', + pageId: 'save-all-onboarding-intro', + } as FlowNodeData, + }, + { + id: 'n-onboarding-insurance', + type: 'screen', + position: { x: X, y: ROW }, + data: { + label: 'Onboarding Insurance', + screenId: 'save-all-onboarding-insurance', + nodeType: 'screen', + pageId: 'save-all-onboarding-insurance', + } as FlowNodeData, + }, + { + id: 'n-manage-dashboard', + type: 'screen', + position: { x: X, y: ROW * 2 }, + data: { + label: 'Manage Dashboard', + screenId: 'save-all-manage-dashboard', + nodeType: 'screen', + pageId: 'save-all-manage-dashboard', + } as FlowNodeData, + }, + { + id: 'n-manage-hub', + type: 'screen', + position: { x: X, y: ROW * 3 }, + data: { + label: 'Manage Hub', + screenId: 'save-all-manage-hub', + nodeType: 'screen', + pageId: 'save-all-manage-hub', + } as FlowNodeData, + }, + { + id: 'n-manage-hub-yield-info', + type: 'screen', + position: { x: X, y: ROW * 4 }, + data: { + label: 'Manage Hub (yield info open)', + screenId: 'save-all-manage-hub-yield-info', + nodeType: 'screen', + pageId: 'save-all-manage-hub-yield-info', + } as FlowNodeData, + }, + { + id: 'n-manage-insurance', + type: 'screen', + position: { x: X, y: ROW * 5 }, + data: { + label: 'Manage Insurance', + screenId: 'save-all-manage-insurance', + nodeType: 'screen', + pageId: 'save-all-manage-insurance', + } as FlowNodeData, + }, + ], + [ + { id: 'e-intro-ins', source: 'n-onboarding-intro', target: 'n-onboarding-insurance' }, + { id: 'e-ins-dash', source: 'n-onboarding-insurance', target: 'n-manage-dashboard' }, + { id: 'e-dash-hub', source: 'n-manage-dashboard', target: 'n-manage-hub' }, + { id: 'e-hub-yieldinfo', source: 'n-manage-hub', target: 'n-manage-hub-yield-info' }, + { id: 'e-yieldinfo-ins', source: 'n-manage-hub-yield-info', target: 'n-manage-insurance' }, + ], + 2, +) diff --git a/src/flows/poupar/Screen1_Intro.tsx b/src/flows/poupar/Screen1_Intro.tsx index 10a4938..939c825 100644 --- a/src/flows/poupar/Screen1_Intro.tsx +++ b/src/flows/poupar/Screen1_Intro.tsx @@ -28,9 +28,9 @@ export default function Screen1_Intro({ onNext, onBack, onElementTap }: FlowScre > - Caixinha que faz seu dinheiro render + Caixinha que faz seu dólar render - Guarde qualquer valor e veja ele crescer todo dia. Seu dinheiro rende automaticamente — e você resgata quando quiser, sem taxas e sem burocracia. + Guarde qualquer valor e veja seu saldo crescer todo dia. Seu dinheiro rende automaticamente — e você resgata quando quiser, sem taxas e sem burocracia. @@ -38,21 +38,29 @@ export default function Screen1_Intro({ onNext, onBack, onElementTap }: FlowScre , title: 'Rendimento automático', description: 'Seu saldo rende todos os dias, sem precisar fazer nada' }, + { icon: , title: 'Rendimento dinâmico', description: 'Seu saldo rende todos os dias de acordo com a taxa atual, sem precisar fazer nada' }, { icon: , title: 'Resgate quando quiser', description: 'Sem carência — retire seus fundos a qualquer momento' }, - { icon: , title: 'Seu dinheiro protegido', description: 'Se uma falha técnica afetar seu saldo, você é reembolsado em até 30 dias', linkText: 'Saiba mais', onLinkPress: handleSaibaMais }, + { icon: , title: 'Seu dinheiro protegido', description: 'Se uma falha técnica afetar seu saldo, você será reembolsado em até 45 dias', linkText: 'Saiba mais', onLinkPress: handleSaibaMais }, ]} /> - + + + + ) diff --git a/src/flows/savings-onboarding-b/Screen2_InsuranceAbout.tsx b/src/flows/savings-onboarding-b/Screen2_InsuranceAbout.tsx index bd9062f..ba1f884 100644 --- a/src/flows/savings-onboarding-b/Screen2_InsuranceAbout.tsx +++ b/src/flows/savings-onboarding-b/Screen2_InsuranceAbout.tsx @@ -2,34 +2,40 @@ import type { FlowScreenProps } from '@/pages/simulator/flowRegistry' import BaseLayout from '@/library/layout/BaseLayout' import Stack from '@/library/layout/Stack' import Header from '@/library/navigation/Header' -import Button from '@/library/inputs/Button' +import Link from '@/library/foundations/Link' import Alert from '@/library/display/Alert' import Summary from '@/library/display/Summary' import GroupHeader from '@/library/navigation/GroupHeader' import DataList from '@/library/display/DataList' -import { RiCodeLine, RiLineChartLine, RiShieldCheckLine, RiGovernmentLine } from '@remixicon/react' +import { RiCodeLine, RiLineChartLine, RiShieldCheckLine } from '@remixicon/react' export default function Screen2_InsuranceAbout({ onBack }: FlowScreenProps) { return ( -
+
- - - , title: 'Bugs em smart contracts', description: 'Falhas no código que resultem em uso não intencional e perda de fundos' }, - { icon: , title: 'Falha e manipulação de oráculos', description: 'Dados de preço incorretos ou deliberadamente corrompidos usados pelos contratos' }, - { icon: , title: 'Falha de liquidação', description: 'Problemas na liquidação de garantias que gerem dívida socializada entre os usuários' }, - { icon: , title: 'Tomada de governança', description: 'Ataques maliciosos que forcem atualizações indevidas nos smart contracts' }, - ]} + + + , title: 'Falhas técnicas', description: 'Erros técnicos do provedor que afetem seu saldo ou bloqueiem o resgate' }, + { icon: , title: 'Valores incorretos', description: 'Erros de cotação que causem perda do valor depositado e rendimentos' }, + { icon: , title: 'Ataques ao provedor', description: 'Ataques maliciosos que comprometam o valor investido' }, + ]} + /> + + + @@ -37,16 +43,22 @@ export default function Screen2_InsuranceAbout({ onBack }: FlowScreenProps) { window.open('https://api.nexusmutual.io/v2/ipfs/QmUJFWdxC7UxQBJXatgkUmJstcb6Kb9erYfSanVkReeXhE', '_blank')} + /> + ), + }, ]} /> - - ) diff --git a/src/flows/savings-reviewed/manage/Screen3_InsuranceCard.tsx b/src/flows/savings-reviewed/manage/Screen3_InsuranceCard.tsx index 671c993..b4a7257 100644 --- a/src/flows/savings-reviewed/manage/Screen3_InsuranceCard.tsx +++ b/src/flows/savings-reviewed/manage/Screen3_InsuranceCard.tsx @@ -1,59 +1,69 @@ /** * @screen Coverage - * @description Nexus Mutual-style green certificate card with coverage details. + * @description Full coverage detail screen — mirrors the onboarding insurance screen + * and shows the coverage certificate illustration at the top. */ import type { FlowScreenProps } from '../../../pages/simulator/flowRegistry' import BaseLayout from '../../../library/layout/BaseLayout' import Stack from '../../../library/layout/Stack' import Header from '../../../library/navigation/Header' -import InsurancePolicyCard from '../../../library/display/InsurancePolicyCard' +import Link from '../../../library/foundations/Link' +import Alert from '../../../library/display/Alert' import Summary from '../../../library/display/Summary' import GroupHeader from '../../../library/navigation/GroupHeader' import DataList from '../../../library/display/DataList' -import { RiBugLine, RiAlertLine, RiRefundLine } from '@remixicon/react' +import { RiCodeLine, RiLineChartLine, RiShieldCheckLine } from '@remixicon/react' +import coverageSvg from '../../../assets/images/coverage.svg' export default function Screen3_InsuranceCard({ onBack }: FlowScreenProps) { return (
- - + Certificado de cobertura - - , title: 'Falha técnica no sistema', description: 'Um erro na tecnologia afeta seu saldo? Você é reembolsado.' }, - { icon: , title: 'Dados de preço incorretos', description: 'Se uma informação errada causar perda, a cobertura entra em ação.' }, - { icon: , title: 'Problema na liquidação', description: 'Algo trava na hora de processar? O seguro garante a devolução.' }, - ]} + + + , title: 'Falhas técnicas', description: 'Erros técnicos do provedor que afetem seu saldo ou bloqueiem o resgate' }, + { icon: , title: 'Valores incorretos', description: 'Erros de cotação que causem perda do valor depositado e rendimentos' }, + { icon: , title: 'Ataques ao provedor', description: 'Ataques maliciosos que comprometam o valor investido' }, + ]} + /> + + + - + window.open('https://api.nexusmutual.io/v2/ipfs/QmUJFWdxC7UxQBJXatgkUmJstcb6Kb9erYfSanVkReeXhE', '_blank')} + /> + ), + }, ]} /> diff --git a/src/flows/savings-v2/manage/Screen2_Hub.parts.tsx b/src/flows/savings-v2/manage/Screen2_Hub.parts.tsx index d338184..2750a28 100644 --- a/src/flows/savings-v2/manage/Screen2_Hub.parts.tsx +++ b/src/flows/savings-v2/manage/Screen2_Hub.parts.tsx @@ -1,7 +1,10 @@ +import { useState } from 'react' import Stack from '../../../library/layout/Stack' import DataList from '../../../library/display/DataList' import Alert from '../../../library/display/Alert' import Text from '../../../library/foundations/Text' +import Button from '../../../library/inputs/Button' +import BottomSheet from '../../../library/layout/BottomSheet' import { RiAddLine, RiSubtractLine } from '@remixicon/react' // ── Balance Display (CurrencyInput typography) ── @@ -52,9 +55,13 @@ interface DetailsTabProps { hasBalance?: boolean yieldAmount?: string onViewPolicy?: () => void + defaultYieldSheetOpen?: boolean } -export function DetailsTab({ hasBalance = true, yieldAmount, onViewPolicy }: DetailsTabProps) { +export function DetailsTab({ hasBalance = true, yieldAmount, onViewPolicy, defaultYieldSheetOpen = false }: DetailsTabProps) { + const [yieldSheetOpen, setYieldSheetOpen] = useState(defaultYieldSheetOpen) + const openYieldInfo = () => setYieldSheetOpen(true) + return ( @@ -62,13 +69,13 @@ export function DetailsTab({ hasBalance = true, yieldAmount, onViewPolicy }: Det data={ hasBalance ? [ - { label: 'Rendimento', value: '4,72% a.a.' }, - { label: 'Rendeu até agora', value: yieldAmount ?? 'US$ 80,32' }, + { label: 'Rentabilidade atual', value: '4,72% a.a.', info: openYieldInfo }, + { label: 'Rendimento acumulado', value: yieldAmount ?? 'US$ 80,32' }, { label: 'Guardando desde', value: '21 jan 2026' }, { label: 'Resgate', value: 'A qualquer momento' }, ] : [ - { label: 'Rendimento', value: '4,72% a.a.' }, + { label: 'Rentabilidade atual', value: '4,72% a.a.', info: openYieldInfo }, { label: 'Resgate', value: 'A qualquer momento' }, { label: 'Proteção', value: 'Seguro incluso' }, ] @@ -82,6 +89,17 @@ export function DetailsTab({ hasBalance = true, yieldAmount, onViewPolicy }: Det description="Seu saldo é coberto contra falhas técnicas e fraudes — sem custo adicional." action={} /> + + setYieldSheetOpen(false)} title="Sobre a rentabilidade"> + + + A rentabilidade da sua Caixinha acompanha a taxa de mercado — ela pode aumentar ou diminuir de acordo com a demanda. Normalmente se mantém estável por algumas semanas, mas pode se ajustar mais rápido em momentos de muita movimentação no mercado. + + + + ) } diff --git a/src/flows/savings-v2/manage/Screen2_Hub.tsx b/src/flows/savings-v2/manage/Screen2_Hub.tsx index 93ce67d..6614786 100644 --- a/src/flows/savings-v2/manage/Screen2_Hub.tsx +++ b/src/flows/savings-v2/manage/Screen2_Hub.tsx @@ -26,11 +26,12 @@ interface ScreenData { tab?: number hasBalance?: boolean hasPending?: boolean + showYieldInfo?: boolean [key: string]: unknown } export default function Screen2_Hub({ onNext, onBack, onElementTap }: FlowScreenProps) { - const { tab: initialTab, hasBalance: hasBalanceData, hasPending: hasPendingData } = useScreenData() + const { tab: initialTab, hasBalance: hasBalanceData, hasPending: hasPendingData, showYieldInfo } = useScreenData() const hasBalance = hasBalanceData ?? true const hasPending = hasPendingData ?? false const [activeTab, setActiveTab] = useState(initialTab ?? 0) @@ -113,7 +114,7 @@ export default function Screen2_Hub({ onNext, onBack, onElementTap }: FlowScreen className="self-start" /> - {activeTab === 0 && } + {activeTab === 0 && } {activeTab === 1 && } diff --git a/src/library/display/DataList.tsx b/src/library/display/DataList.tsx index 90f210e..d42fcc5 100644 --- a/src/library/display/DataList.tsx +++ b/src/library/display/DataList.tsx @@ -74,7 +74,7 @@ function HorizontalRow({ item, isLast }: { item: DataListItem; isLast: boolean } return (
@@ -228,7 +228,7 @@ function VerticalRow({ items, isLast }: { items: DataListItem[]; isLast: boolean return (
diff --git a/src/pages/PageGalleryPage.tsx b/src/pages/PageGalleryPage.tsx index 3d6e1b5..dbb6a52 100644 --- a/src/pages/PageGalleryPage.tsx +++ b/src/pages/PageGalleryPage.tsx @@ -2,8 +2,8 @@ import { useState, useEffect, useCallback } from 'react' import { motion } from 'framer-motion' import { RiAddLine } from '@remixicon/react' -/* Auto-discover all flow registrations */ -import.meta.glob('../flows/*/index.ts', { eager: true }) +/* Flow registrations — scoped to caixinha-review for the handoff session. */ +import.meta.glob('../flows/caixinha-review/index.ts', { eager: true }) import AppHeader from '../components/AppHeader' import PageGalleryCanvas from './gallery/PageGalleryCanvas' diff --git a/src/pages/PreviewPage.tsx b/src/pages/PreviewPage.tsx index b8a00b2..94d301f 100644 --- a/src/pages/PreviewPage.tsx +++ b/src/pages/PreviewPage.tsx @@ -23,8 +23,8 @@ import { } from '@remixicon/react' import { PiPiggyBank, PiPiggyBankFill } from 'react-icons/pi' -/* Auto-discover all flow registrations */ -import.meta.glob('../flows/*/index.ts', { eager: true }) +/* Flow registrations — scoped to caixinha-review for the handoff session. */ +import.meta.glob('../flows/caixinha-review/index.ts', { eager: true }) import { getFlow, hydrateDynamicFlows } from './simulator/flowRegistry' import { getFlowGraph } from './simulator/flowGraphStore' diff --git a/src/pages/SimulatorPage.tsx b/src/pages/SimulatorPage.tsx index 2c32545..56abada 100644 --- a/src/pages/SimulatorPage.tsx +++ b/src/pages/SimulatorPage.tsx @@ -3,8 +3,9 @@ import { useSearchParams } from 'react-router-dom' import { motion } from 'framer-motion' import { RiComputerLine, RiGitBranchLine, RiPaintBrushLine, RiAddLine } from '@remixicon/react' -/* Auto-discover all flow registrations */ -import.meta.glob('../flows/*/index.ts', { eager: true }) +/* Flow registrations — scoped to caixinha-review for the handoff session. + * Other flows are intentionally not imported to keep the sidebar focused. */ +import.meta.glob('../flows/caixinha-review/index.ts', { eager: true }) import AppHeader from '../components/AppHeader' import FlowSidebar from './simulator/FlowSidebar' @@ -16,7 +17,7 @@ import EditableFlowSlug from './simulator/EditableFlowSlug' import { migrateHardcodedFlows, migrateStaleScreenPaths } from './simulator/flowMigration' import { subscribeToGraphChanges } from './simulator/flowGraphStore' import { subscribeToDynamicFlowChanges, migrateSavingsToEarnDomain } from './simulator/dynamicFlowStore' -import { seedDefaultGroups, migrateV1Flows, enableUserActions, subscribeToGroupChanges } from './simulator/flowGroupStore' +import { seedDefaultGroups, migrateV1Flows, cleanupObsoleteEarnGroups, enableUserActions, subscribeToGroupChanges } from './simulator/flowGroupStore' import { pullFromSupabase, pushAllToSupabase } from '../lib/syncStore' // Expose push function for console use @@ -73,6 +74,7 @@ export default function SimulatorPage() { migrateSavingsToEarnDomain() hydrateDynamicFlows() seedDefaultGroups() + cleanupObsoleteEarnGroups() migrateV1Flows() setVersion((v) => v + 1) }, []) diff --git a/src/pages/simulator/flowGroupStore.ts b/src/pages/simulator/flowGroupStore.ts index fa8585f..5301d5e 100644 --- a/src/pages/simulator/flowGroupStore.ts +++ b/src/pages/simulator/flowGroupStore.ts @@ -565,12 +565,6 @@ export function seedDefaultGroups(_allFlows?: unknown): void { const seeds: { domain: string; group: string; flowIds: string[] }[] = [ { domain: 'cards', group: 'Card Detail', flowIds: ['card-info', 'edit-daily-limits', 'rename-virtual-card', 'remove-virtual-card', 'report-card-loss', 'apple-pay-setup'] }, { domain: 'cards', group: 'Card Actions', flowIds: ['create-virtual-card', 'update-phone'] }, - { domain: 'earn', group: 'Caixinha Dólar', flowIds: ['caixinha-dolar', 'caixinha-deposit', 'caixinha-withdraw'] }, - { domain: 'earn', group: 'Yields 2', flowIds: ['yields2', 'yields2-deposit', 'yields2-withdraw'] }, - { domain: 'earn', group: 'Yields 3', flowIds: ['yields3', 'yields3-deposit', 'yields3-withdraw'] }, - { domain: 'earn', group: 'Yields 4', flowIds: ['yields4', 'yields4-deposit', 'yields4-withdraw'] }, - { domain: 'earn', group: 'Yields 5', flowIds: ['yields5', 'yields5-deposit', 'yields5-withdraw'] }, - { domain: 'earn', group: 'Reviewed', flowIds: ['caixinha-create', 'caixinha-manage', 'caixinha-deposit-reviewed', 'caixinha-withdraw-reviewed'] }, { domain: 'add-funds', group: 'ACH Deposit', flowIds: ['deposit-ach', 'noah-registration'] }, ] @@ -603,6 +597,39 @@ export function seedDefaultGroups(_allFlows?: unknown): void { writeState(state) } +// ── Cleanup: remove obsolete seeded earn groups from existing state ── + +const OBSOLETE_EARN_GROUPS_CLEANUP_KEY = 'picnic-design-lab:cleanup-obsolete-earn-groups' +const OBSOLETE_EARN_GROUP_NAMES = new Set([ + 'Caixinha Dólar', + 'Yields 2', + 'Yields 3', + 'Yields 4', + 'Yields 5', + 'Reviewed', +]) + +export function cleanupObsoleteEarnGroups(): void { + if (localStorage.getItem(OBSOLETE_EARN_GROUPS_CLEANUP_KEY)) return + + const state = readState() + let changed = false + + for (const [id, group] of Object.entries(state.groups)) { + if (group.domainId !== 'earn' || !OBSOLETE_EARN_GROUP_NAMES.has(group.name)) continue + + const hasFlows = Object.values(state.memberships).some((m) => m.groupId === id) + if (hasFlows) continue + + delete state.groups[id] + delete state.archivedGroups[id] + changed = true + } + + if (changed) writeState(state) + localStorage.setItem(OBSOLETE_EARN_GROUPS_CLEANUP_KEY, '1') +} + // ── vs1.0 Migration ── const V1_MIGRATION_KEY = 'picnic-design-lab:v1-migration-done'